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
|
# Phase 3 split table — the proving consumer's 25 gates
|
||||||
|
|
||||||
Status: **drafted and independently corroborated; operator review outstanding.**
|
Status: **reviewed by the proving consumer and by the operator (2026-07-25);
|
||||||
Phase 3's plan requires this mapping before any porting. Nothing here is
|
the port may begin.** Phase 3's plan requires this mapping before any porting.
|
||||||
implemented yet, and nothing in the consumer's repository is modified by this
|
Nothing in the consumer's repository is modified by this repository.
|
||||||
repository.
|
|
||||||
|
|
||||||
The proving consumer (`claude-code-llm-wiki`) answered the same questions from
|
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
|
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
|
to it. Two independent readings of the same source agreed on every substantive
|
||||||
point: four required keys, three reserved basenames, `summary` as the only
|
point: the required-key set as their code spells it, three reserved basenames,
|
||||||
nullable key, the index structure, and the closed sets. Their answer added four
|
`summary` as the only nullable key, the index structure, and the closed sets.
|
||||||
refinements, each folded in below and marked. Their column assignments match
|
Their answer added four refinements, each folded in below and marked. Their
|
||||||
this table's, with one wording difference resolved in gate 5's row.
|
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:
|
Read from the consumer's working tree on 2026-07-25:
|
||||||
`tools/wiki_ingest/validate.py` and `tools/wiki_ingest/bundle.py` at commit
|
`tools/wiki_ingest/validate.py` and `tools/wiki_ingest/bundle.py` at commit
|
||||||
|
|
@ -103,19 +105,37 @@ considered.
|
||||||
## What STRICT_V1 must express
|
## What STRICT_V1 must express
|
||||||
|
|
||||||
The phase-3 plan sketched this profile before the validator was read. Two of
|
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
|
its claims did not survive contact and are corrected here; the plan file
|
||||||
corrected in the same commit.
|
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,
|
1. **Required keys are eight — settled by the consumer, not read off their
|
||||||
description, timestamp)`. The eight-key list in the plan was a truncation of
|
code.** Their `REQUIRED_KEYS` constant is four (`type`, `title`,
|
||||||
`FRONTMATTER_KEY_ORDER`, which is a different thing: an eleven-key
|
`description`, `timestamp`), and this table first corrected the plan down to
|
||||||
*allowlist* that doubles as canonical emission order — `type`, `title`,
|
it. Asked whether four was the contract or an artefact of unfinished triage
|
||||||
`description`, `timestamp`, `layer`, `source_tier`, `source_url`,
|
(Q2), they measured the whole bundle and their operator ratified eight:
|
||||||
`source_sha`, `version`, `date`, `summary`. So `frontmatter_schema` needs
|
`type`, `title`, `description`, `timestamp`, `layer`, `source_tier`,
|
||||||
three fields, not one: required, allowlisted, and ordered. The non-required
|
`source_url`, `source_sha`, present on 522/522 documents. The four-key
|
||||||
keys are validated against closed sets **when present**, not required to be
|
constant is their *emit-path* minimum, not their contract.
|
||||||
present — a profile that requires all of them rejects pages the consumer
|
|
||||||
ships today.
|
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
|
2. **Three reserved basenames, not two.** `index.md`, `log.md`, and
|
||||||
`README.md`. README is bundle documentation, reserved like `index.md`: it
|
`README.md`. README is bundle documentation, reserved like `index.md`: it
|
||||||
carries no frontmatter, is skipped by the per-document gates, is never
|
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
|
by the golden suite), so `index_policy` has to express both shapes rather than
|
||||||
generalising one into the other.
|
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
|
All four questions were answered on 2026-07-25, each measured or read out of
|
||||||
`verbatim` disposition, because no LLM rewrite layer sits in front of the
|
their code that day rather than recalled. **No row in the mapping moved**; the
|
||||||
published text. That is a policy tier chosen per document, over a guard
|
reasons under three of the four are sharper than the ones this table drafted.
|
||||||
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?
|
- **Q1 — gate tiers stay Consumer/Guard.** This repository's position was
|
||||||
This repository's position is the latter — the profile describes structure,
|
accepted, and the escalation turns out not to be a document-class rule at
|
||||||
not security tiers — but it is the consumer's call, since it decides where
|
all: gate 5 escalates WARN to ERROR when the per-version *record* carries
|
||||||
their gate lives after the port.
|
`disposition == "verbatim"` (`validate.py:528-540`), i.e. when the LLM
|
||||||
- **Q2.** Is the four-key required set final, or is it four only because seed
|
rewrite layer was skipped and the input lint is the only filter left. That is
|
||||||
triage has not finished? `layer`, `source_tier`, `source_url` and
|
run state, not bundle shape — a profile could not see it even if it wanted
|
||||||
`source_sha` are allowlisted and, in practice, present on every generated
|
to. A profile describes structure; it does not set security tiers.
|
||||||
document — if they are meant to be required, STRICT_V1 should require them.
|
- **Q2 — eight, ratified by their operator.** Folded into "What STRICT_V1 must
|
||||||
- **Q3.** Gate 4's second half asserts that no `concepts/` page may derive from
|
express" above, together with the consequence both sides accepted.
|
||||||
the changelog page. That reads as a layer-provenance rule, which is Profile
|
- **Q3 — Consumer, for a sharper reason than "URL predicate".** The check is
|
||||||
territory, but it is expressed as a URL predicate over one specific upstream,
|
`is_changelog_url()` (`docs_corpus.py:181-184`): it lowercases the last URL
|
||||||
which is Consumer territory. Mapped to Consumer here. Correct?
|
segment and tests membership in `("changelog", "release-notes")` — a
|
||||||
- **Q4.** Gates 19 and 22 are mapped to Consumer because they need state this
|
vendor-specific slug list for one upstream. Two details worth carrying: the
|
||||||
library never collects (link liveness, page hashes). If either is expected to
|
gate is **not** scoped to `concepts/` at all (it walks every non-reserved
|
||||||
move, the profile would have to grow a notion of external state, which is a
|
document across both layers, `validate.py:480-492`, despite a rationale
|
||||||
larger change than phase 3 scopes.
|
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
|
## 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
|
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
|
this repository — the phase-2 grep gate over guard functionality still applies
|
||||||
unchanged.
|
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
|
- `type_policy`: closed enum vs. open set; the reserved `verdict` exclusion
|
||||||
stays unconditional (it is a spec invariant, not profile config).
|
stays unconditional (it is a spec invariant, not profile config).
|
||||||
- `frontmatter_schema`: three distinct fields, not one — the *required* key
|
- `frontmatter_schema`: three distinct fields, not one — the *required* key
|
||||||
set, the *allowlisted* key set, and the canonical emission *order*. The
|
set, the *allowlisted* key set, and the canonical emission *order*.
|
||||||
proving consumer keeps four required keys inside an eleven-key allowlist
|
STRICT_V1 requires eight keys inside an eleven-key allowlist (see
|
||||||
(see `docs/phase-3-split-table.md`), so a single ordered required/optional
|
`docs/phase-3-split-table.md`), so a single ordered required/optional pair
|
||||||
pair cannot express it. Also carries nullable keys and the scalar encoding
|
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
|
subset. Phase 1's seven-key layer and Phase 2's inbox layer are
|
||||||
expressions of this schema.
|
expressions of this schema.
|
||||||
- `path_policy`: layer/path prefixes, filename namespaces, reserved filename
|
- `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:**
|
2. **Built-in profiles:**
|
||||||
- `DEFAULT` (implicit): exactly the ingest-spec v1 + Phase 2 contract.
|
- `DEFAULT` (implicit): exactly the ingest-spec v1 + Phase 2 contract.
|
||||||
- `STRICT_V1`: the `claude-code-llm-wiki` contract — closed type enum
|
- `STRICT_V1`: the `claude-code-llm-wiki` contract — closed type enum
|
||||||
(`Concept`/`Guide`/`Reference`/`Release`), four required frontmatter keys
|
(`Concept`/`Guide`/`Reference`/`Release`), eight required frontmatter keys
|
||||||
(`type`, `title`, `description`, `timestamp`) inside an eleven-key
|
(`type`, `title`, `description`, `timestamp`, `layer`, `source_tier`,
|
||||||
allowlist that doubles as emission order, layer/path/type/source_tier
|
`source_url`, `source_sha`) inside an eleven-key allowlist that doubles as
|
||||||
agreement rules, `log.md` required-absent, `README.md` reserved-exempt,
|
emission order, layer/path/type/source_tier agreement rules, `log.md`
|
||||||
per-directory indexes, and root-index metadata (`okf_version`,
|
required-absent, `README.md` reserved-exempt, per-directory indexes, and
|
||||||
`bundle_profile`, `okf_spec_commit`).
|
root-index metadata (`okf_version`, `bundle_profile`, `okf_spec_commit`).
|
||||||
|
|
||||||
An earlier draft of this line listed eight required keys. That was the
|
This line was drafted as eight, corrected to four against the consumer's
|
||||||
allowlist read as the required set; corrected against the validator on
|
`REQUIRED_KEYS` constant on 2026-07-25, and settled at eight by their
|
||||||
2026-07-25.
|
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
|
3. **Plumbing change:** materialization, inbox, import, and index generation take
|
||||||
an optional `profile` argument defaulting to `DEFAULT`. No behavior branches
|
an optional `profile` argument defaulting to `DEFAULT`. No behavior branches
|
||||||
outside what the profile object expresses.
|
outside what the profile object expresses.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue