Skip to content

Card

Card is the base representation type of the knowledge domain: a document that gives a piece of knowledge a physical form.

type Card : Doc {
    // the document represents a piece of knowledge
    // the document has a place in a vault
}

The narrowing conditions do not include the body's format. A plain Markdown file gives knowledge a representation as readily as an .hmd one — see hmd-integration. Card-ness is a knowledge notion.

Everything Doc provides — a Data header, a body, sections, anchors, links — a card has, because a card is one. This page is about what a card has in addition, and that addition is the point of the type.

Two kinds of card

Card deliberately says nothing about what the card is about. That is declared by its two subtypes:

type ConceptCard : {Card, Concept}  // its subject is a concept — a node
type RelationCard : Card             // its subject is a relation — an edge

{Card, Concept} is the normalized supertype set: Card : Doc already holds, so {Card, Concept, Doc} would name Doc twice — see type-system. The third parent, Concept, is what puts a concept card in a node position in a knowledge graph.

Most knowledge lives in nodes, so ConceptCard is the ordinary case and is the default. A RelationCard is the exception, opted into explicitly:

card.metadata.knowledge.type : KnowledgeKind   // Concept | Relation

A card that declares nothing is a concept card. knowledge.type == Relation makes it a relation card, and — like every refinement — the entry is an author's claim that has to be checked against a contract rather than trusted. See hql/knowledge and the knowledge model for what each kind does.

A card is a document that knows where it is

A bare document is a file. A card is a file with a place in a vault, and that place is what makes it addressable by name.

The vault boundary is HMD's. A namespace is a rooted tree of modules, and resolution never crosses into a neighbouring vault: two vaults are two namespaces. A card therefore has a name that other cards can write in double brackets, a spine of ancestor modules that a bare name is probed against, and a set of documents that may point at it.

This is what turns document machinery into card machinery:

  • [[alice]] resolves, because there is a namespace to resolve it in.
  • downlinks are answerable, because the vault knows what points at this card.
  • path means something, because there is a root to be relative to.
  • import and use in the header inherit or not, because there are ancestor folder notes to inherit from.

A plain Markdown file dropped into a vault becomes a card by acquiring that context, not by being rewritten. That is the practical content of HMD's claim that every .md file is already valid .hmd.

What has to be there for a vault to be one

A .hmd/ directory at the root, and nothing more. It is the marker hmd walks up to find, it is where vault state lives, and it is what tells a vault apart from a folder of Markdown.

.hmd/config.toml inside it is optional. Where it is absent its defaults are assumed — the wiki root, the namespace name, autodiscovery and its mode, as hmd init writes them — so a vault is readable before anyone has configured it, and configuring it later changes behaviour rather than granting it. The directory is not optional in the same way: without it there is no root to discover, nowhere for vault state to live, and no semantic index, which is why working with .hmd cards outside one fails semantic search first and more afterwards. The requirements state the chain in full — see semantic search.

A card is a knowledge participant

The difference that matters is not the file format. card is a term from knowledge management, and a card is the kind of document that can reference knowledge and operate on knowledge concepts.

knowledge : Knowledge

Knowledge is designed from day one, not deferred behind a release boundary. A card's body, links, header and embeds are not merely content to be rendered: they are the material that concepts, relations, propositions and evidence are drawn from. A type system that treats cards as documents first and knowledge as a later projection gets the dependency backwards, because the reason cards exist is to carry meaning that a file system cannot.

Concretely, a card can:

  • be a ConceptCard — an identified thing the vault has something to say about, and a node in the knowledge graph.
  • be a RelationCard — a relationship promoted into a document so that it gains a body, a name in the vault and incoming links. It represents an edge; it does not become a node.
  • carry propositions in its body, which other cards may support or dispute.
  • supply evidence, as ordinary sections with ordinary links.

None of this is automatic. A link is structural connectivity and does not assert a semantic relation; a header entry is an author's claim and not a proof of conformance. The path from document to knowledge runs through explicit interpretation, and the rule used must be retained with the result. See hql/knowledge for the semantic universe and what it retains: conflicting claims, provenance, constraints and satisfaction.

Doc has no such capability and does not need one. A constructed body, an embedded fragment or an ordinary Markdown file outside any vault is a perfectly good document with nothing to say about knowledge. The narrowing is exactly the step at which meaning becomes possible.

A card's metadata

A card has a second layer the document does not, and it has its own name:

type Card : Doc {
    metadata : Data     // the knowledge attached to this card
}

header and metadata are not two spellings of one field. A header is a document-level notion — what an author wrote about the file, plus its title. Metadata is a knowledge-level one: concepts, relations, propositions and evidence attached to this card, which is exactly the addition that makes a card more than a document. Only a card has it, because only a card participates in knowledge.

That is why metadata belongs to hql/knowledge rather than to doc-type, and why every extension — the knowledge extension, a git or database one — contributes to card.metadata and none of them writes into the header. They refine one unchanged tree in the same way — see type-system.

card.metadata is assembled, not inherited

card.metadata is not the authored header.metadata under another name. The two are different fields with different semantics, and both stay addressable:

card.header.metadata?   // optional — what the author wrote, if anything
card.metadata           // always present — what the system knows

The authored block is optional, because authoring metadata is a choice. The card's own layer is not, because it exists whether or not the file said anything. It is assembled when the document becomes a card, from inputs that include:

  • the authored header.metadata, where the document has one;
  • system metadata supplied at load;
  • entries contributed by extensions — git, an indexer, a link checker;
  • vault state held under .hmd/**;
  • semantic-search state and other derived information.

Authored metadata is one input among these and does not win by being authored, because nothing contests it: an extension contributes under a key named after itself — metadata.lexical.* for the search index — so a contribution overwrites no authored entry and no two extensions collide. Precedence is settled by construction rather than by a rule for resolving conflicts.

A query has to know which layer it is addressing. card.header.metadata answers what did the author write?; card.metadata answers what does the system know about this card? A linter checking that authors tagged their notes wants the first, a search ranking cards wants the second, and confusing them reports the vault's own bookkeeping as something a person typed. See fields for the two-layer distinction in full.

metadata.knowledge.type lives in the assembled layer, which is why the choice between ConceptCard and RelationCard is a knowledge-level one and not a filename or folder convention.

What a card's metadata contains before any extension declares its shape is open, as are the precedence rules above.

A card's header

A card's header is a Data tree like any document's — what the author wrote, plus a title — and it is readable like any other field: nothing about a card hides it. Two things are worth stating here, though neither is what makes a document a card.

First, format is what licenses wikilinks, embeds and fragment addressing in the body. A card written in hmd has them; a card written in md has a header and a body like any other document, and no hyper layer to ask about.

Second, where the body is hmd, HyperMarkDown reserves exactly four authored header keys and leaves every other key to the author:

Key Meaning
tags /-separated hierarchical strings; categorize, but do not resolve
use Per-card feature toggles; inherits from the nearest ancestor folder note
import Name bindings and search origins; applies to this card only, never inherits
nav order and visibility for publication

Projecting these as stronger HQL types is sound, because HMD validates them. The rest of the tree stays Data, and that openness is deliberate: a type system with a declared header record would have to grow a field every time a vault invented a key.

card.header.tags
card.header.nav.visibility

What a card whose authored header block fails to parse projects to is unresolved — a card with an empty header, a card carrying a diagnostic, or no card at all.

cards

cards is a reserved keyword. It denotes every card in the current namespace vault:

cards : Set<Card>

This is a context value, not a function call and not a filesystem sweep written by the user. It has the same horizon a wikilink does: cards in one vault never contains a card from another.

cards
| typed RelationCard
| graph

Being a keyword rather than a binding means a card cannot shadow it by being named cards, and that a program never has to state where the vault is. What cards means inside a nested module — the whole vault, or the module subtree — still needs a decision, as does whether imported namespaces contribute to it.

There is no matching keyword for documents, and there should not be. cards is bounded because a vault is bounded; documents in general are not a collection anything can enumerate.

cards is a Set<Card>: discovering files establishes no ordering guarantee. A reproducible prefix does not need one, because a card carries its own ordering key — see orderable and hql/collections.

Extensions extend the header

The header is the extension boundary, and an extension can extend it in two quite different senses. Keeping them apart matters.

Contributing entries. Some extensions know facts the file does not state — a git extension supplying authorship and revision, an indexer supplying its embedding and which index the card belongs to, a link checker supplying resolution diagnostics, a build supplying publication state.

A score is not among them. An embedding is a property of the card: it exists whether or not anyone ever searches, and it goes stale when the card's content changes, so the assembled metadata layer is its home. A score exists only relative to a query, and storing one per card would mean either one privileged query or a number whose name no longer describes it. A score belongs to the retrieval result, where it travels with the query and the model revision that produced it — see hql/semantic-search.

git.metadata(card) : Data

This is the second reason the header is broader than what an author writes: most of what a mature vault knows about a card was never typed into the card.

Contributing type knowledge. Other extensions add nothing to the tree at all. They declare what an existing region of it means, so a subtree that was Data becomes usable at a stronger type without being converted or copied.

import knowledge

card.metadata          : Data              // before
card.metadata          : KnowledgeMetadata
card.metadata.concepts : List<Concept>   // see below

List is no longer part of the decided collection vocabulary, so this occurrence needs a semantic choice between Seq<Concept> and Set<Concept>. It is left unresolved deliberately: the answer follows from whether the order of a card's concepts carries information, not from the bracket migration. See hql/collections.

The runtime value is unchanged; what changed is what the checker can prove about it. An extension owns type knowledge about a header, not the header itself — type-system develops this as open structural data with progressive refinement, and it is the mechanism behind the narrowing below.

The core's obligation is to keep contributions distinguishable, and where a value sits is what keeps them so. A derived fact about the file is a field, an authored entry is in the header, and a contribution is in card.metadata under its extension's own key; provenance records where a value came from rather than conferring authority on it. An extension that only contributes type knowledge writes nowhere at all, which is an argument for preferring that form.

Types narrow

Card : Doc is the first narrowing, not a special case. The same mechanism continues past it, and it is the same mechanism by which a Data subtree becomes a known shape — see type-system.

type Person : Card {
    name: String
    born: Date?
}

Declaration syntax is illustrative. The direction is not: a Person card keeps its body, its links and its own identity, and other cards can link to it. Narrowing adds guarantees; it never replaces the card with a record.

ConceptCard and RelationCard are narrowings of this kind too — the two the domain declares rather than ones a vault invents.

What is not a narrowing is the relation a relation card is about. Relation is a first-class knowledge type, never a specialization of Card: it can be imported, derived by a rule or read out of a store with no document anywhere. The card represents one, and the relation is reached through the card's knowledge layer:

card.metadata.knowledge.type == Relation
card.metadata.relation : Relation<S, T>

That is refinement, reached the same way as any other metadata — see type-system for why representation and ancestry are different mechanisms, graph-type for the edge a relation narrows and hql/knowledge for why significant relationships are preferably given cards.

Narrowing has to be checked. A knowledge.type: Relation entry in the metadata is an author's claim, and typed RelationCard must verify the contract rather than trust the label — an unchecked as would let any YAML key mint a type. The result of a failed narrowing, the spelling of validation and refinement, and whether narrowing is nominal or structural remain open.

person : Person = validate<Person>(card)

Narrowing is also how a vault stays usable while it is being typed. A document that satisfies nothing beyond Doc is still a first-class value; a card that satisfies nothing beyond Card is still in cards and still traversable. Stronger types are earned where there is a reason for them, and the untyped remainder is not an error.

See doc-type for the document a card narrows, data-type for the header's tree type, hql/knowledge for the semantic universe, pipes for composition and hql/design-status for what is still undecided.