Contracts may declare where a repo keeps its knowledge: an optional `knowledge:` block, and a governed set that widens to match
rank: decision outranks: []
Section titled “rank: decision outranks: []”Contracts may declare where a repo keeps its knowledge: an optional knowledge: block, and a governed set that widens to match
Section titled “Contracts may declare where a repo keeps its knowledge: an optional knowledge: block, and a governed set that widens to match”Date: 2026-10-22 Status: closed Supersedes: none Superseded-by: none — current MR-compliance: MR-4, MR-7, MR-9, MR-12, MR-15, MR-20
Context
Section titled “Context”docs/decisions/2026-10-22-estate-knowledge-routing.md gives each kind of
knowledge artefact one home, and its rule R4 says a registered repo may
declare its knowledge directories in .petrova/contract.yaml. That rule
needs a field to exist. Today it cannot:
contracts/contract.schema.jsonsetsadditionalProperties: falseat the root, so anyknowledge:key makes a contract invalid.host/src/estate/domains.ts:78hard-codesGOVERNED_PATHSto.petrova/**,docs/decisions/**,MILESTONES.mdandCLAUDE.md. Every domain module indocs/domains/lists exactly those four. Findings, runbooks and doctrine are outside the control plane’s view in every repo, even where a repo keeps them in a fixed place.
The cross-store index (eva-hq cycles/knowledge-index/, Phase 3 of the
routing work) needs to know where each repo’s knowledge lives. Guessing from
directory names is the inference MR-6 forbids for subagents; a declaration in
the contract is the repo saying so itself.
Decision
Section titled “Decision”Proposed for FLIGHT. Merging this doc changes no contract in any repo.
1. Shape. An optional top-level block in .petrova/contract.yaml:
knowledge: decisions: docs/decisions findings: docs/findings runbooks: docs/runbooks doctrine: docs/doctrine external: - { repo: devarno-cloud/atlas, path: findings, kind: findings }- All keys are optional, but the block, when present, has at least one.
- Each local value is a repo-relative directory: no leading slash, no
trailing slash, no
..segment, no glob. externalnames knowledge this repo relies on that lives in another repo, as{repo: <org>/<name>, path, kind}.kindis one ofdecisions,findings,runbooks,doctrine.- No other kinds. Prompts, plays and skills already have fixed homes in the routing table; adding them here would be a second place to say the same thing.
2. Schema. contracts/contract.schema.json gains the optional
knowledge property and a knowledge_dir definition. Existing contracts
stay valid unchanged. contracts/bootstrap-questions.yaml is unaffected:
it is generated from the integrations only (MR-15).
3. Governed set. host/src/estate/domains.ts gains governedPaths(),
which returns the four fixed paths plus <dir>/** for each declared local
dir, deduplicated, in a stable order. external entries are never added:
another repo’s paths are governed by that repo’s own module.
renderModule() takes the block as an optional argument.
4. What ships now, and what follows. This PR ships the schema field, the
Contract type, governedPaths() and the renderModule() argument, with
tests. It does not feed real contracts into the generator.
generateDomains() reads only registry.yaml and estate.yaml; neither
carries a repo’s contract, and the generator must stay deterministic for its
--check gate. So every committed module keeps the four fixed paths until a
follow-up does this:
- the state sweep records each contract’s
knowledge:block instate/<slug>.yaml(acontracts/state.schema.jsonchange); generateDomains()reads it from there and passes it torenderModule();- the regenerated
docs/domains/*.mdland in the same PR.
That follow-up is its own PR under this decision, after the countersign.
Alternatives considered
Section titled “Alternatives considered”- A registry field instead of a contract field. Rejected. The registry is the control plane’s view of a repo; where a repo keeps its notes is the repo’s own fact (push-down), and contracts are where repos declare intent.
- Widen
GOVERNED_PATHStodocs/findings/**for every repo. Rejected. It asserts authority over a directory many repos do not have and some keep elsewhere, which is a governed set that claims more than it governs. - Infer knowledge dirs by scanning each repo. Rejected. An inferred location is a guess that reads as a fact in the rendered module.
- Fetch each contract live inside
generateDomains(). Rejected. It would make a committed projection depend on the network and break the byte-equal--checkgate.
Consequences
Section titled “Consequences”For code:
- This PR:
contracts/contract.schema.json,cli/src/types.ts(ContractKnowledge),host/src/estate/domains.ts(governedPaths(),renderModule()argument), with fixtures and tests.docs/domains/*.mddo not change. - Follow-up PR: state sweep and
contracts/state.schema.json, then the generator wiring and regenerated modules (Decision §4).
For docs: consumer repos may add the block to their contracts at will.
The petrova-contract-author skill gains a line on it when the follow-up
lands.
For in-flight phases: none paused.
For invariants: none (MR-9).
What this decision does NOT do:
- It does not require any repo to declare the block.
- It does not move, rename or create any knowledge directory.
- It does not decide the routing table; that is
docs/decisions/2026-10-22-estate-knowledge-routing.md. - It does not change what the write verbs may touch.
References
Section titled “References”docs/decisions/2026-10-22-estate-knowledge-routing.md(rule R4; sibling draft)contracts/contract.schema.json(rootadditionalProperties: false)host/src/estate/domains.ts:78(GOVERNED_PATHS)host/src/estate/cli.ts(domains:generate -- --check, the byte-equal gate)
Sign-off
Section titled “Sign-off”- Subagent: Claude Opus 5.5, 2026-10-22 (drafted with the schema and
governedPaths()change in the same PR; read at petrova-codesedbfd044) - Human countersign — the block’s shape in §1, the governed-set rule in §3 (local dirs only, never
external), and the staged follow-up in §4 — flight@arno.host, 2026-10-22 (jzero.run T1)
Countersigned by human:flight@arno.host on 2026-10-22 through jzero.run /approvals —
box ticked in commit 98b2c9fb on main, authored by the human: the block’s shape in §1, the governed-set rule in §3 and the staged follow-up in §4 accepted as drafted. The §4 follow-up (state sweep records each contract’s knowledge: block; the domains generator reads it) is now unblocked and lands as its own PR.
Status flipped open→closed on 2026-10-22 by an agent as scribe (MR-7).