Store extraction evidence#
Publication preparation at 0.1.0-next.0 adds an owner-dispatched
workspace publish workflow.
The packed TypeScript consumer now pins @types/node@24.19.2 alongside
typescript@5.9.3, with install scripts disabled. It optionally exports the
exact tested Store and Conformance tarballs plus their SHA-256 hashes for the
separate OIDC publish job. Conformance includes the root Apache-2.0 license.
This preparation does not
claim npm publication or change the behavioral evidence below.
This package-only change implements adjusted phase 3 steps 1–2. It does not
publish, merge, migrate consumers or apply the deferred owner decisions.
Station source: 2479f38b1ab062e2d9cd6df9f8fa2cdbeee1d136 (origin/main).
Corpus base: 8e82ec4 (origin/main).
Module mapping#
Paths at left are under Station src-server/knowledge-store/.
| Station source | Corpus destination |
|---|---|
adapters/default-store.ts |
packages/store/src/default-store.ts |
adapters/obsidian-store.ts |
packages/store/src/obsidian-store.ts |
adapters/shared/category.ts |
packages/store/src/shared/category.ts |
adapters/shared/freshness.ts |
packages/store/src/shared/freshness.ts |
adapters/shared/frontmatter.ts |
packages/store/src/shared/frontmatter.ts |
adapters/shared/graph-index.ts |
packages/store/src/shared/graph-index.ts |
adapters/shared/identity.ts |
packages/store/src/shared/identity.ts |
adapters/shared/record-schema.ts |
packages/store/src/shared/record-schema.ts |
adapters/shared/wikilinks.ts |
packages/store/src/shared/wikilinks.ts |
adapters/shared/file-transactions.ts |
packages/store/src/shared/file-transactions.ts |
adapters/shared/observation-file.ts |
packages/store/src/shared/observation-file.ts |
errors.ts |
packages/store/src/errors.ts |
adapters/__tests__/contract-suite.ts |
packages/store/test/contract-suite.ts |
adapters/__tests__/default-store.contract.test.ts |
packages/store/test/default-store.contract.test.ts |
adapters/__tests__/obsidian-store.contract.test.ts |
packages/store/test/obsidian-store.contract.test.ts |
adapters/__tests__/file-transactions.test.ts |
packages/store/test/file-transactions.test.ts |
adapters/__tests__/persistence-corruption.test.ts |
packages/store/test/persistence-corruption.test.ts |
adapters/__tests__/wikilinks.test.ts |
packages/store/test/wikilinks.test.ts |
adapters/__tests__/fixtures/knowledge-file-transaction-child.ts |
packages/store/test/fixtures/knowledge-file-transaction-child.ts |
Additional seams: Station packages/contracts/src/knowledge-store.ts record,
evidence, error and adapter interfaces → packages/store/src/contracts.ts;
src-server/knowledge-index/path-safety.ts predicate → src/path-safety.ts;
src-server/utils/is-record.ts predicate → src/is-record.ts. Root registry,
provider and descriptors stay host-owned. services.ts, standalone-lock.ts,
index.ts and observation.ts compose/export the extracted owners.
The Station-only source scan for host document services and vault onboarding
validateRoot tests were not ported: those host owners are outside the package.
The 123 ported behavioral tests were executed, not merely copied; round 1 also restores a derived package-wide raw-fs guard. Logger mocks
become injected diagnostics; transaction fixtures receive an isolated coordination
directory. The Station lock itself is neither copied nor moved.
Conformance interpretation#
The runner executes every public store vector for both profiles; host vectors are a host obligation (see Host vectors). It preserves raw negative results and adjudicates them against the explicit expected-fail list. Expected failures await versioned working-set/history, expiry, compiled-source or any-type proposal implementation; they do not mean full semantic conformance. The list fails on an unexpected pass or wrong failure pattern.
Two existing vector defects were corrected: wikilink body $b was a literal,
not an interpolated reference; the fixture now supplies an explicit target ID.
Expiry clearing removes the field, so its assertion now uses absent: true
instead of requiring a stored null. The runner tests absence rejection.
Storage probes exercise real subprocess concurrency, crash points (prepared, first/final publication, commit marker), traversal (including safe opaque-ID acceptance), physical-ID collisions without indexes/after archival/mismatched frontmatter, symlink refusal, lost/corrupt derived indexes and disk graph integrity. The vault path index is authoritative, so derived-index loss probes preserve it.
Initial extraction evidence (before round 1)#
EXECUTED on macOS, Node 22.22.2 and 24.19.0:
- Package-local TypeScript build succeeds, producing ESM and declarations.
- Store suite: 134/134 tests (123 ported behavior tests, 9 codec regressions, 2 injected-service/observation tests), seven files, on both Node versions.
- Root docs/conformance-runner suite: 138/138 tests on Node 24.
- Built and installed-tarball conformance: all 124 vectors per profile execute; 104 pass and 20 are the explicitly listed deferred decisions, for both default and vault. No unexpected failures or unexpected passes.
- Isolated JS consumer imports all public subpaths, creates/reads both profiles, and proves source and storage deep imports are rejected.
- Isolated TS consumer compiles declarations with strict NodeNext and executes the emitted JS against the installed package with a real create/read.
- Installed packed docs CLI checks this repository without building docs or importing store sources. The installed root has no prepare or build script.
- Packed store SHA-256 on both Node versions:
e492f8678cfa88bab42789bb76ad28f0d5c54af8028579bc1d8a5d6bf9ae49e6. Store source is identical to extraction commitd71564d361e21dd7a96d02c6d0c29b293296534b; executable TS pack fixture is recorded inf964bf1f0eee628f6a1cc3fbca235e2c1a3e7421. - Docs dogfood and committed-range whitespace checks pass.
EXECUTED fault proofs at clean committed baseline
d71564d361e21dd7a96d02c6d0c29b293296534b, Node 24.19.0:
| Injection | Baseline | Injected | Restored |
|---|---|---|---|
| Skip injected lock acquisition in the transaction owner | exit 0 | exit 1: subprocess create/update conflicts, failed storage invariants | exit 0 |
| Restore typed YAML loading and remove legacy-dash repair | exit 0 | exit 1: 1.10, null, true, - fixtures and unknown-text metadata fail |
exit 0 |
Add passing create-roundtrip to expected-fail list |
exit 0 | exit 1: expected-fail vector passed, remove it from the list | exit 0 |
Each mutation was restored byte for byte, rebuilt, and rerun. The fault driver asserts a clean tree before and after, writes separate negative logs, and can be reproduced from a committed worktree with:
npm run proof:faults --workspace=@kontourai/corpus-store -- <output-directory>
The fault driver writes diagnostic receipts to the caller-selected output directory. CI repeats build, contracts and packed qualification on Linux Node 22/24. Local results are distinct from hosted CI. No Windows or mixed-host qualification is inferred from these executions.
Round 1 corrections#
The independent review exposed numeric supersede evidence that the original empty-log byte fixtures missed. The field inventory is: record strings (id/type/title/category/status/timestamps/expiry/sentinel), tags/aliases, provenance strings and source IDs, link target/kind/label, log op/time/agent/note and supersession rationale/new ID; update evidence fields (string array), link added evidence (link array), propose/apply/reject/retire/superseded-by evidence strings; numeric TTL and supersede evidence count. No Station-written boolean record field exists. Unknown extension scalars follow the orchestrator's lossless core-schema rule, with source slices retained for unchanged values.
Round 1 also requires explicit lock/coordination options; adds injected host-home identity rechecks; binds 6 of the 20 expected failures to ref/kind/expected/actual assertions and 14 to specific error patterns, and adjudicates selected vectors; restores held-owner PID/liveness and raw-fs publication guards; removes machine-private paths; and hides the existing internal transaction field from enumeration. Origin main was merged, including #13's dependency and instruction changes. Package/root CI is rerun after this merge. Each correction has a targeted negative control recorded below after execution.
Round 1 executed qualification#
EXECUTED at source revision 9ec0bed on macOS:
- Node 22.22.2 and 24.19.0: package build and 166/166 store tests.
- Node 24.19.0: 231/231 root tests, including main #13's builtOn gate tests and the new public-document path guard.
- Packed JS and strict TS consumers execute successfully on both Node versions. TS also rejects missing lock/directory configurations at compile time.
- All 124 vectors per default/vault profile execute against installed tarballs: 104 pass, 20 expected failures, with no unexpected result. Six are assertion-bound and fourteen are specific-error-pattern-bound. Selected vectors receive the same adjudication.
- Packed docs CLI executes without a build, including the merged builtOn gate.
- Both Node versions produce store tarball SHA-256
1db0ff939d4caf08adda171d920d17573724e0f26a60df3455e4f57806ce335f.
EXECUTED negative controls at clean baseline
862b17c6c5fd53f83790f503dc19e3d1eaec4dd6, Node 24.19.0:
| Control | Injected regression | Baseline | Injected | Restored |
|---|---|---|---|---|
| B1 | Leave numeric supersede count as text | 0 | 1 | 0 |
| B2-read | Disable lossless unknown-scalar conversion | 0 | 1 | 0 |
| B2-source | Drop retained unknown-field source ranges | 0 | 1 | 0 |
| B3 | Restore silent lock/directory fallback | 0 | 1 | 0 |
| M1 | Skip injected writer/observation home-identity rechecks | 0 | 1 | 0 |
| M2-assertion | Excuse a different assertion in a listed vector | 0 | 1 | 0 |
| M2-selected | Bypass expected-fail adjudication for selected vectors | 0 | 1 | 0 |
| M3-publication | Import raw file publication into an adapter | 0 | 1 | 0 |
| M3-owner | Publish parent PID instead of actual lock holder PID | 0 | 1 | 0 |
| L1 | Restore a machine-private path to the public document | 0 | 1 | 0 |
| L2 | Make the transaction field enumerable again | 0 | 1 | 0 |
| L3 | Restore unconditional quoted numeric-field coercion | 0 | 1 | 0 |
| B3-types | Make required coordination options optional in declarations | 0 | 1 | 0 |
| B4-packed-dependency | Drop the merged npm-package-arg dependency from the packed docs CLI | 0 | 1 | 0 |
The derived publication guard was separately proved with the same 0 → 1 → 0
sequence at 9ec0bed. Each completed control restored the exact original bytes
and confirmed a clean worktree. The first quoted-TTL control was corrected
because removing only the plain-style condition still left core-schema typing
intact; the completed control actually restores the original unconditional
coercion. That diagnostic did not count as successful fault proof.
npm run proof:round1 --workspace=@kontourai/corpus-store -- <output-directory>
These are local execution receipts. Hosted CI status is reported on the PR for its exact pushed head; local results do not stand in for hosted checks.
Plan disagreements and limits#
REASONED: the brief's extraction-only scope wins over design suggestions for public storage, capabilities, moving the Station lock, dual vault formats and immediate semantic convergence. Search vectors remained an expected host-level failure because retrieval is deliberately outside this library; they were later reclassified as host vectors. No ranked search, hosted CI, mixed Station/standalone lock or deployment qualification is inferred from the package tests.
Round 2 policy and data preservation#
The current rule replaces round 1's broad source-slice preservation. Only an unchanged top-level, single-line plain scalar whose core conversion is lossy may retain its original token, and that token must be valid YAML that re-parses as the same failsafe string. Collections, block scalars, nested or multiline data use the normal serializer. Invalid bare-dash text is always quoted. This avoids copying missing line separators or obsolete indentation into new YAML.
The package preserves parsed values and following keys across updates for
Station-written | and |-, externally authored > accepted by Station,
Kit-style unindented log sequences, and flow collections. Station's serializer
uses lineWidth: -1 and does not emit folded >; the folded fixture therefore
starts with a Station-written envelope and deliberately models an external edit.
coordinationIdentity is now mandatory for stores and exact observation; its
factory/recheck shape is validated before directory preparation. Fixed-directory
standalone hosts must also opt in to an explicit identity.
Round 2 bound 6 of its 20 expected failures to ref/kind/expected/actual, and 14 to specific error patterns. This is a diagnostic extraction with deferred owner decisions, not a claim of complete semantic conformance. The live counts are in Owner decisions after extraction.
EXECUTED on macOS at 1ff4b262163edf3edf4c5481302300ce41f0733f:
- Node 22.22.2 and 24.19.0: 181/181 store tests, including both-profile block, sequence, following-key, standard-YAML and required-identity regressions.
- Node 24.19.0: 233/233 root tests.
- Strict TS/executable JS tarball consumers and build-free packed docs CLI pass on both Node versions. All 124 vectors per profile execute with 104 passes, 6 assertion-bound expected failures and 14 error-pattern-bound expected failures.
- Both Node versions produce store tarball SHA-256
5fd72a61f7dc1940e2632dc29f1c9b6f0540660d56b8eb6fa40456df36b966af.
EXECUTED Station byte comparison: the driver reads immutable Station revision
2479f38b1ab062e2d9cd6df9f8fa2cdbeee1d136 into an isolated snapshot and runs
its actual adapters and lock, using the installed dependency set from the
supplied Station checkout. Separate Corpus stores use the same fixed mutation
clock, explicit IDs and inputs. No Station checkout or user store is modified.
The stores contain |, |-, externally authored >, Kit-style log sequences,
flow collections and counterparts without lossy numeric text. Updates append
following expiry/tags keys and move vault titles; both owners must re-read their
own prior extension values identically.
The complete comparison receipt records per-file hashes and results. Of 18 files, 14 are byte-identical. Only these four differ:
| Profile | File | Reason |
|---|---|---|
| Default | records/kit-sequence.md |
Corpus preserves nested 1.10 as a string; Station reads/writes numeric 1.1. |
| Default | records/flow.md |
The same decided unknown-text divergence within a flow collection. |
| Vault | engineering/kit-sequence-updated.md |
The same nested-sequence text divergence. |
| Vault | engineering/flow-updated.md |
The same flow-collection text divergence. |
Block-scalar records, lossless sequence/flow counterparts and all emitted indexes match byte for byte. Folded input normalizes through each owner's serializer identically. No swallowed keys, malformed records or unexpected file differences remain in this comparison.
EXECUTED reverted-fix controls at clean baseline
627217c3629c5021ef9b6c3743b5f7a46ab2cda5, Node 24.19.0:
| Control | Injected regression | Baseline | Injected | Restored |
|---|---|---|---|---|
| N1 | Restore c0c44c7 codec: EOF block absorbs newly appended tags | 0 | 1 | 0 |
| N2 | Restore c0c44c7 codec: Kit-indented items become sibling log entries | 0 | 1 | 0 |
| N3 | Restore c0c44c7 codec: bare dash is published as invalid YAML | 0 | 1 | 0 |
| N4 | Restore silent missing-identity fallback | 0 | 1 | 0 |
| N4 types | Make coordination identity optional in declarations | 0 | 1 | 0 |
| N5 | Misstate all 20 failures as assertion-bound | 0 | 1 | 0 |
Each source mutation was restored exactly and the worktree checked clean. The first N1 probe already had a following key and did not exercise the EOF-to-new-key transition; the retained regression adds keys during update. The first N2 fixture produced a related indentation parse error; the retained fixture reproduces the reported sibling-entry/schema failure. Neither incomplete diagnostic counted as completed fault proof.
npm run proof:round2 --workspace=@kontourai/corpus-store -- <output-directory>
npm run compare:station --workspace=@kontourai/corpus-store -- <station-repository> <report.json>
Hosted CI is tracked against the exact pushed head on the PR. These local receipts do not claim hosted CI success.
Owner decisions after extraction#
Each later change that implements an owner decision removes its vectors from the expected-fail list in the same commit; the runner fails a listed vector that starts passing. The list is empty when every decision has shipped.
The expected-fail list is empty.
Host vectors#
A vector with "scope": "host" is a host obligation: it needs behavior that
this library deliberately does not implement. search-working-set is the only
one. It retires a record and expects search to omit it unless
includeHistory: true is passed, and search belongs to the host that mounts the
store. The package therefore cannot pass it, and listing it as an expected
failure could never empty the list. Corpus Conformance exports storeVectors
and hostVectors; this package's runner executes only storeVectors (123 per
profile), refuses a selected host vector, and fails if the expected-fail list
names one. A host such as Station runs hostVectors through its own search
route and must pass them.
EXECUTED on macOS, Node 24.19.0: 123 store vectors per profile, 104 passing and 19 expected failures, with no unexpected result, for both default and vault.
Proposals may target any record type#
propose, apply and reject no longer require a concept target. Only the
type guard was removed; the evidence, existence and proposes-link checks
remain. The 12 propose-target-*, apply-target-* and reject-target-*
vectors now pass on both profiles and leave the list. The contract suite's
three wrong-type rejection tests are replaced by an every-type acceptance test
and a test that the kept checks still reject.
EXECUTED on macOS, Node 24.19.0: 123 store vectors per profile, 116 passing and 7 expected failures, with no unexpected result, for both default and vault.
Lists return the working set#
One predicate (shared/lifecycle.ts) decides history for both profiles:
retired status, a superseded-by mutation-log entry, or an archived vault
note. Lists exclude historical records unless includeHistory: true; the
vault's history walks archived notes. includeRetired is translated to
includeHistory. Before this change the default profile listed superseded
records and the vault excluded archived notes even from includeRetired, so
the two profiles disagreed; the contract suite's header exclusion for
supersede listing is removed and the shared suite now asserts the working set
and history for both. retire-working-set, supersede-listing and
read-retired leave the list. The adjudication tests used live list entries as
examples, so they now adjudicate a fixture list instead (the selected-vector
CLI test passes it through CORPUS_EXPECTED_FAILS), keeping their force once
the real list is empty.
EXECUTED on macOS, Node 24.19.0: 123 store vectors per profile, 119 passing and 4 expected failures, with no unexpected result, for both default and vault.
One expiry rule and an expiry-only stale filter#
shared/freshness.ts computes one expiry for both profiles: explicit
expires_at, otherwise created_at + ttl_seconds, otherwise none. List
options {stale, now} filter on it, with now defaulting to the injected
clock. test/expiry.test.ts covers both profiles with an injected clock: the
TTL survives a later update, expiry is inclusive, expires_at wins and
clearing it restores the TTL, records without expiry never go stale, the
filter composes with the working set, and malformed options are refused. The
public ttl-creation-anchor vector creates without updating, so it cannot
detect a TTL that resets on update; the update case lives in the expiry test.
read-stale-filter, ttl-creation-anchor and expiry-precedence leave the
list.
EXECUTED on macOS, Node 24.19.0: 123 store vectors per profile, 122 passing and 1 expected failure, with no unexpected result, for both default and vault.
A compiled record must link a raw source#
create of a compiled record requires a source link to an existing raw
record (shared/compiled-source.ts), checked before any write in both
profiles. Provenance IDs, related links and wikilinks do not satisfy it.
Fixtures that created source-less compiled records now create a raw source
first, including the conformance harness's physical-id-archived probe. A
Station-written source-less compiled record is kept readable by writing its
exact legacy bytes (fixtures/station/contract-record.md) into a default store
rather than creating it. compiled-without-source leaves the list, which is
now {}.
EXECUTED on macOS, Node 24.19.0: 123 store vectors per profile, all 123 passing, with no expected failure and no unexpected result, for both default and vault.
Canonical vault index and offline migration#
The vault adapter selects its path index on every operation: canonical
path-index.json, or the Knowledge Kit's .graph-index.json (kit-v1), which
opens read-only. Both at once is refused, and a root that holds store data
without either index is refused instead of being read as empty. The
corpus-store migrate bin converts a kit-v1 vault through one journaled
transaction of the store's transaction engine, which gains readBytes and
writeBytes so the saved index keeps its exact bytes. It writes a hashed
manifest and the original index, verifies every other file's sha256 and reads
every record through the vault adapter, and reverses only while the bytes still
match.
Two committed fixtures were written by the Knowledge Kit's own adapters (see
packages/store/test/fixtures/README.md): a vault with every record type,
archive, retirement, alias and expiry, and a hybrid root that a Kit default
store and a Kit vault share. test/vault-index.test.ts copies them and covers
read-only kit-v1 access, refusal of an occupied unindexed vault, plain-note
and empty roots, the dual-index conflict, conversion of both fixtures with a
byte-hash check before and after and identical reads through the package,
reversal and its refusal after an edit or a move, refusal of inexact mappings,
and CLI usage errors. Pack verification runs the installed bin on a copy of
the Kit vault.
The phase plan asked for the conformance vectors on "both vault formats".
kit-v1 is read-only by design, and the vectors write, so they run against the
canonical format only; kit-v1 is covered by the read and conversion tests
above.
Named vault dimensions#
The optional vault constructor dimensions setting names category segments after
the domain. For example, ['territory', 'customer', 'initiative'] projects
sales.east.acme.renewal into those three frontmatter fields. Every write
recomputes the configured fields; shortening the category removes fields whose
segments are absent. Configured dimension fields stay out of returned record
envelopes. Omitting the setting preserves unknown frontmatter without projecting
named dimensions.
Names must be unique identifiers and cannot replace identity, evidence or bookkeeping fields. Invalid settings refuse before creating the store directory. The vault contract tests read actual on-disk frontmatter after create and category changes, check obsolete-field removal and the returned record, and exercise reserved and invalid names. Existing files change only when an operation writes them; setting this option alone does not rewrite the vault.