test(okf): consume nav-golden fixtures + thin STEG 0 corpus gate

Consume commons' nav-golden fixture class byte-exact from
portfolio-optimiser-commons @ b641741 (nav-golden-hierarchy positive +
nav-golden-escape boundary) into test/nav-golden-corpus/ and wire a thin
gate scripts/check-nav-golden.mjs (+ .test.mjs, 10 tests).

Scope (operator decision, thin): the catalog is the convention owner and
holds no consumer (null konsument), and the shared retrieval skill is
deferred (spec.md §10 Stage 3). So the gate does NOT run a read-context
navigator and does NOT assert the goldens byte-exact. It asserts only what
the catalog owns: each fixture bundle/ is a conformant OKF bundle under the
existing §3 checkBundle (every concept typed + root okf_version), and the
committed expected-read-context.md golden is present and non-empty. Byte-exact
navigator conformance stays a consumer concern (okr STEG 4 / Stage 3 skill).

Fixtures pinned byte-exact (.gitattributes -text); manifest carries source
repo+SHA per the vendoring-provenance rule. Naming: the stale "§5 pts 1-5"
read-context label maps to the settled method-spec §3 Step 1 (commons 9801d35);
no open §5 dependency. commons confirmed b641741 complete + goldens derived
from §3 Step 1 (via coord).

Verified: check-nav-golden 10/10; gate CLI exit 0 (2 fixtures PASS); full
suite 73/73 across the six test files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeK9hkxrU9wFPBYGYnSV1V
This commit is contained in:
Kjell Tore Guttormsen 2026-07-24 20:35:44 +02:00
commit 325727523d
20 changed files with 539 additions and 0 deletions

View file

@ -0,0 +1,34 @@
# nav-golden-hierarchy — hierarchical navigation golden
Fixture class **nav-golden** (commons-owned, per the D4 step-0 split): `bundle/` in,
`expected-read-context.md` out. Unlike the ingest-golden class (ingest-spec §11,
extraction-shaped: `manifest.json` → `expected-bundle/`), a nav-golden case is
**bundle → read-context** — the only shape that can express the load-bearing property
"two conformant implementations MUST produce an identical read-context from the same
bundle" (method-spec §3 Step 1).
This **positive** case exercises every decision the Q3 navigation contract pins:
| Decision (method-spec §3 Step 1) | How this bundle exercises it |
|---|---|
| Hierarchy allowed; escape, not depth, forbidden | `a/`, `a/b/` are navigated; nothing escapes |
| Link syntax: leading `/` = bundle root, else relative | `index.md`: relative `overview.md` + root-relative `/a/index.md`; `a/index.md`: relative `b/index.md` + root-relative `/overview.md` |
| Depth-first, first-seen order | render order is `overview` → `doc-a` → `doc-b` |
| Dedup on resolved path | `/overview.md` (from `a/`) resolves to the already-seen `overview.md` → one entry |
| Cycle termination | `a/b/index.md` links back to `/a/index.md` (already seen) → stops |
| One segment per level | each index links only its immediate children |
| Missing `index.md` binds the root alone | `c/` has no `index.md` and nothing links `c/orphan.md`; it is simply unreachable, not an error |
| Verdict exclusion = type check per reached file, recursive | `a/verdict-nested.md` is reached but excluded |
| Flat render regardless of depth | nested concepts render as the same `## {type}: {title}` sections; no level heading; nested index bodies are navigation, not content, and are not rendered |
**Traversal trace:** `index.md` (its body is the leading summary) → `overview.md` →
`/a/index.md` → `a/doc-a.md` → `a/verdict-nested.md` (reached, excluded) → `a/b/index.md`
→ `a/b/doc-b.md` → `/a/index.md` (seen, deduped) → `/overview.md` (seen, deduped).
Unreachable: `c/orphan.md`.
**Serialization** the gate compares against: the root index body verbatim, then each
**non-index** concept as `## {type}: {title}` + blank line + the file body, sections
separated by one blank line, file ending in exactly one trailing newline. The spec does
not pin whitespace beyond this shape; a gate MAY compare byte-exact or after
trailing-whitespace normalization. **Only the root index body is the summary** — nested
index bodies (`a/index.md`, `a/b/index.md`) are navigation and do not appear.