vault_relative
All paths above are vault-relative. The vault root is determined by walking up from CWD until _inbox/, _outbox/, or .obsidian/ is found, or by VAULT_ROOT env var, or by a tool-specific override.
The contract
A convention with no declared strength gets argued about every time it is inconvenient. One that says invariant has already had the argument.
vault_relativeAll paths above are vault-relative. The vault root is determined by walking up from CWD until _inbox/, _outbox/, or .obsidian/ is found, or by VAULT_ROOT env var, or by a tool-specific override.
singleton_surfacesThe test for singleton status is stronger than counting instances: would a SECOND, FOLDER-SCOPED instance defeat the reason the surface exists? (CR-036). .handoff/ is outward-facing, self-carrying and holds no vault links (CR-033) — a scoped copy would make the snapshot inherit a context it is built to survive without. .knowledge/ synthesises ACROSS sources with INDEX.md as the single entry point (CR-027) — a scoped copy would prevent the cross-source synthesis that justifies the layer. RESOLVED 260907: .transcripts/ and .ephemeral/ are now singleton. Both had scoped copies — .transcripts/ one level down (consolidated to root under human authorisation, with relative links repaired in the same operation) and .ephemeral/ in three places (all empty, removed). Read-blocked content is only ever relocated on explicit human instruction, never by a skill acting on its own; the link repair is part of the same operation because a moved read-blocked file whose references still point at the old path is worse than the duplication it fixed.
INDEX.mdCONTEXT.md_inbox/_outbox/_config/.handoff/.knowledge/.transcripts/.ephemeral/
single_inbox_outboxExactly one _inbox/ and exactly one _outbox/ per vault, both at vault root. Per-folder inboxes/outboxes are not part of this contract.
manifest_edit_in_placeA manifest that already exists is edited, never regenerated (CR-102). A skill that changes an existing manifest changes only the lines it means to change and carries every other line through verbatim. The dispatcher-owned fields (status, status_note, channel, contact) are never dropped, reordered out of the field block, or reset to a template value by a skill. Read the manifest immediately before writing it and compare with what was last read: if it changed (a status appeared, a field changed), stop and report instead of writing over it. A manifest whose status is past draft (ready, sent, withdrawn) is settled: body sections may be appended to (the outcome and timeline sections), its field block is left alone, and a new version of the material is staged as a new item. Invariant because a violation loses the only record that something left the vault, and a manifest that silently reverts to not-sent invites a second send of the same message
config_resolution_order/ops resolves config in this order: project-level .claude/ops-config.yaml > nearest <folder>/_ops.yaml walking up from CWD > <vault>/_config/base.yaml > skill base.yaml. First match wins. Implemented in v1.16.0 (CR-011). The pre-v1.16.0 chain (~/.claude/skills/{org}-ops-config/{org}.yaml) is deprecated and removed in v1.17.0; until then it acts as a fallback between vault-wide and skill defaults with a one-time per-session deprecation warning.
dot_surface_levelsDot surfaces are of two kinds and skills must treat the second as a typed category, not a list of names. DORMANT (.archive/, .notes/, .ephemeral/, .analytics/): present, rarely read, safe to read on demand. BLOCKED — read access itself is constrained: .transcripts/ = read-block, never read back unattended and never quoted into summaries or insights; .handoff/ = total block, untouched by ops/insights/analytics/sweep/lint/normalize, never indexed, read only when named (CR-033). A new blocked surface must inherit the behaviour without patching each skill. NOTE: .knowledge/wiki/ is NOT blocked — it is read freely and often. Its constraint is WRITE-side (generated, hand-editing forbidden, owned by /insights synthesize, CR-027), which is a different axis; see prefix_known_exception.
audio_transcript_pairingAudio in _inbox/.audio/<basename>.m4a and the matching transcript at _inbox/<basename>.md are paired by basename. Tools that rename one must rename the other. Detailed in CR-012.
placement_classesEvery declared path belongs to exactly one placement class (CR-036). SINGLETON — exactly one, at vault root; a second instance is a finding: INDEX.md, _inbox/, _outbox/, _config/, .handoff/, .knowledge/. PER_FOLDER — appears in every folder of its kind: _meta.yaml, _insights.yaml, _tasks.yaml, _manifest.md, .archive/. PER_FOLDER presence can be CONDITIONAL (CR-040): a folder declaring workflows.task_ledger.mode external or none legitimately has no _tasks.yaml, and that absence is a declaration, not missing data — readers must not treat it as an empty or stalled folder. PER_BOUNDARY — appears where the boundary it describes actually applies: CLAUDE.md, _ops.yaml, CHANGELOG.md, README.md.
prefix_conventionsThe prefix answers ONE question: is this surface READ in everyday work? (CR-034). Underscore = read routinely, sorts first: folders (_inbox/, _outbox/, _contacts/, _config/, _private/, _products/, _infrastructure/) and structure-bearing files that recur in every folder of a kind (_tasks.yaml, _insights.yaml, _meta.yaml, _manifest.md, _ops.yaml, _INDEX-*). Dot = read rarely, on demand, or never. Archive is ALWAYS .archive, never _archive. A dated post never takes underscore — it takes YYMMDD-.
prefix_known_exception.knowledge/ is READ frequently — INDEX.md is the documented first stop for any knowledge question — so by the rule above it would take an underscore. It keeps the dot for compatibility: the name is established in CR-027, INDEX.md, vault CLAUDE.md and every [[wiki]] link, and renaming costs more than the consistency gains. Declared here as a KNOWN EXCEPTION rather than hidden behind a rule it breaks. Do not cite .knowledge/ as precedent for dot-prefixing a new read surface. SECOND DECLARED EXCEPTION: .handoff/_archive keeps its underscore against the archive-is-always-.archive rule, because .handoff/ carries a TOTAL BLOCK (CR-033) — the surface is not touched by any skill, including to rename it. Exception by necessity, not by preference.
generated_view_isolationA generated READ view that lives among its own source material must be isolated STRUCTURALLY, not by a skip flag. Put it in a hidden subfolder (<folder>/.status/current.md) rather than beside the sources as <folder>/_status.md. Reason: CR-033 found four skills reaching .handoff/ files despite an explicit skip-list, because they globbed on the YYMMDD- filename prefix rather than consulting the list. A per-folder markdown file among meeting documents is easier to hit by accident than one in a dot-folder. The dot here does not mean rarely read — the user opens it deliberately; it means unreachable by scanners, which is the .knowledge/wiki/ pattern applied at folder scope.
system_file_languageSystem and structure files carry ENGLISH names regardless of the language of their content: _config/priority.md, _config/naming-prefix.md, _inbox/_frame.md, .status/current.md. Content stays in the vault's working language. Applies going FORWARD — the derived registers with Swedish names (_INDEX-agare.md, _INDEX-koppling.md, _INDEX-maskiner.md, _INDEX-objektmodell.md, _INDEX-verksamheter.md) are read by scripts and renaming them costs more than the consistency gains; the generated wiki articles under .knowledge/wiki/ likewise keep the language of the corpus they summarise.
identifier_languageIdentifiers are ENGLISH; the words a person reads may be in any language, and are data rather than literals in code. This covers schema field names, enum values, status keys, config keys, dictionary keys and variable names — everything a second tool must agree with in order to interoperate. Display text (a manifest's Status wording, an external board's column name, a generated view's headings) stays in the vault's working language and lives in a settings or vocabulary file, so changing it is a settings change rather than a code change. Where an identifier and its label were historically the same string, the LABEL keeps the existing spelling: renaming a key costs nothing, while renaming a label changes what an external system or a person sees. A reader meeting a value it does not recognise should tolerate it rather than silently treat it as the default — see task_statuses, where recognising only the canonical set made finished tasks count as outstanding. THIS DECLARATION FOLLOWS ITS OWN RULE: where a field is named here, it is named by its identifier, not by the label a vault happens to write it under. Five entries described a manifest's fields as Status/Kanal/Kontakt until 2026-09-21, which reads as Swedish identifiers in a public contract and invites a tool to hardcode the label.
filename_role_keywordA filename is YYMMDD-<role>-<description>.md (CR-089). The ROLE keyword is an identifier — scripts and skills select files by it — so it is ENGLISH and comes from `terms:` (`file:`). The description stays in the working language and keeps å/ä/ö (CR-021). FORWARD ONLY: existing files are never renamed; every reader also accepts the term's `legacy_file:` keywords, and a check flags only files dated on or after the release. A series' declared note_suffix / agenda_suffix always wins over the default keyword, because a series that changed shape mid-history reads as a broken carry-forward chain.
one_term_per_conceptEvery concept has one term, declared once in `terms:` (CR-089). Skill names are nouns (the place or object owned) unless declared `kind: command_name`; subcommands are verbs, or `<object> <verb>`; a verb means the same thing in every skill; the loop's step ids ARE the command verbs; anything outside the loop is named for what it repairs or explains. A localized word not declared in a term's language column is drift. `avoid:` phrases are checked by scripts/check-terms.py.
write_ownershipIndependent of the prefix, every surface declares who may write it. HUMAN: _inbox/_capture.md, _inbox/_frame.md, _meta.yaml, .handoff/ (once, then frozen). GENERATED — hand-editing forbidden: .knowledge/wiki/ (/insights synthesize, CR-027), _INDEX-maskiner.md and _INDEX-koppling.md (scan.py), the personal working-document view (CR-035). MIXED: _tasks.yaml, _insights.yaml. A generated surface that is edited by hand loses its next regeneration silently — which is why the property is declared rather than inferred from the prefix.
audit_lifecycleAn audit or review document lives in _inbox/ only WHILE IN USE. Once its decisions are made and executed it moves to _inbox/.archive/. An audit left in place after execution becomes a competing source of truth against the working document (observed 260907). _inbox/ normally holds at most three CONTENT files: the working document, one active audit, one active review. Declared system files do not count against the ceiling (_capture.md, _frame.md, _inbox.yaml, _tasks.yaml) — they are infrastructure of the surface, not material passing through it. More than three content files means something has taken up residence, which CR-025 forbids in principle but does not measure.
stale_vs_junkStale is not junk. A document that WAS current and is now outdated is archived to .archive/ (never deleted). Material with no vault destination goes to .ephemeral/ and may die (swept after 14 days, CR-024). Conflating the two loses history or retains rubbish.
changelog_readme_conditionsCHANGELOG.md belongs only where history is actually kept — contacts, organisations, projects, products. A CHANGELOG in a folder that never retires anything is noise (observed: 130 of 174 have no sibling .archive/). README.md belongs only where a folder needs explaining to someone OTHER THAN its author (observed: 269 instances). Forward-looking: governs new folders, and is a sweep WARNING rather than an error. INDEX.md is root plus generated registers only.
One concept, one word — in the skill name, the subcommand, the loop step, the filename and here. The identifier is English; the Swedish word is the only permitted translation.
| Term | Swedish | Loop step | Filename | Note |
|---|---|---|---|---|
| vault | valv | |||
| venture | verksamhet | a top-level business folder; the config key `organization` keeps its name | ||
| project | projekt | a folder whose config runs the loop | ||
| contact | kontakt | a folder under _contacts/, with no config | ||
| config | konfiguration | the resolved chain: project, _ops.yaml, _config/base.yaml, defaults | ||
| meeting | möte | one occurrence: a meeting, a call or a recording | ||
| series | serie | |||
| session | session | a Claude Code session; in older text also a meeting of a series | ||
| transcript | transkript | the input; the raw copy lives in .transcripts/ | ||
| summary | sammanfattning | process | summarystill read:samtal, sammanfattning, möte | the config keys note_suffix keep their name |
| agenda | agenda | prepare | agendastill read:förberedelse, preparation | |
| facilitator sheet | facilitatorsblad | facilitate | facilitator | |
| carry-forward | överföring | carry | ||
| recap | recap | recap | recap | |
| priorities | prioriteringar | priorities | ||
| content | underlag | content | incoming material the vault received and did not produce: a report, a newsletter, an automated digest. Not a meeting -- it has a sender, not participants, and no facilitator. CR-105 | |
| correspondence | korrespondens | correspondence | a two-way thread with a person: turns alternate and a decision may be reached in the exchange. Sender and recipients PER TURN, never a participant list. One-way material is `content` instead. CR-080 | |
| task | uppgift | the action-table column (Action / Åtgärd) is a template-contract heading (CR-018) and keeps its label | ||
| ledger | uppgiftsregister | where tasks live; matches workflows.task_ledger | ||
| index | index | the generated _INDEX-* files; a project REGISTRY (CR-086) is a different, hand-written thing | ||
| insight | insikt | |||
| rule | regel | an insight confirmed often enough to be loaded as a standing instruction (CR-013) | ||
| convention | konvention | the contract's rules for the vault (vault_conventions.conventions, CR-092) | ||
| standard | standard | a convention's middle level: holds generally, exceptions named (CR-037, CR-092) | ||
| wiki | wiki | compound | ||
| handoff | överlämning | handoff | ||
| fetch | hämta | fetch | an external system into a local archive; component ids keep 'archiver' | |
| archive | arkiv | the fetched local copy only | ||
| snapshot | ögonblicksbild | a dated file inside an archive | ||
| retire | pensionera | to .archive/ or a tombstone; the folder keeps its name | ||
| close | stänga | a resolved outbox item, filed with its recipient | ||
| ops · command name | the config layer; the suite's major part | |||
| preparation · command name | the config-free form of /ops prepare |
From ecosystem.yaml, contract 41 · core-skills 1.89.4 · read at build 2026-10-08