docs(phase-3): the four open questions are answered; required set is eight
The proving consumer answered Q1-Q4 on 2026-07-25, each measured or read out of their code that day. No row in the mapping moved. Three of the four answers carry a sharper reason than this table drafted, and one reverses a correction this table made a day earlier. Q2 is the substantive change. This table had corrected the plan's eight-key required set down to four, against the consumer's REQUIRED_KEYS constant. Their operator ratified eight: the constant is their emit-path minimum, not their contract, and all eight keys are present on 522/522 of their documents. STRICT_V1 is therefore deliberately stricter than the consumer's own validator; if they ever ship a class lacking source_sha they bump the profile rather than bend the bundle, and they say so before shipping it. The eleven-key allowlist is unchanged and still a different thing from the required set: version/date are layer-specific and summary is the one nullable key, so all three stay optional-when-absent. Q1, Q3 and Q4 keep their Consumer/Guard assignments, now with the reasons that justify them: gate 5 escalates on run state (disposition == verbatim) rather than document class, so a profile structurally cannot see it; gate 4's changelog rule is a vendor slug list that is not scoped to concepts/ at all; gates 19 and 22 need external state, one of them by construction never blocking. Assumption C2 is confirmed at 25 gates and the index shape is confirmed non-negotiable. Both reviews - the consumer's and the operator's - are now in, so the port may begin. The Guard column is unaffected by all of it.
This commit is contained in:
parent
6f42c10608
commit
cf1f7984c4
2 changed files with 108 additions and 57 deletions
|
|
@ -1,17 +1,19 @@
|
|||
# Phase 3 split table — the proving consumer's 25 gates
|
||||
|
||||
Status: **drafted and independently corroborated; operator review outstanding.**
|
||||
Phase 3's plan requires this mapping before any porting. Nothing here is
|
||||
implemented yet, and nothing in the consumer's repository is modified by this
|
||||
repository.
|
||||
Status: **reviewed by the proving consumer and by the operator (2026-07-25);
|
||||
the port may begin.** Phase 3's plan requires this mapping before any porting.
|
||||
Nothing in the consumer's repository is modified by this repository.
|
||||
|
||||
The proving consumer (`claude-code-llm-wiki`) answered the same questions from
|
||||
their own code on 2026-07-25, crossing with this draft rather than responding
|
||||
to it. Two independent readings of the same source agreed on every substantive
|
||||
point: four required keys, three reserved basenames, `summary` as the only
|
||||
nullable key, the index structure, and the closed sets. Their answer added four
|
||||
refinements, each folded in below and marked. Their column assignments match
|
||||
this table's, with one wording difference resolved in gate 5's row.
|
||||
point: the required-key set as their code spells it, three reserved basenames,
|
||||
`summary` as the only nullable key, the index structure, and the closed sets.
|
||||
Their answer added four refinements, each folded in below and marked. Their
|
||||
column assignments match this table's, with one wording difference resolved in
|
||||
gate 5's row. A later round answered the four open questions outright — see
|
||||
"Answered by the proving consumer" — and moved the required set from what their
|
||||
code enforces to what their operator ratified.
|
||||
|
||||
Read from the consumer's working tree on 2026-07-25:
|
||||
`tools/wiki_ingest/validate.py` and `tools/wiki_ingest/bundle.py` at commit
|
||||
|
|
@ -103,19 +105,37 @@ considered.
|
|||
## What STRICT_V1 must express
|
||||
|
||||
The phase-3 plan sketched this profile before the validator was read. Two of
|
||||
its claims do not survive contact and are corrected here; the plan file is
|
||||
corrected in the same commit.
|
||||
its claims did not survive contact and are corrected here; the plan file
|
||||
carries the same corrections. One of the two was then corrected a second time
|
||||
by the consumer's operator — the sequence is recorded rather than smoothed
|
||||
over, because the end state is deliberately neither what the plan drafted nor
|
||||
what their code says.
|
||||
|
||||
1. **Required keys are four, not eight.** `REQUIRED_KEYS = (type, title,
|
||||
description, timestamp)`. The eight-key list in the plan was a truncation of
|
||||
`FRONTMATTER_KEY_ORDER`, which is a different thing: an eleven-key
|
||||
*allowlist* that doubles as canonical emission order — `type`, `title`,
|
||||
`description`, `timestamp`, `layer`, `source_tier`, `source_url`,
|
||||
`source_sha`, `version`, `date`, `summary`. So `frontmatter_schema` needs
|
||||
three fields, not one: required, allowlisted, and ordered. The non-required
|
||||
keys are validated against closed sets **when present**, not required to be
|
||||
present — a profile that requires all of them rejects pages the consumer
|
||||
ships today.
|
||||
1. **Required keys are eight — settled by the consumer, not read off their
|
||||
code.** Their `REQUIRED_KEYS` constant is four (`type`, `title`,
|
||||
`description`, `timestamp`), and this table first corrected the plan down to
|
||||
it. Asked whether four was the contract or an artefact of unfinished triage
|
||||
(Q2), they measured the whole bundle and their operator ratified eight:
|
||||
`type`, `title`, `description`, `timestamp`, `layer`, `source_tier`,
|
||||
`source_url`, `source_sha`, present on 522/522 documents. The four-key
|
||||
constant is their *emit-path* minimum, not their contract.
|
||||
|
||||
Two consequences are carried deliberately:
|
||||
|
||||
- **STRICT_V1 is stricter than the consumer's own validator.** Should they
|
||||
ever ship a document class that legitimately lacks `source_sha` — a
|
||||
hand-written Guide, say — this profile rejects what their validator
|
||||
passes. The agreed resolution is to bump the profile, not to bend the
|
||||
bundle, and they send the requirement before shipping such a class rather
|
||||
than after.
|
||||
- **The allowlist remains a different thing from the required set.**
|
||||
`FRONTMATTER_KEY_ORDER` is eleven keys — the eight above plus `version`,
|
||||
`date`, `summary` — and doubles as canonical emission order. `version` and
|
||||
`date` are layer-specific (present on 351/522) and `summary` on 349/522,
|
||||
so all three stay optional-when-absent and are validated against closed
|
||||
sets **only when present**; a profile that required them would reject
|
||||
pages the consumer ships today. So `frontmatter_schema` needs three
|
||||
fields, not one: required, allowlisted, and ordered.
|
||||
2. **Three reserved basenames, not two.** `index.md`, `log.md`, and
|
||||
`README.md`. README is bundle documentation, reserved like `index.md`: it
|
||||
carries no frontmatter, is skipped by the per-document gates, is never
|
||||
|
|
@ -184,28 +204,52 @@ profile the observable output must stay byte-identical (assumption C1, proven
|
|||
by the golden suite), so `index_policy` has to express both shapes rather than
|
||||
generalising one into the other.
|
||||
|
||||
## Open questions for the proving consumer
|
||||
## Answered by the proving consumer
|
||||
|
||||
- **Q1.** Gate 5 escalates the injection scan from WARN to ERROR on a
|
||||
`verbatim` disposition, because no LLM rewrite layer sits in front of the
|
||||
published text. That is a policy tier chosen per document, over a guard
|
||||
verdict. Should the profile be able to express "this document class gets a
|
||||
stricter gate tier", or does that stay entirely in the consumer's hands?
|
||||
This repository's position is the latter — the profile describes structure,
|
||||
not security tiers — but it is the consumer's call, since it decides where
|
||||
their gate lives after the port.
|
||||
- **Q2.** Is the four-key required set final, or is it four only because seed
|
||||
triage has not finished? `layer`, `source_tier`, `source_url` and
|
||||
`source_sha` are allowlisted and, in practice, present on every generated
|
||||
document — if they are meant to be required, STRICT_V1 should require them.
|
||||
- **Q3.** Gate 4's second half asserts that no `concepts/` page may derive from
|
||||
the changelog page. That reads as a layer-provenance rule, which is Profile
|
||||
territory, but it is expressed as a URL predicate over one specific upstream,
|
||||
which is Consumer territory. Mapped to Consumer here. Correct?
|
||||
- **Q4.** Gates 19 and 22 are mapped to Consumer because they need state this
|
||||
library never collects (link liveness, page hashes). If either is expected to
|
||||
move, the profile would have to grow a notion of external state, which is a
|
||||
larger change than phase 3 scopes.
|
||||
All four questions were answered on 2026-07-25, each measured or read out of
|
||||
their code that day rather than recalled. **No row in the mapping moved**; the
|
||||
reasons under three of the four are sharper than the ones this table drafted.
|
||||
|
||||
- **Q1 — gate tiers stay Consumer/Guard.** This repository's position was
|
||||
accepted, and the escalation turns out not to be a document-class rule at
|
||||
all: gate 5 escalates WARN to ERROR when the per-version *record* carries
|
||||
`disposition == "verbatim"` (`validate.py:528-540`), i.e. when the LLM
|
||||
rewrite layer was skipped and the input lint is the only filter left. That is
|
||||
run state, not bundle shape — a profile could not see it even if it wanted
|
||||
to. A profile describes structure; it does not set security tiers.
|
||||
- **Q2 — eight, ratified by their operator.** Folded into "What STRICT_V1 must
|
||||
express" above, together with the consequence both sides accepted.
|
||||
- **Q3 — Consumer, for a sharper reason than "URL predicate".** The check is
|
||||
`is_changelog_url()` (`docs_corpus.py:181-184`): it lowercases the last URL
|
||||
segment and tests membership in `("changelog", "release-notes")` — a
|
||||
vendor-specific slug list for one upstream. Two details worth carrying: the
|
||||
gate is **not** scoped to `concepts/` at all (it walks every non-reserved
|
||||
document across both layers, `validate.py:480-492`, despite a rationale
|
||||
string that says "concepts layer"), and it spares their own release pages
|
||||
only because their `source_url` ends in `CHANGELOG.md`, whose last segment is
|
||||
`changelog.md` rather than `changelog`. That is a coupling, not a design. A
|
||||
structural profile could express "layer X may not carry source_tier Y"; it
|
||||
could not express this.
|
||||
- **Q4 — both stay Consumer.** Gate 19 sits in their `NEVER_BLOCKING_GATES`
|
||||
(`validate.py:86-88`) by construction, precisely because liveness is a flake
|
||||
magnet: a profile that owned it would inherit a gate that can never block.
|
||||
Gate 22 is their provenance model, the one commons decision D1 settled in
|
||||
their favour, and it is why the ordered-prefix emission matters. The profile
|
||||
gets no notion of external state from either.
|
||||
|
||||
Two confirmations arrived unasked and are recorded because later work leans on
|
||||
them:
|
||||
|
||||
- **Assumption C2 holds.** `validate.py` is still the single entrypoint for
|
||||
every per-run gate, and the registry at lines 50-78 is the whole set — 5
|
||||
`INGEST_*`, 7 `LLM_*`, 13 `BUNDLE_*` = 25. A change reaches this repository
|
||||
as a message before it can be read out of their code.
|
||||
- **The index shape is not negotiable downward**, which makes "express both
|
||||
shapes rather than generalise one into the other" the right call: one index
|
||||
per directory, exactly one `# ` heading, entries `* [Title](link) -
|
||||
description` and nothing else, directory and index matching in both
|
||||
directions, and a root index carrying `okf_version` / `bundle_profile` /
|
||||
`okf_spec_commit` frontmatter in that key order.
|
||||
|
||||
## What this table does not do
|
||||
|
||||
|
|
@ -213,3 +257,7 @@ It does not port anything. Per the plan, porting starts only after this
|
|||
mapping is reviewed, and the Guard column must contain no rows implemented in
|
||||
this repository — the phase-2 grep gate over guard functionality still applies
|
||||
unchanged.
|
||||
|
||||
Both reviews are in as of 2026-07-25 — the proving consumer's (above) and the
|
||||
operator's — so the port may begin. The Guard-column constraint is unaffected
|
||||
by either.
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ the golden suite is the regression harness for that claim.
|
|||
- `type_policy`: closed enum vs. open set; the reserved `verdict` exclusion
|
||||
stays unconditional (it is a spec invariant, not profile config).
|
||||
- `frontmatter_schema`: three distinct fields, not one — the *required* key
|
||||
set, the *allowlisted* key set, and the canonical emission *order*. The
|
||||
proving consumer keeps four required keys inside an eleven-key allowlist
|
||||
(see `docs/phase-3-split-table.md`), so a single ordered required/optional
|
||||
pair cannot express it. Also carries nullable keys and the scalar encoding
|
||||
set, the *allowlisted* key set, and the canonical emission *order*.
|
||||
STRICT_V1 requires eight keys inside an eleven-key allowlist (see
|
||||
`docs/phase-3-split-table.md`), so a single ordered required/optional pair
|
||||
cannot express it. Also carries nullable keys and the scalar encoding
|
||||
subset. Phase 1's seven-key layer and Phase 2's inbox layer are
|
||||
expressions of this schema.
|
||||
- `path_policy`: layer/path prefixes, filename namespaces, reserved filename
|
||||
|
|
@ -39,16 +39,19 @@ the golden suite is the regression harness for that claim.
|
|||
2. **Built-in profiles:**
|
||||
- `DEFAULT` (implicit): exactly the ingest-spec v1 + Phase 2 contract.
|
||||
- `STRICT_V1`: the `claude-code-llm-wiki` contract — closed type enum
|
||||
(`Concept`/`Guide`/`Reference`/`Release`), four required frontmatter keys
|
||||
(`type`, `title`, `description`, `timestamp`) inside an eleven-key
|
||||
allowlist that doubles as emission order, layer/path/type/source_tier
|
||||
agreement rules, `log.md` required-absent, `README.md` reserved-exempt,
|
||||
per-directory indexes, and root-index metadata (`okf_version`,
|
||||
`bundle_profile`, `okf_spec_commit`).
|
||||
(`Concept`/`Guide`/`Reference`/`Release`), eight required frontmatter keys
|
||||
(`type`, `title`, `description`, `timestamp`, `layer`, `source_tier`,
|
||||
`source_url`, `source_sha`) inside an eleven-key allowlist that doubles as
|
||||
emission order, layer/path/type/source_tier agreement rules, `log.md`
|
||||
required-absent, `README.md` reserved-exempt, per-directory indexes, and
|
||||
root-index metadata (`okf_version`, `bundle_profile`, `okf_spec_commit`).
|
||||
|
||||
An earlier draft of this line listed eight required keys. That was the
|
||||
allowlist read as the required set; corrected against the validator on
|
||||
2026-07-25.
|
||||
This line was drafted as eight, corrected to four against the consumer's
|
||||
`REQUIRED_KEYS` constant on 2026-07-25, and settled at eight by their
|
||||
operator the same day: the four-key constant is their emit-path minimum,
|
||||
not their contract, and all eight are present on 522/522 of their
|
||||
documents. STRICT_V1 is therefore deliberately stricter than the
|
||||
consumer's own validator; the split table records the agreed consequence.
|
||||
3. **Plumbing change:** materialization, inbox, import, and index generation take
|
||||
an optional `profile` argument defaulting to `DEFAULT`. No behavior branches
|
||||
outside what the profile object expresses.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue