Spec Studio

An Obsidian vault lets a person iterate on designs from the one graph

spec:consumers.obsidian-vaultUse Case / Behavior · featureready floor ready

Packs: pack:obsidian-vault-v1, pack:self-hosting-v1

Design: 13 design entries · 5 pinned declarations · 2 decisions

Findings: no errors, no warnings

specs/consumers/obsidian-vault.sdp.md

No open questions and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle present.

The Design Review links a Spec's relations both ways and gives keyed Pack questions a separate carrier-line link, but it offers no built-in filtering of a Pack or navigation from arbitrary prose mentions to their entries. The Spec Studio serves a person who iterates on a design in the repository and a pull request reviewer who holds only a link. A linked-notes view lets a person follow relations, filter members by rung and design columns, open entries by address and see each pass's edits arrive. It is a projection of its own, not the Studio's local half: the Studio's page set serves a reader who holds a link, the vault serves a person who iterates in a notes app, and each has its own verb; the verb that writes every projection root writes the vault too.

Obsidian already does that for linked Markdown notes with properties: backlinks, a graph view, search, and filterable tables it calls Bases. The first prototype, in explorations/obsidian-vault/, wrote notes from dumped graph and reader values, with relations and checked mentions as wikilinks, entry addresses as block links, and Bases for Pack members and standing queries. The second prototype checked the vault this Pack designs on the Protocol's corpus and the first adopter's: links that resolve, Bases whose views select what their recipes select, two identical renders, and a person's notes that survive. Its measurements remain in reviews/assets/plan46-prototypes/vault-measurements.md; git holds the replaced prototype.

The principle is a linked-notes view regenerated from the one graph; Obsidian and its Bases are the representation chosen first. Like every projection, the vault states no truth, and two runs over one graph write the same bytes, so no note names a commit or a time. Intent is still written only in the carriers. One thing sets the vault apart from the other projections: a person keeps it open and writes notes of their own in it. So the command owns one folder of the vault and a list of what it wrote there, and synchronizes that folder instead of rewriting a root wholesale; the vault's owned-folder decision, spec:decisions.vault-owned-folder, says why. For the same reason the vault never lies where the shared build removes files, and a run refuses rather than guess whose a file is.

The default vault sits under generated/, which discovery skips and an adopter usually keeps out of git. A person who wants to keep the intent notes they write in the vault points --out at a directory of their own.

Intent
actor
A person in the repository who designs a capability in passes while an agent edits the carriers.
problem
The Markdown Design Review offers no built-in Pack filters or navigation from arbitrary prose mentions to their entries, so a person iterating on a design still reads carrier files alongside it.
outcome
Generate an Obsidian vault from the one graph, in which a person reads, navigates and filters the corpus and follows each pass's edits, while the carriers stay the only place intent is written.
risks
  • A notes app invites editing, and an edit to a generated note never reaches the carrier.
Behavior

The world writes a probe corpus of one Pack and its member Specs, runs the vault command with the vault under the root's generated/ directory unless a point names another, and reads the vault from disk; a second run reads the same graph. The world leaves a stopped run's files by running the command with its first rename failing, which leaves the file list recording both contents and a whole temporary beside its target, and a failed run's files by running it while the member's carrier is one the extractor refuses, then repairing that carrier. A fresh vault is one the command writes into an empty directory from the same graph.

rule.ownProjection line 39 · specs/consumers/obsidian-vault.sdp.md:39
The vault is a projection of its own, written only through its synchronization, which sdp vault and the verb that writes every projection root run, and no part of the Studio's page set.

no example exercises it

rule.verb line 40 · specs/consumers/obsidian-vault.sdp.md:40
sdp vault [root | --root PATH] [--exclude PATH]... [--out DIR] [--watch] validates the graph as sdp validate does and then synchronizes the vault at DIR, by default generated/obsidian-vault/ under the root; the root is given as an argument or by --root, never both, and defaults to the working directory, each exclusion is a root-relative path prefix as for the publishers, and --out resolves against the working directory; a root given both ways, --root or --out given twice or without a value, and any other option refuse the run.

spec:consumers.obsidian-vault.fresh-vault · enabled

rule.outsideExtraction line 41 · specs/consumers/obsidian-vault.sdp.md:41
A vault directory inside the root must lie under a directory discovery skips or under an excluded prefix, so no file in the vault is ever read as a carrier or a source; any other vault directory inside the root refuses the run before the build, writing and removing nothing.

no example exercises it

rule.apartFromBuild line 42 · specs/consumers/obsidian-vault.sdp.md:42
A vault directory that is, lies inside or holds a path the shared build writes or removes, as spec:consumers.obsidian-vault#design.buildOwnedPaths lists, refuses the run before the build, writing and removing nothing, so no build ever removes a file in the vault.

spec:consumers.obsidian-vault.vault-in-build-path-refused · enabled

rule.ownedFolder line 43 · specs/consumers/obsidian-vault.sdp.md:43
The command writes only the generated folder sdp/ inside the vault; Obsidian's settings, a person's intent notes and every other file of the vault lie outside it and are never written, moved or removed.

spec:consumers.obsidian-vault.fresh-vault · enabled, spec:consumers.obsidian-vault.person-notes-survive · enabled, spec:consumers.obsidian-vault.vault-in-build-path-refused · enabled

rule.guard line 44 · specs/consumers/obsidian-vault.sdp.md:44
Before the build and before any cleanup, a run inspects sdp/ and every entry under it, names that begin with a dot included, and refuses when sdp/ or an entry under it is a symbolic link or neither a file nor a directory, when the file list is not well formed, when a file other than the file list, the command's own temporaries and a .DS_Store file is one the list does not name, or when a listed file other than a .base file has bytes matching no content the list records for it; a refused run writes nothing, removes nothing, names each such path and exits 1. A .DS_Store file under sdp/ is ignored. A listed .base file whose bytes a person changed is kept, even when it leaves the generated set; the run warns with its path and the words delete it to regenerate, keeps its recorded content in the list, and goes on. Without a file list, every file under sdp/ other than .DS_Store is one the list does not name.

spec:consumers.obsidian-vault.changed-note-refused · enabled, spec:consumers.obsidian-vault.failed-extraction-recovered · enabled, spec:consumers.obsidian-vault.hidden-file-refused · enabled, spec:consumers.obsidian-vault.unknown-note-refused · enabled

rule.synchronize line 45 · specs/consumers/obsidian-vault.sdp.md:45
A run that passes the guard first removes each file the list names that the new set no longer holds, except a changed .base file; then it writes each generated file whose bytes differ from the file on disk through a temporary sibling renamed into place, except a changed .base file, leaves every file whose bytes already match untouched, removes every directory under sdp/ left empty, and writes the file list last.

spec:consumers.obsidian-vault.interrupted-run-recovered · enabled, spec:consumers.obsidian-vault.person-notes-survive · enabled

rule.interruptedRun line 46 · specs/consumers/obsidian-vault.sdp.md:46
Before it changes any file, a run records in the file list both the content each path holds and the content it will hold, so a run stopped at any point leaves every generated file other than a kept, changed .base file matching the list, and the next run recognizes each, and each whole temporary, as its own and completes the synchronization; a temporary cut short while it was written matches no recorded content, so the guard refuses it as unknown and the person deletes it.

spec:consumers.obsidian-vault.interrupted-run-recovered · enabled

rule.standsApart line 47 · specs/consumers/obsidian-vault.sdp.md:47
The vault is not one of the five projection roots the shared build rewrites: the build's invalidation never removes it, and the repository's projection suite does not certify it. The verb that writes every projection root also synchronizes the vault, at the directory sdp vault would use for the same root unless that verb names another, through the same directory checks and guard, before it publishes any root; a refusal there leaves the vault untouched and fails that run. sdp vault enters the shared build, so it removes the five projection roots as sdp validate does.

no example exercises it

rule.deterministic line 48 · specs/consumers/obsidian-vault.sdp.md:48
Without kept, changed .base files, two runs over one graph write byte-identical files under sdp/, the file list included, and no file there holds a time, a commit, an absolute path or a host name.

spec:consumers.obsidian-vault.fresh-vault · enabled

rule.linksResolve line 49 · specs/consumers/obsidian-vault.sdp.md:49
Every wikilink, embed and block link a generated note writes resolves to a file the run wrote or kept and, with a block, to a block id that file carries; no note links outside the vault, a carrier or source file is named by its root-relative path in a code span, and text in a code span or a fenced block, where Obsidian reads no link, is no link.

spec:consumers.obsidian-vault.fresh-vault · enabled

rule.findingsShown line 50 · specs/consumers/obsidian-vault.sdp.md:50
Findings never withhold the vault: a run over a graph with validation findings still synchronizes and returns the validation exit code. A finding whose subject is a Spec or a Pack with a note shows on that note; every other finding shows on the index with its severity, its validator, its file and line when known and its message; and the index counts every finding by severity.

no example exercises it

rule.failedExtraction line 51 · specs/consumers/obsidian-vault.sdp.md:51
A run whose extraction fails keeps every generated file but the index and the file list: it rewrites the index to open with a warning naming the failure, through the steps spec:consumers.obsidian-vault#rule.interruptedRun and spec:consumers.obsidian-vault#rule.synchronize state, so the file list ends recording the warning index, and exits 1. The next run that succeeds finds the index matching the list and writes it whole again; with no earlier run, the failed run leaves only that index and the file list under sdp/.

spec:consumers.obsidian-vault.failed-extraction-keeps-notes · enabled, spec:consumers.obsidian-vault.failed-extraction-recovered · enabled

rule.plainNotes line 52 · specs/consumers/obsidian-vault.sdp.md:52
Notes are plain Markdown with YAML properties, wikilinks and block ids, which other notes tools read; only the .base files are Obsidian's own.

no example exercises it

rule.openAsVault line 53 · specs/consumers/obsidian-vault.sdp.md:53
A person opens the vault directory itself as an Obsidian vault, and every link and Base filter names its target by a path from that directory.

no example exercises it

0 unkeyed rules.

Verifying examples

A generated note a person changed refuses the regeneration and keeps the edit · spec:consumers.obsidian-vault.changed-note-refused · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files an earlier run wrote"}
Given the vault holds {extra: "a generated note a person changed"}
Given the member {change: "stays in the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 1}
Then the run refuses the path {refusedPath: "sdp/specs/probe/spec.probe.member.md"} as {refusal: "changed"}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

A failed extraction keeps the notes and says so on the index · spec:consumers.obsidian-vault.failed-extraction-keeps-notes · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files an earlier run wrote"}
Given the vault holds {extra: "an intent note and Obsidian settings outside the generated folder"}
Given the member {change: "has a carrier the extractor refuses"}
When the vault command runs at that root
Then the run exits {exitCode: 1}
Then the member's note is present: {memberNote: true}
Then the index note warns that the last regeneration failed: {indexWarns: true}
Then every generated file matches a content the file list records for it: {listMatches: true}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

The run after a failed extraction passes the guard and restores the index · spec:consumers.obsidian-vault.failed-extraction-recovered · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files a run whose extraction failed left"}
Given the vault holds {extra: "an intent note and Obsidian settings outside the generated folder"}
Given the member {change: "stays in the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 0}
Then the index note warns that the last regeneration failed: {indexWarns: false}
Then the generated folder is byte-identical to a fresh vault's, the file list included: {matchesFresh: true}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

A fresh vault is written the same way twice and every link resolves · spec:consumers.obsidian-vault.fresh-vault · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "nothing"}
Given the vault holds {extra: "no other file"}
Given the member {change: "stays in the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 0}
Then a second run over the same graph leaves every generated file byte-identical: {byteIdentical: true}
Then the member's note is present: {memberNote: true}
Then every link a generated note writes resolves inside the vault: {linksResolve: true}

Open example

A person's file under a temporary's name refuses the regeneration · spec:consumers.obsidian-vault.hidden-file-refused · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files an earlier run wrote"}
Given the vault holds {extra: "a file a person wrote inside the generated folder under a temporary's name"}
Given the member {change: "stays in the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 1}
Then the run refuses the path {refusedPath: "sdp/specs/probe/.spec.probe.member.md.sdp-tmp"} as {refusal: "unknown"}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

A run after a stopped synchronization finishes it · spec:consumers.obsidian-vault.interrupted-run-recovered · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files a run stopped at its first rename left"}
Given the vault holds {extra: "an intent note and Obsidian settings outside the generated folder"}
Given the member {change: "changes its title"}
When the vault command runs at that root
Then the run exits {exitCode: 0}
Then the generated folder is byte-identical to a fresh vault's, the file list included: {matchesFresh: true}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

A regeneration removes a note that left the graph and keeps a person's notes · spec:consumers.obsidian-vault.person-notes-survive · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files an earlier run wrote"}
Given the vault holds {extra: "an intent note and Obsidian settings outside the generated folder"}
Given the member {change: "leaves the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 0}
Then the member's note is present: {memberNote: false}
Then every file the person wrote is byte-identical: {personFilesKept: true}
Then every link a generated note writes resolves inside the vault: {linksResolve: true}

Open example

A note a person wrote inside the generated folder refuses the regeneration · spec:consumers.obsidian-vault.unknown-note-refused · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given a vault whose generated folder holds {generated: "the files an earlier run wrote"}
Given the vault holds {extra: "a note a person wrote inside the generated folder"}
Given the member {change: "leaves the corpus"}
When the vault command runs at that root
Then the run exits {exitCode: 1}
Then the run refuses the path {refusedPath: "sdp/specs/probe/mine.md"} as {refusal: "unknown"}
Then the member's note is present: {memberNote: true}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

A vault directory the build removes refuses the run and keeps a person's notes · spec:consumers.obsidian-vault.vault-in-build-path-refused · ready floor ready

Executable Spec

Given an extraction root holding a Pack and its member Specs, among them {member: "spec:probe.member"}
Given the vault command names the vault directory {out: "generated/design-review"}
Given a vault whose generated folder holds {generated: "nothing"}
Given the vault holds {extra: "an intent note and Obsidian settings outside the generated folder"}
When the vault command runs at that root
Then the run exits {exitCode: 1}
Then the run refuses the vault directory as {placement: "overlapping a path the build owns"}
Then every file the person wrote is byte-identical: {personFilesKept: true}

Open example

Example space

Given an extraction root holding a Pack and its member Specs, among them {member:string}
Given the vault command names the vault directory {out:string}
Given a vault whose generated folder holds {generated:"nothing"|"the files an earlier run wrote"|"the files a run stopped at its first rename left"|"the files a run whose extraction failed left"}
Given the vault holds {extra:"no other file"|"an intent note and Obsidian settings outside the generated folder"|"a note a person wrote inside the generated folder"|"a file a person wrote inside the generated folder under a temporary's name"|"a generated note a person changed"}
Given the member {change:"stays in the corpus"|"leaves the corpus"|"changes its title"|"has a carrier the extractor refuses"}
When the vault command runs at that root
Then the run exits {exitCode:number}
Then the run refuses the path {refusedPath:string} as {refusal:"unknown"|"changed"}
Then the run refuses the vault directory as {placement:"overlapping a path the build owns"|"inside the extraction"}
Then the index note warns that the last regeneration failed: {indexWarns:boolean}
Then every generated file matches a content the file list records for it: {listMatches:boolean}
Then the generated folder is byte-identical to a fresh vault's, the file list included: {matchesFresh:boolean}
Then a second run over the same graph leaves every generated file byte-identical: {byteIdentical:boolean}
Then the member's note is present: {memberNote:boolean}
Then every file the person wrote is byte-identical: {personFilesKept:boolean}
Then every link a generated note writes resolves inside the vault: {linksResolve:boolean}
Design
layout line 56 · specs/consumers/obsidian-vault.sdp.md:56
the generated folder sdp/ holds index.md, specs.base, specs/<family>/<note>.md, packs/<note>.md beside packs/<note>.base, code/<namespace>/<note>.md, terms/<note>.md and the file list .sdp-vault.json; <family> is the first segment of a Spec's path, <namespace> a code unit's or anchor's namespace, and <note> the note name spec:consumers.obsidian-vault.notes#design.noteName states.
fileList line 57 · specs/consumers/obsidian-vault.sdp.md:57
{ "schema": "sdp-vault/1", "files": { "<path under sdp/>": ["<sha256 hex of the file's bytes>"] } } in sdp/.sdp-vault.json, paths sorted in code-unit order, JSON with two-space indentation and a closing newline; a path lists one content after a completed run and at most two while a run is under way, the content it held first.

no citation recorded

wellFormedList line 58 · specs/consumers/obsidian-vault.sdp.md:58
a file list is well formed when it parses to the fileList shape, every path is relative to sdp/, /-separated, with no empty, . or .. segment, and names neither the file list nor a temporary, every content is 64 lowercase hex digits, every path lists one or two contents, and the file's bytes equal the serialization spec:consumers.obsidian-vault#design.fileList states of what it parses to, so a repeated or reordered path is not well formed.
temporaryName line 59 · specs/consumers/obsidian-vault.sdp.md:59
a file is written as .<name>.sdp-tmp in its own directory and renamed over <name>; a temporary a run finds is the command's own only when its bytes match a content the file list records for <name>, and the run removes it after the guard passes; the file list's own temporary is the command's own only when it is itself a well-formed file list; any other file of a temporary's name is one the list does not name.
buildOwnedPaths line 60 · specs/consumers/obsidian-vault.sdp.md:60
under the root, generated/graph.json, generated/contracts/, generated/registrars.json, generated/imports.json, generated/design-review/, generated/census/, generated/mermaid/, generated/gherkin/ and generated/spec-studio/, each with its .tmp sibling, and every path the shared build comes to write or remove.

Cited at

  • test/skills.test.ts:2037
guardMessage line 61 · specs/consumers/obsidian-vault.sdp.md:61
one stderr line per refused path, with <path> relative to the vault: sdp vault: <path> is not a file this command wrote (unknown), sdp vault: <path> has changed since this command wrote it (changed), sdp vault: <path> is a link or a special file, which this command never writes (not a file) or sdp vault: sdp/.sdp-vault.json is not a file list this command wrote (malformed); then sdp vault: nothing was written or removed; keep what you wrote outside sdp/, then delete each refused path, or delete sdp/ whole.
outsideExtractionMessage line 62 · specs/consumers/obsidian-vault.sdp.md:62
sdp vault: the vault <out> lies inside the root and would be read as source; choose a directory under generated/, under an excluded prefix, or outside the root.

no citation recorded

buildOwnedMessage line 63 · specs/consumers/obsidian-vault.sdp.md:63
sdp vault: the vault <out> overlaps <path>, which every build rewrites or removes; choose another directory, such as generated/obsidian-vault/, with <path> the first overlapping path in the order spec:consumers.obsidian-vault#design.buildOwnedPaths lists them.

no citation recorded

summaryLine line 64 · specs/consumers/obsidian-vault.sdp.md:64
on stdout after a synchronization, Wrote <out>/sdp (<n> files: <w> written, <u> unchanged, <r> removed).
exitCodes line 65 · specs/consumers/obsidian-vault.sdp.md:65
0 when the vault is synchronized over a graph with no validation error; the validation exit code when it is synchronized over one with errors; 1 on a refused option, a refused vault directory, a guard refusal, a case collision, a failed extraction or a write that fails.
indexNote line 66 · specs/consumers/obsidian-vault.sdp.md:66
sdp/index.md opens with > [!info] Generated by `sdp vault` from the graph. Every file under `sdp/` is regenerated, except a `.base` file you changed: write your own notes outside it, and intent notes in `intent/`. , then # Spec graph, the finding counts by severity, ## Findings without a note with one line per finding whose subject has no Spec or Pack note, in report order, the corpus Base embedded, and a link to each Pack note in id order.

no citation recorded

findingLine line 67 · specs/consumers/obsidian-vault.sdp.md:67
- <severity> · <validator id> · <file>:<line> · <message>, leaving out the file and line when the finding names none and the line when it names only a file, with the message escaped as spec:consumers.obsidian-vault.notes#rule.textEscaped states.

no citation recorded

failedIndex line 68 · specs/consumers/obsidian-vault.sdp.md:68
after a failed extraction, sdp/index.md opens with > [!warning] The last regeneration failed, and the notes show the graph of the last one that succeeded. followed by one line per extraction error, its root-relative file and its message.
Verification

Verifier bindings

  • spec:consumers.obsidian-vault.changed-note-refused · A generated note a person changed refuses the regeneration and keeps the edit · declared · example enabled · specs/consumers/obsidian-vault.changed-note-refused.sdp.md

    External run evidence from test/vault-examples.test.ts

    Test anchors: test:protocol.consumers.obsidian-vault.bases.views-match-sources, test:protocol.consumers.obsidian-vault.changed-note-refused, test:protocol.consumers.obsidian-vault.failed-extraction-keeps-notes, test:protocol.consumers.obsidian-vault.failed-extraction-recovered, test:protocol.consumers.obsidian-vault.fresh-vault, test:protocol.consumers.obsidian-vault.hidden-file-refused, test:protocol.consumers.obsidian-vault.interrupted-run-recovered, test:protocol.consumers.obsidian-vault.iteration.watch-rewrites-changed-notes, test:protocol.consumers.obsidian-vault.iteration.watch-source-anchor, test:protocol.consumers.obsidian-vault.notes.case-collision-refused, test:protocol.consumers.obsidian-vault.notes.entry-address-link, test:protocol.consumers.obsidian-vault.notes.term-address-link, test:protocol.consumers.obsidian-vault.notes.unresolved-mention-stays, test:protocol.consumers.obsidian-vault.person-notes-survive, test:protocol.consumers.obsidian-vault.unknown-note-refused, test:protocol.consumers.obsidian-vault.vault-in-build-path-refused. Specs verified by those bindings: spec:consumers.obsidian-vault, spec:consumers.obsidian-vault.bases, spec:consumers.obsidian-vault.bases.views-match-sources, spec:consumers.obsidian-vault.changed-note-refused, spec:consumers.obsidian-vault.failed-extraction-keeps-notes, spec:consumers.obsidian-vault.failed-extraction-recovered, spec:consumers.obsidian-vault.fresh-vault, spec:consumers.obsidian-vault.hidden-file-refused, spec:consumers.obsidian-vault.interrupted-run-recovered, spec:consumers.obsidian-vault.iteration, spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes, spec:consumers.obsidian-vault.iteration.watch-source-anchor, spec:consumers.obsidian-vault.notes, spec:consumers.obsidian-vault.notes.case-collision-refused, spec:consumers.obsidian-vault.notes.entry-address-link, spec:consumers.obsidian-vault.notes.term-address-link, spec:consumers.obsidian-vault.notes.unresolved-mention-stays, spec:consumers.obsidian-vault.person-notes-survive, spec:consumers.obsidian-vault.unknown-note-refused, spec:consumers.obsidian-vault.vault-in-build-path-refused.

    Run 1: external run evidence from vitest 4.1.10

    Build: CI 380 · Branch: feature/roadmap-and-slices-v0 · Commit: 34909918bf0be0e849959644401797191cae379f · Start: 2026-10-22T17:40:31.456Z · Stop: 2026-10-22T17:42:16.171Z

    NameSuiteResultDuration
    keeps incomplete oracle points unspecifiednot recordedpassed1.89 ms
    executes the bound example against a fresh worldA fresh vault is written the same way twice and every link resolves (spec:consumers.obsidian-vault.fresh-vault)passed39.02 ms
    executes the bound example against a fresh worldA regeneration removes a note that left the graph and keeps a person's notes (spec:consumers.obsidian-vault.person-notes-survive)passed20.74 ms
    executes the bound example against a fresh worldA note a person wrote inside the generated folder refuses the regeneration (spec:consumers.obsidian-vault.unknown-note-refused)passed5.75 ms
    executes the bound example against a fresh worldA generated note a person changed refuses the regeneration and keeps the edit (spec:consumers.obsidian-vault.changed-note-refused)passed4.83 ms
    executes the bound example against a fresh worldA person's file under a temporary's name refuses the regeneration (spec:consumers.obsidian-vault.hidden-file-refused)passed6.02 ms
    executes the bound example against a fresh worldA vault directory the build removes refuses the run and keeps a person's notes (spec:consumers.obsidian-vault.vault-in-build-path-refused)passed1.15 ms
    executes the bound example against a fresh worldA run after a stopped synchronization finishes it (spec:consumers.obsidian-vault.interrupted-run-recovered)passed33.58 ms
    executes the bound example against a fresh worldA failed extraction keeps the notes and says so on the index (spec:consumers.obsidian-vault.failed-extraction-keeps-notes)passed10.21 ms
    executes the bound example against a fresh worldThe run after a failed extraction passes the guard and restores the index (spec:consumers.obsidian-vault.failed-extraction-recovered)passed20.42 ms
    executes the bound example against a fresh worldAn entry address in prose opens the entry's block in its Spec's note (spec:consumers.obsidian-vault.notes.entry-address-link)passed15.36 ms
    executes the bound example against a fresh worldA mention of an entry that does not exist stays as written (spec:consumers.obsidian-vault.notes.unresolved-mention-stays)passed9.95 ms
    executes the bound example against a fresh worldA keyed Model term gets a note, and its address in prose opens the term's block (spec:consumers.obsidian-vault.notes.term-address-link)passed14.06 ms
    executes the bound example against a fresh worldTwo Specs whose notes differ only in case refuse the run (spec:consumers.obsidian-vault.notes.case-collision-refused)passed2.55 ms
    executes the bound example against a fresh worldEach standing view selects what its source selects (spec:consumers.obsidian-vault.bases.views-match-sources)passed24.64 ms
    executes the bound example against a fresh worldA carrier edit under watch rewrites only the notes it changes (spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes)passed446.54 ms
    executes the bound example against a fresh worldAn anchor added in a source file under watch rewrites the Spec's note (spec:consumers.obsidian-vault.iteration.watch-source-anchor)passed516.04 ms
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes · A failed extraction keeps the notes and says so on the index · declared · example enabled · specs/consumers/obsidian-vault.failed-extraction-keeps-notes.sdp.md
  • spec:consumers.obsidian-vault.failed-extraction-recovered · The run after a failed extraction passes the guard and restores the index · declared · example enabled · specs/consumers/obsidian-vault.failed-extraction-recovered.sdp.md
  • spec:consumers.obsidian-vault.fresh-vault · A fresh vault is written the same way twice and every link resolves · declared · example enabled · specs/consumers/obsidian-vault.fresh-vault.sdp.md
  • spec:consumers.obsidian-vault.hidden-file-refused · A person's file under a temporary's name refuses the regeneration · declared · example enabled · specs/consumers/obsidian-vault.hidden-file-refused.sdp.md
  • spec:consumers.obsidian-vault.interrupted-run-recovered · A run after a stopped synchronization finishes it · declared · example enabled · specs/consumers/obsidian-vault.interrupted-run-recovered.sdp.md
  • spec:consumers.obsidian-vault.person-notes-survive · A regeneration removes a note that left the graph and keeps a person's notes · declared · example enabled · specs/consumers/obsidian-vault.person-notes-survive.sdp.md
  • spec:consumers.obsidian-vault.unknown-note-refused · A note a person wrote inside the generated folder refuses the regeneration · declared · example enabled · specs/consumers/obsidian-vault.unknown-note-refused.sdp.md
  • spec:consumers.obsidian-vault.vault-in-build-path-refused · A vault directory the build removes refuses the run and keeps a person's notes · declared · example enabled · specs/consumers/obsidian-vault.vault-in-build-path-refused.sdp.md

Example space

  • member · string · declared by an extraction root holding a Pack and its member Specs, among them {member}
  • out · string · declared by the vault command names the vault directory {out}
  • generated · "nothing" | "the files an earlier run wrote" | "the files a run stopped at its first rename left" | "the files a run whose extraction failed left" · declared by a vault whose generated folder holds {generated}
  • extra · "no other file" | "an intent note and Obsidian settings outside the generated folder" | "a note a person wrote inside the generated folder" | "a file a person wrote inside the generated folder under a temporary's name" | "a generated note a person changed" · declared by the vault holds {extra}
  • change · "stays in the corpus" | "leaves the corpus" | "changes its title" | "has a carrier the extractor refuses" · declared by the member {change}

Bound points

Examplememberoutgeneratedextrachange
spec:consumers.obsidian-vault.changed-note-refused"spec:probe.member"unbound"the files an earlier run wrote""a generated note a person changed""stays in the corpus"
spec:consumers.obsidian-vault.failed-extraction-keeps-notes"spec:probe.member"unbound"the files an earlier run wrote""an intent note and Obsidian settings outside the generated folder""has a carrier the extractor refuses"
spec:consumers.obsidian-vault.failed-extraction-recovered"spec:probe.member"unbound"the files a run whose extraction failed left""an intent note and Obsidian settings outside the generated folder""stays in the corpus"
spec:consumers.obsidian-vault.fresh-vault"spec:probe.member"unbound"nothing""no other file""stays in the corpus"
spec:consumers.obsidian-vault.hidden-file-refused"spec:probe.member"unbound"the files an earlier run wrote""a file a person wrote inside the generated folder under a temporary's name""stays in the corpus"
spec:consumers.obsidian-vault.interrupted-run-recovered"spec:probe.member"unbound"the files a run stopped at its first rename left""an intent note and Obsidian settings outside the generated folder""changes its title"
spec:consumers.obsidian-vault.person-notes-survive"spec:probe.member"unbound"the files an earlier run wrote""an intent note and Obsidian settings outside the generated folder""leaves the corpus"
spec:consumers.obsidian-vault.unknown-note-refused"spec:probe.member"unbound"the files an earlier run wrote""a note a person wrote inside the generated folder""leaves the corpus"
spec:consumers.obsidian-vault.vault-in-build-path-refused"spec:probe.member""generated/design-review""nothing""an intent note and Obsidian settings outside the generated folder"unbound

Oracle binding: present · oracle:protocol.vault · test/vault-examples.test.ts:140

Coverage

member: "spec:probe.member" · spec:consumers.obsidian-vault.changed-note-refused, spec:consumers.obsidian-vault.failed-extraction-keeps-notes, spec:consumers.obsidian-vault.failed-extraction-recovered, spec:consumers.obsidian-vault.fresh-vault, spec:consumers.obsidian-vault.hidden-file-refused, spec:consumers.obsidian-vault.interrupted-run-recovered, spec:consumers.obsidian-vault.person-notes-survive, spec:consumers.obsidian-vault.unknown-note-refused, spec:consumers.obsidian-vault.vault-in-build-path-refused · not enumerable

out: "generated/design-review" · spec:consumers.obsidian-vault.vault-in-build-path-refused · not enumerable

Unbound

Design view

Requirement drawing

Requirement drawing for spec:consumers.obsidian-vault

Shown as text: this drawing exceeds the height budget of 2000 pixels after folding.

The diagram as text
  • spec:consumers.obsidian-vault: An Obsidian vault lets a person iterate on designs from the one graph · declared Spec
  • spec:consumers.obsidian-vault.bases: A Pack's members and the standing queries are tables over the reader's values · declared Spec
  • spec:consumers.obsidian-vault.changed-note-refused: A generated note a person changed refuses the regeneration and keeps the edit · declared Spec
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes: A failed extraction keeps the notes and says so on the index · declared Spec
  • spec:consumers.obsidian-vault.failed-extraction-recovered: The run after a failed extraction passes the guard and restores the index · declared Spec
  • spec:consumers.obsidian-vault.fresh-vault: A fresh vault is written the same way twice and every link resolves · declared Spec
  • spec:consumers.obsidian-vault.hidden-file-refused: A person's file under a temporary's name refuses the regeneration · declared Spec
  • spec:consumers.obsidian-vault.interrupted-run-recovered: A run after a stopped synchronization finishes it · declared Spec
  • spec:consumers.obsidian-vault.iteration: A design pass is followed and answered from the vault · declared Spec
  • spec:consumers.obsidian-vault.notes: Every Spec, Pack, code unit and keyed term is a note, and every relation is a link · declared Spec
  • spec:consumers.obsidian-vault.person-notes-survive: A regeneration removes a note that left the graph and keeps a person's notes · declared Spec
  • spec:consumers.obsidian-vault.unknown-note-refused: A note a person wrote inside the generated folder refuses the regeneration · declared Spec
  • spec:consumers.obsidian-vault.vault-in-build-path-refused: A vault directory the build removes refuses the run and keeps a person's notes · declared Spec
  • spec:decisions.vault-owned-folder: The vault's generator owns one folder and synchronizes it · declared Spec
  • spec:consumers.obsidian-vault.bases.views-match-sources: Each standing view selects what its source selects · declared Spec
  • spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes: A carrier edit under watch rewrites only the notes it changes · declared Spec
  • spec:consumers.obsidian-vault.iteration.watch-source-anchor: An anchor added in a source file under watch rewrites the Spec's note · declared Spec
  • spec:consumers.obsidian-vault.notes.case-collision-refused: Two Specs whose notes differ only in case refuse the run · declared Spec
  • spec:consumers.obsidian-vault.notes.entry-address-link: An entry address in prose opens the entry's block in its Spec's note · declared Spec
  • spec:consumers.obsidian-vault.notes.term-address-link: A keyed Model term gets a note, and its address in prose opens the term's block · declared Spec
  • spec:consumers.obsidian-vault.notes.unresolved-mention-stays: A mention of an entry that does not exist stays as written · declared Spec
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores · declared Spec
  • impl:protocol.vault-bases: serializes standing query predicates over reader properties as Obsidian Bases · anchored code · src/projections/vault/bases.ts:43
  • impl:protocol.vault-command: validates after vault preflight and retains prior notes behind a failed-extraction index · anchored code · src/cli/vault-command.ts:191
  • impl:protocol.vault-notes: renders reader contexts, declared relations and keyed terms as linked notes · anchored code · src/projections/vault/notes.ts:45
  • impl:protocol.vault-sections: renders sections in graph order with keyed entry block ids · anchored code · src/projections/vault/sections.ts:10
  • impl:protocol.vault-sync: guards every owned entry and records transitional hashes before atomic file replacement · anchored code · src/projections/vault/sync.ts:98
  • impl:protocol.vault-text: renders checked mentions and escapes Obsidian syntax outside code spans · anchored code · src/projections/vault/text.ts:30
  • impl:protocol.vault-watch: reuses the coalescing watcher with source suffixes and a synchronous vault cycle · anchored code · src/cli/vault-command.ts:219
  • oracle:protocol.vault: oracle:protocol.vault · anchored oracle binding · test/vault-examples.test.ts:140
  • oracle:protocol.vault-bases: oracle:protocol.vault-bases · anchored oracle binding · test/vault-examples.test.ts:405
  • oracle:protocol.vault-iteration: oracle:protocol.vault-iteration · anchored oracle binding · test/vault-examples.test.ts:549
  • oracle:protocol.vault-notes: oracle:protocol.vault-notes · anchored oracle binding · test/vault-examples.test.ts:291
  • test:protocol.consumers.obsidian-vault.bases.views-match-sources: test:protocol.consumers.obsidian-vault.bases.views-match-sources · anchored test binding · binding present · test/vault-examples.test.ts:747
  • test:protocol.consumers.obsidian-vault.changed-note-refused: test:protocol.consumers.obsidian-vault.changed-note-refused · anchored test binding · binding present · test/vault-examples.test.ts:674
  • test:protocol.consumers.obsidian-vault.failed-extraction-keeps-notes: test:protocol.consumers.obsidian-vault.failed-extraction-keeps-notes · anchored test binding · binding present · test/vault-examples.test.ts:702
  • test:protocol.consumers.obsidian-vault.failed-extraction-recovered: test:protocol.consumers.obsidian-vault.failed-extraction-recovered · anchored test binding · binding present · test/vault-examples.test.ts:712
  • test:protocol.consumers.obsidian-vault.fresh-vault: test:protocol.consumers.obsidian-vault.fresh-vault · anchored test binding · binding present · test/vault-examples.test.ts:653
  • test:protocol.consumers.obsidian-vault.hidden-file-refused: test:protocol.consumers.obsidian-vault.hidden-file-refused · anchored test binding · binding present · test/vault-examples.test.ts:681
  • test:protocol.consumers.obsidian-vault.interrupted-run-recovered: test:protocol.consumers.obsidian-vault.interrupted-run-recovered · anchored test binding · binding present · test/vault-examples.test.ts:695
  • test:protocol.consumers.obsidian-vault.iteration.watch-rewrites-changed-notes: test:protocol.consumers.obsidian-vault.iteration.watch-rewrites-changed-notes · anchored test binding · binding present · test/vault-examples.test.ts:754
  • test:protocol.consumers.obsidian-vault.iteration.watch-source-anchor: test:protocol.consumers.obsidian-vault.iteration.watch-source-anchor · anchored test binding · binding present · test/vault-examples.test.ts:761
  • test:protocol.consumers.obsidian-vault.notes.case-collision-refused: test:protocol.consumers.obsidian-vault.notes.case-collision-refused · anchored test binding · binding present · test/vault-examples.test.ts:740
  • test:protocol.consumers.obsidian-vault.notes.entry-address-link: test:protocol.consumers.obsidian-vault.notes.entry-address-link · anchored test binding · binding present · test/vault-examples.test.ts:719
  • test:protocol.consumers.obsidian-vault.notes.term-address-link: test:protocol.consumers.obsidian-vault.notes.term-address-link · anchored test binding · binding present · test/vault-examples.test.ts:733
  • test:protocol.consumers.obsidian-vault.notes.unresolved-mention-stays: test:protocol.consumers.obsidian-vault.notes.unresolved-mention-stays · anchored test binding · binding present · test/vault-examples.test.ts:726
  • test:protocol.consumers.obsidian-vault.person-notes-survive: test:protocol.consumers.obsidian-vault.person-notes-survive · anchored test binding · binding present · test/vault-examples.test.ts:660
  • test:protocol.consumers.obsidian-vault.unknown-note-refused: test:protocol.consumers.obsidian-vault.unknown-note-refused · anchored test binding · binding present · test/vault-examples.test.ts:667
  • test:protocol.consumers.obsidian-vault.vault-in-build-path-refused: test:protocol.consumers.obsidian-vault.vault-in-build-path-refused · anchored test binding · binding present · test/vault-examples.test.ts:688
  • test:protocol.vault-priority: test:protocol.vault-priority · anchored test binding · binding present · test/vault.test.ts:53
  • spec:consumers.obsidian-vault refines spec:consumers.projections-model · declared — a projection for a person who iterates on designs in a notes app
  • spec:consumers.obsidian-vault.bases refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.bases.views-match-sources refines spec:consumers.obsidian-vault.bases · declared
  • spec:consumers.obsidian-vault.bases.views-match-sources verifies spec:consumers.obsidian-vault.bases · declared
  • spec:consumers.obsidian-vault.changed-note-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.changed-note-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-recovered refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-recovered verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.fresh-vault refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.fresh-vault verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.hidden-file-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.hidden-file-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.interrupted-run-recovered refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.interrupted-run-recovered verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.iteration refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes refines spec:consumers.obsidian-vault.iteration · declared
  • spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes verifies spec:consumers.obsidian-vault.iteration · declared
  • spec:consumers.obsidian-vault.iteration.watch-source-anchor refines spec:consumers.obsidian-vault.iteration · declared
  • spec:consumers.obsidian-vault.iteration.watch-source-anchor verifies spec:consumers.obsidian-vault.iteration · declared
  • spec:consumers.obsidian-vault.notes refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.notes.case-collision-refused refines spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.case-collision-refused verifies spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.entry-address-link refines spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.entry-address-link verifies spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.term-address-link refines spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.term-address-link verifies spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.unresolved-mention-stays refines spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.notes.unresolved-mention-stays verifies spec:consumers.obsidian-vault.notes · declared
  • spec:consumers.obsidian-vault.person-notes-survive refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.person-notes-survive verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.unknown-note-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.unknown-note-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.vault-in-build-path-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.vault-in-build-path-refused verifies spec:consumers.obsidian-vault · declared
  • spec:decisions.vault-owned-folder refines spec:consumers.obsidian-vault · declared — settles which files the vault's generator owns and how it regenerates them
  • impl:protocol.vault-bases satisfies spec:consumers.obsidian-vault.bases · anchored
  • impl:protocol.vault-command satisfies spec:consumers.obsidian-vault · anchored
  • impl:protocol.vault-command references spec:decisions.vault-owned-folder · anchored
  • impl:protocol.vault-notes satisfies spec:consumers.obsidian-vault.notes · anchored
  • impl:protocol.vault-notes satisfies spec:consumers.obsidian-vault · anchored
  • impl:protocol.vault-notes satisfies spec:consumers.obsidian-vault.bases · anchored
  • impl:protocol.vault-sections satisfies spec:consumers.obsidian-vault.notes · anchored
  • impl:protocol.vault-sync satisfies spec:consumers.obsidian-vault · anchored
  • impl:protocol.vault-sync satisfies spec:consumers.obsidian-vault.iteration · anchored
  • impl:protocol.vault-sync references spec:decisions.vault-owned-folder · anchored
  • impl:protocol.vault-text satisfies spec:consumers.obsidian-vault.notes · anchored
  • impl:protocol.vault-text satisfies spec:consumers.obsidian-vault.iteration · anchored
  • impl:protocol.vault-watch satisfies spec:consumers.obsidian-vault.iteration · anchored
  • oracle:protocol.vault models spec:consumers.obsidian-vault · anchored
  • oracle:protocol.vault-bases models spec:consumers.obsidian-vault.bases · anchored
  • oracle:protocol.vault-iteration models spec:consumers.obsidian-vault.iteration · anchored
  • oracle:protocol.vault-notes models spec:consumers.obsidian-vault.notes · anchored
  • test:protocol.consumers.obsidian-vault.bases.views-match-sources verifies spec:consumers.obsidian-vault.bases.views-match-sources · anchored
  • test:protocol.consumers.obsidian-vault.changed-note-refused verifies spec:consumers.obsidian-vault.changed-note-refused · anchored
  • test:protocol.consumers.obsidian-vault.failed-extraction-keeps-notes verifies spec:consumers.obsidian-vault.failed-extraction-keeps-notes · anchored
  • test:protocol.consumers.obsidian-vault.failed-extraction-recovered verifies spec:consumers.obsidian-vault.failed-extraction-recovered · anchored
  • test:protocol.consumers.obsidian-vault.fresh-vault verifies spec:consumers.obsidian-vault.fresh-vault · anchored
  • test:protocol.consumers.obsidian-vault.hidden-file-refused verifies spec:consumers.obsidian-vault.hidden-file-refused · anchored
  • test:protocol.consumers.obsidian-vault.interrupted-run-recovered verifies spec:consumers.obsidian-vault.interrupted-run-recovered · anchored
  • test:protocol.consumers.obsidian-vault.iteration.watch-rewrites-changed-notes verifies spec:consumers.obsidian-vault.iteration.watch-rewrites-changed-notes · anchored
  • test:protocol.consumers.obsidian-vault.iteration.watch-source-anchor verifies spec:consumers.obsidian-vault.iteration.watch-source-anchor · anchored
  • test:protocol.consumers.obsidian-vault.notes.case-collision-refused verifies spec:consumers.obsidian-vault.notes.case-collision-refused · anchored
  • test:protocol.consumers.obsidian-vault.notes.entry-address-link verifies spec:consumers.obsidian-vault.notes.entry-address-link · anchored
  • test:protocol.consumers.obsidian-vault.notes.term-address-link verifies spec:consumers.obsidian-vault.notes.term-address-link · anchored
  • test:protocol.consumers.obsidian-vault.notes.unresolved-mention-stays verifies spec:consumers.obsidian-vault.notes.unresolved-mention-stays · anchored
  • test:protocol.consumers.obsidian-vault.person-notes-survive verifies spec:consumers.obsidian-vault.person-notes-survive · anchored
  • test:protocol.consumers.obsidian-vault.unknown-note-refused verifies spec:consumers.obsidian-vault.unknown-note-refused · anchored
  • test:protocol.consumers.obsidian-vault.vault-in-build-path-refused verifies spec:consumers.obsidian-vault.vault-in-build-path-refused · anchored
  • test:protocol.vault-priority verifies spec:consumers.obsidian-vault.notes · anchored

Decisions

  • The shipped projections stay frozen

    Re-specifying the shipped Design Review, census, Mermaid, or Gherkin projection is refused. These four projections are the ruled read surfaces over the one graph. A later decision Spec that supersedes this one replaces the ruling whole; a decision Spec that refines this one narrows it for one projection or one rendering and leaves the rest frozen. Either passes the ADR three-part test.

  • The vault's generator owns one folder and synchronizes it

    The vault's generator owns one folder of the vault, sdp/, and keeps there a file list naming every file it wrote with a hash of its content. A .DS_Store file under sdp/ is ignored. A listed .base file a person changed is kept, even when it leaves the generated set, with its recorded content unchanged in the list; the run warns with its path and the words delete it to regenerate and goes on. For every other file, a run refuses, writing and removing nothing, while that folder holds a file the list does not name or one whose content the list does not record. Otherwise it synchronizes the folder: it writes the files whose content changed, leaves the others untouched, and removes the files it wrote that the graph no longer holds. The vault is not one of the projection roots the shared build rewrites: no build removes it, a vault directory where the build writes or removes is refused, and only the vault's synchronization writes it, whether sdp vault or the verb that writes every projection root runs it.

Runtime
  • component:protocol.cli · entry points, shared build and publication · src/cli/build-command.ts:32 · layer edge · context protocol

    Uses

    • component:protocol.adapters · external runtime and source adapters · src/adapters/vitest.ts:23 · layer adapter · context protocol
    • component:protocol.codegen · contracts and registrar output · src/codegen/contracts.ts:633 · layer application · context protocol
    • component:protocol.extract · the carrier and source pipeline · src/extract/index.ts:192 · layer application · context protocol
    • component:protocol.graph · the one graph and its semantic decisions · src/graph/schema.ts:34 · layer domain · context protocol
    • component:protocol.import · canonical conversion with a fidelity boundary · src/import/import.ts:45 · layer application · context protocol
    • component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · context protocol
    • component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol
    • component:protocol.reader · the typed graph contract and body evaluator · src/reader/reader.ts:671 · layer application · context protocol
    • component:protocol.validate · one finding currency and ordered checks · src/validate/validators.ts:72 · layer domain · context protocol
  • component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol

    Uses

    • component:protocol.extract · the carrier and source pipeline · src/extract/index.ts:192 · layer application · context protocol
    • component:protocol.graph · the one graph and its semantic decisions · src/graph/schema.ts:34 · layer domain · context protocol
    • component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · context protocol
    • component:protocol.notation · the leaf step codec · src/notation/slots.ts:261 · layer domain · context protocol
    • component:protocol.reader · the typed graph contract and body evaluator · src/reader/reader.ts:671 · layer application · context protocol
    • component:protocol.validate · one finding currency and ordered checks · src/validate/validators.ts:72 · layer domain · context protocol
  • impl:protocol.vault-command · validates after vault preflight and retains prior notes behind a failed-extraction index · src/cli/vault-command.ts:191

    Component: component:protocol.cli · entry points, shared build and publication · src/cli/build-command.ts:32 · layer edge · context protocol

    Uses

    • component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol
  • impl:protocol.vault-notes · renders reader contexts, declared relations and keyed terms as linked notes · src/projections/vault/notes.ts:45

    Component: component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol

    Uses

    • impl:protocol.reader · typed graph queries, entry adapters and context joins · src/reader/reader.ts:687
  • impl:protocol.vault-sync · guards every owned entry and records transitional hashes before atomic file replacement · src/projections/vault/sync.ts:98

    Component: component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol

    Uses

    None.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:consumers.obsidian-vault

Shown as text: this drawing exceeds the height budget of 2000 pixels after folding.

The diagram as text
  • spec:consumers.obsidian-vault: An Obsidian vault lets a person iterate on designs from the one graph
  • spec:consumers.obsidian-vault.bases: A Pack's members and the standing queries are tables over the reader's values
  • spec:consumers.obsidian-vault.changed-note-refused: A generated note a person changed refuses the regeneration and keeps the edit
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes: A failed extraction keeps the notes and says so on the index
  • spec:consumers.obsidian-vault.failed-extraction-recovered: The run after a failed extraction passes the guard and restores the index
  • spec:consumers.obsidian-vault.fresh-vault: A fresh vault is written the same way twice and every link resolves
  • spec:consumers.obsidian-vault.hidden-file-refused: A person's file under a temporary's name refuses the regeneration
  • spec:consumers.obsidian-vault.interrupted-run-recovered: A run after a stopped synchronization finishes it
  • spec:consumers.obsidian-vault.iteration: A design pass is followed and answered from the vault
  • spec:consumers.obsidian-vault.notes: Every Spec, Pack, code unit and keyed term is a note, and every relation is a link
  • spec:consumers.obsidian-vault.person-notes-survive: A regeneration removes a note that left the graph and keeps a person's notes
  • spec:consumers.obsidian-vault.unknown-note-refused: A note a person wrote inside the generated folder refuses the regeneration
  • spec:consumers.obsidian-vault.vault-in-build-path-refused: A vault directory the build removes refuses the run and keeps a person's notes
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores
  • spec:consumers.reader: The reader bridges agent entry points to composable graph context
  • spec:consumers.vault-on-ramp: The vault skill teaches agents to keep an Obsidian vault of the graph
  • spec:consumers.wholesale-view-rewrite: Every view run rewrites the view wholesale
  • spec:decisions.shipped-projections-frozen: The shipped projections stay frozen
  • spec:decisions.vault-owned-folder: The vault's generator owns one folder and synchronizes it
  • spec:extraction.determinism: Committed source derives byte-identical output
  • spec:consumers.obsidian-vault constrainedBy spec:extraction.determinism · declared
  • spec:consumers.obsidian-vault decidedBy spec:decisions.shipped-projections-frozen · declared — the vault is a new root, since the four shipped projections stay frozen
  • spec:consumers.obsidian-vault decidedBy spec:decisions.vault-owned-folder · declared — the generator owns one folder of the vault and synchronizes it instead of rewriting a root
  • spec:consumers.obsidian-vault dependsOn spec:consumers.reader · declared — the generator reads the graph through the reader's joined values
  • spec:consumers.obsidian-vault dependsOn spec:consumers.wholesale-view-rewrite · declared — the shared build's invalidation removes the five projection roots and never the vault
  • spec:consumers.obsidian-vault refines spec:consumers.projections-model · declared — a projection for a person who iterates on designs in a notes app
  • spec:consumers.vault-on-ramp dependsOn spec:consumers.obsidian-vault · declared — the skill teaches the verb, where the vault may lie and the guard's refusals
  • spec:consumers.obsidian-vault.bases refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.changed-note-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-recovered refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.fresh-vault refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.hidden-file-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.interrupted-run-recovered refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.iteration refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.notes refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.person-notes-survive refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.unknown-note-refused refines spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.vault-in-build-path-refused refines spec:consumers.obsidian-vault · declared
  • spec:decisions.vault-owned-folder refines spec:consumers.obsidian-vault · declared — settles which files the vault's generator owns and how it regenerates them
  • spec:consumers.obsidian-vault.changed-note-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-keeps-notes verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.failed-extraction-recovered verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.fresh-vault verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.hidden-file-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.interrupted-run-recovered verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.person-notes-survive verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.unknown-note-refused verifies spec:consumers.obsidian-vault · declared
  • spec:consumers.obsidian-vault.vault-in-build-path-refused verifies spec:consumers.obsidian-vault · declared

Rests on

Rested on by

Distance 1

Distance 2

Relations

Relation and directionOther endClaimReasonExercised keys
constrainedBy · outgoingCommitted source derives byte-identical output
spec:extraction.determinism · resolves
declaredNoneNone
decidedBy · outgoingThe shipped projections stay frozen
spec:decisions.shipped-projections-frozen · resolves
declaredthe vault is a new root, since the four shipped projections stay frozenNone
decidedBy · outgoingThe vault's generator owns one folder and synchronizes it
spec:decisions.vault-owned-folder · resolves
declaredthe generator owns one folder of the vault and synchronizes it instead of rewriting a rootNone
dependsOn · outgoingThe reader bridges agent entry points to composable graph context
spec:consumers.reader · resolves
declaredthe generator reads the graph through the reader's joined valuesNone
dependsOn · outgoingEvery view run rewrites the view wholesale
spec:consumers.wholesale-view-rewrite · resolves
declaredthe shared build's invalidation removes the five projection roots and never the vaultNone
refines · outgoingProjections fan out from one graph without becoming truth stores
spec:consumers.projections-model · resolves
declareda projection for a person who iterates on designs in a notes appNone
dependsOn · incomingThe vault skill teaches agents to keep an Obsidian vault of the graph
spec:consumers.vault-on-ramp · resolves
declaredthe skill teaches the verb, where the vault may lie and the guard's refusalsNone
refines · incomingA Pack's members and the standing queries are tables over the reader's values
spec:consumers.obsidian-vault.bases · resolves
declaredNoneNone
refines · incomingA generated note a person changed refuses the regeneration and keeps the edit
spec:consumers.obsidian-vault.changed-note-refused · resolves
declaredNoneNone
refines · incomingA failed extraction keeps the notes and says so on the index
spec:consumers.obsidian-vault.failed-extraction-keeps-notes · resolves
declaredNoneNone
refines · incomingThe run after a failed extraction passes the guard and restores the index
spec:consumers.obsidian-vault.failed-extraction-recovered · resolves
declaredNoneNone
refines · incomingA fresh vault is written the same way twice and every link resolves
spec:consumers.obsidian-vault.fresh-vault · resolves
declaredNoneNone
refines · incomingA person's file under a temporary's name refuses the regeneration
spec:consumers.obsidian-vault.hidden-file-refused · resolves
declaredNoneNone
refines · incomingA run after a stopped synchronization finishes it
spec:consumers.obsidian-vault.interrupted-run-recovered · resolves
declaredNoneNone
refines · incomingA design pass is followed and answered from the vault
spec:consumers.obsidian-vault.iteration · resolves
declaredNoneNone
refines · incomingEvery Spec, Pack, code unit and keyed term is a note, and every relation is a link
spec:consumers.obsidian-vault.notes · resolves
declaredNoneNone
refines · incomingA regeneration removes a note that left the graph and keeps a person's notes
spec:consumers.obsidian-vault.person-notes-survive · resolves
declaredNoneNone
refines · incomingA note a person wrote inside the generated folder refuses the regeneration
spec:consumers.obsidian-vault.unknown-note-refused · resolves
declaredNoneNone
refines · incomingA vault directory the build removes refuses the run and keeps a person's notes
spec:consumers.obsidian-vault.vault-in-build-path-refused · resolves
declaredNoneNone
refines · incomingThe vault's generator owns one folder and synchronizes it
spec:decisions.vault-owned-folder · resolves
declaredsettles which files the vault's generator owns and how it regenerates themNone
verifies · incomingA generated note a person changed refuses the regeneration and keeps the edit
spec:consumers.obsidian-vault.changed-note-refused · resolves
declaredNoneguard
verifies · incomingA failed extraction keeps the notes and says so on the index
spec:consumers.obsidian-vault.failed-extraction-keeps-notes · resolves
declaredNonefailedExtraction
verifies · incomingThe run after a failed extraction passes the guard and restores the index
spec:consumers.obsidian-vault.failed-extraction-recovered · resolves
declaredNonefailedExtraction, guard
verifies · incomingA fresh vault is written the same way twice and every link resolves
spec:consumers.obsidian-vault.fresh-vault · resolves
declaredNoneverb, ownedFolder, deterministic, linksResolve
verifies · incomingA person's file under a temporary's name refuses the regeneration
spec:consumers.obsidian-vault.hidden-file-refused · resolves
declaredNoneguard
verifies · incomingA run after a stopped synchronization finishes it
spec:consumers.obsidian-vault.interrupted-run-recovered · resolves
declaredNoneinterruptedRun, synchronize
verifies · incomingA regeneration removes a note that left the graph and keeps a person's notes
spec:consumers.obsidian-vault.person-notes-survive · resolves
declaredNoneownedFolder, synchronize
verifies · incomingA note a person wrote inside the generated folder refuses the regeneration
spec:consumers.obsidian-vault.unknown-note-refused · resolves
declaredNoneguard
verifies · incomingA vault directory the build removes refuses the run and keeps a person's notes
spec:consumers.obsidian-vault.vault-in-build-path-refused · resolves
declaredNoneapartFromBuild, ownedFolder

Bindings

Implementation binding: present

  • impl:protocol.vault-command · validates after vault preflight and retains prior notes behind a failed-extraction index · src/cli/vault-command.ts:191 · role publisher · component component:protocol.cli · layer edge · context protocol
  • impl:protocol.vault-notes · renders reader contexts, declared relations and keyed terms as linked notes · src/projections/vault/notes.ts:45 · role projection · component component:protocol.projections · layer edge · context protocol
  • impl:protocol.vault-sync · guards every owned entry and records transitional hashes before atomic file replacement · src/projections/vault/sync.ts:98 · role publisher · component component:protocol.projections · layer edge · context protocol

Design reference: none

Verifier binding: present

Expected-outcome oracle: present

  • oracle:protocol.vault · test/vault-examples.test.ts:140

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.