diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57dbe01f..6b371580 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,8 +40,8 @@ jobs: run: bash tests/docs/test_retired_component_refs.sh - name: repository knowledge model regression run: bash tests/docs/test_knowledge_model.sh - - name: ADR namespace regression - run: bash tests/docs/test_adr_namespace.sh + - name: Documentation model regression + run: bash tests/docs/test_documentation_model.sh - name: generated rule truth regression run: bash tests/docs/test_generated_rule_truth.sh - name: determinism policy gate regression diff --git a/ADVANCED_GUIDE.md b/ADVANCED_GUIDE.md index 85c31b5f..601515fa 100644 --- a/ADVANCED_GUIDE.md +++ b/ADVANCED_GUIDE.md @@ -15,7 +15,8 @@ Use [GUIDE.md](GUIDE.md) for operator orientation and the - [Strictly Deterministic Math](docs/determinism/SPEC_DETERMINISTIC_MATH.md) owns the normative numeric policy; - [specifications](docs/spec/), [invariants](docs/invariants/), and - [ADRs](docs/adr/) own executable contracts and durable decisions. + [topics](docs/topics/) own executable contracts and concept-owned durable + decisions under the [Documentation Standards](docs/DOCUMENTATION_STANDARDS.md). Do not recover the former graph-substrate, stale spec-path, generated-client, or fabricated trig-oracle guidance from this page. Git history is the archive, diff --git a/AGENTS.md b/AGENTS.md index e0892b8b..f47daf32 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,7 +68,9 @@ Repository knowledge has one owner for each kind of truth: - **Current architectural truth**: `docs/architecture/`, `docs/spec/`, `docs/invariants/`, and `docs/topics/`. -- **Durable architectural decisions**: accepted ADRs in `docs/adr/`. +- **Documentation policy and decision relationships**: + `docs/DOCUMENTATION_STANDARDS.md`. +- **Historical numbered decisions**: the closed archive in `docs/adr/`. - **Live work, priority, dependencies, and status**: GitHub Issues, Projects, pull requests, and review threads. - **Shipped externally meaningful behavior**: `CHANGELOG.md`. @@ -77,18 +79,20 @@ Repository knowledge has one owner for each kind of truth: Use `README.md`, `GUIDE.md`, and `docs/README.md` as entrances. Do not recreate cycles, retrospectives, a checked-in backlog, a checked-in status ledger, or a post-hoc design document. Change-local design and test plans may live in the -issue or pull request. Write an ADR only when a decision changes a durable -architectural boundary or invariant. +issue or pull request. Record a durable decision in the named current document +that owns its concept, and state supersession, refinement, dependency, and +related-decision edges explicitly. Do not allocate a new numbered ADR. When recovering context, read the relevant canonical topic/spec/invariant and -ADR, then inspect the current GitHub issue or pull request, `git log -n 5`, and -`git status`. +architecture document, follow any explicit links into the historical ADR +archive, then inspect the current GitHub issue or pull request, `git log -n 5`, +and `git status`. ## Work Loop ```text scope claim --> decide whether an ADR is required +-> identify the canonical documentation owner -> name the test plan and executable witness -> RED -> GREEN @@ -139,7 +143,7 @@ changes, compare the branch's actual behavior and artifacts with the current documentation. Revisit the relevant entrances and canonical owners: `README.md`, `GUIDE.md`, `docs/README.md`, `docs/architecture/`, `docs/spec/`, `docs/invariants/`, `docs/topics/`, and the evidence anchors of any applicable -accepted ADRs. +historical ADRs. Search specifically for stale current-state claims such as “not implemented,” “fixture-only,” old ownership or authority boundaries, obsolete version or diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 991404a0..63601eb6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -20,7 +20,8 @@ owns the boundary you are changing: [Runtime Constellation](docs/topics/RuntimeConstellation.md), and [WARP Optics](docs/topics/WarpOptics.md) — living cross-module doctrine; - [specifications](docs/spec/), [invariants](docs/invariants/), and - [ADRs](docs/adr/) — executable contracts and durable decisions. + [topics](docs/topics/) — executable contracts and concept-owned durable + decisions under the [Documentation Standards](docs/DOCUMENTATION_STANDARDS.md). The current north star is: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5432b9e0..ab10e573 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,8 +48,9 @@ prioritize: - Keep `main` pristine. Create feature branches like `echo/` or `timeline/`. - Before starting work, ensure `git status` is clean. If not, resolve or coordinate with the human operator. - Keep change-local design, test plans, review state, and follow-up work in the - GitHub issue or pull request. Record only durable architectural decisions as - ADRs in `docs/adr/`. + GitHub issue or pull request. Record durable architectural decisions in the + semantically named current document that owns the concept, following + `docs/DOCUMENTATION_STANDARDS.md`. ## Testing Expectations @@ -73,8 +74,9 @@ prioritize: ## Documentation & Telemetry - Update relevant docs in `docs/` whenever behavior or architecture changes. -- Record durable architectural decisions in ADRs (`docs/adr/`). Keep - change-local rationale in the issue or pull request. +- Record durable architectural decisions with their owning concept under + `docs/DOCUMENTATION_STANDARDS.md`. Keep change-local rationale in the issue or + pull request; treat `docs/adr/` as a closed historical archive. ## Submitting Changes diff --git a/GUIDE.md b/GUIDE.md index 824bbe8d..67959a45 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -62,7 +62,9 @@ If you need a comprehensive spec, use the [docs/README.md](./docs/README.md) map If you need current architectural truth, use the [architecture](./docs/architecture/), [specification](./docs/spec/), [invariant](./docs/invariants/), and [topic](./docs/topics/) maps. Durable -decisions live in [ADRs](./docs/adr/); live work and status live in GitHub. +decisions live with their owning concept under the +[Documentation Standards](./docs/DOCUMENTATION_STANDARDS.md); the numbered +[ADR directory](./docs/adr/) is historical. Live work and status live in GitHub. If you are just starting, use the [README.md](./README.md) and the orientation tracks above. diff --git a/README.md b/README.md index 978a156f..76ad2658 100644 --- a/README.md +++ b/README.md @@ -601,9 +601,10 @@ the external Hello Echo application. Compiler-produced application packages use the generic `run-edict-operation` witness command. Live work, priorities, and status are maintained in GitHub Issues, Projects, -pull requests, and review threads. Architectural decisions live in -[`docs/adr/`](docs/adr/), while current doctrine lives in -[`docs/topics/`](docs/topics/). +pull requests, and review threads. Durable decisions live in the semantically +named current document that owns their concept, under the +[`Documentation Standards`](docs/DOCUMENTATION_STANDARDS.md). The numbered +[`docs/adr/`](docs/adr/) directory is a closed historical archive. ## Benchmarks And Gates diff --git a/docs/DOCUMENTATION_STANDARDS.md b/docs/DOCUMENTATION_STANDARDS.md new file mode 100644 index 00000000..b0ed01c2 --- /dev/null +++ b/docs/DOCUMENTATION_STANDARDS.md @@ -0,0 +1,205 @@ + + + +# Documentation Standards + +**Status:** Current project policy for new and substantially changed +documentation. +**Supersedes:** [ADR 0012: Repository Knowledge Model After Method](adr/0012-repository-knowledge-model.md) + +Echo documentation is part of the engineering contract. Its job is to make +the current system, its governing decisions, and the evidence for those claims +findable without reconstructing a private chronology. + +This policy adapts the structure of Colorful Language's +[documentation standard](https://github.com/flyingrobots/colorful-language/blob/09dc60a9f23834f8511bcb3067cb1ac4393fae8a/docs/DOCUMENTATION_STANDARDS.md) +to Echo's existing knowledge model. Echo keeps its established split among +architecture, specifications, invariants, topics, and executable witnesses. It +does not adopt Colorful's roadmap, goalpost, or topic test-plan machinery. + +## Core rules + +1. Give every durable claim one canonical owner. +2. Give every page one primary reader job. +3. Name pages after concepts, contracts, or reader tasks, not sequence slots. +4. State relationships explicitly; directory order and filename order carry no + architectural meaning. +5. Keep current truth, planned work, historical reasoning, and executable + evidence distinguishable. +6. Link strong claims to code, tests, specifications, invariants, or retained + historical decisions. + +## Corpus map + +| Location | Job | +| ---------------------------------------------------------- | ------------------------------------------------------------------- | +| `README.md`, `GUIDE.md` | Project entrances and supported routes into deeper documentation. | +| `docs/README.md` | Documentation spine and routing index. | +| `docs/topics/` | Living explanations of durable Echo concepts and boundaries. | +| `docs/architecture/` | Current cross-subsystem architecture and accepted boundary designs. | +| `docs/spec/` | Normative protocols, encodings, and conformance contracts. | +| `docs/invariants/` | Compact laws that implementations must preserve. | +| `docs/determinism/` | Determinism policy, hazards, and evidence contracts. | +| `docs/adr/` | Closed archive of numbered historical decision records. | +| GitHub Issues, Projects, pull requests, and review threads | Live work, plans, priority, blockers, and status. | +| `CHANGELOG.md` | Externally meaningful behavior that shipped. | +| Git history | Removed material and the exact evolution of checked-in documents. | + +Do not create a second current reference merely because another directory or +document type is convenient. Link to the canonical owner and add only the +reader-specific context the new page needs. + +## Page jobs + +A page should primarily help its reader do one of these jobs: + +- learn a concept or boundary; +- perform a supported task; +- look up an exact contract; +- understand a decision and its tradeoffs; +- troubleshoot an observable failure; +- change the implementation and verify the result. + +A page may link across jobs, but it should not become a tutorial, reference, +roadmap, architecture guide, and historical diary at once. + +### Topics + +A topic describes the current conceptual model. It states the boundary, +invariants, ownership, and evidence anchors needed to understand that concept. +Update it in the same change that changes the boundary. + +Topics do not own live implementation queues. A limitation may be stated as a +current fact; the work to change it belongs in GitHub. + +### Architecture + +An architecture page explains current cross-subsystem structure or an accepted +boundary design. When an accepted design is not implemented, the page must say +so prominently and link to the GitHub owner for implementation state. It must +not describe planned behavior as existing runtime behavior. + +### Specifications and invariants + +Specifications define conformance. Invariants state compact laws. They are not +explanatory essays or implementation plans. Examples that form part of a +contract should be executable or backed by exact fixtures when practical. + +## Durable decisions + +A durable decision changes a long-lived boundary, identity, format, invariant, +authority split, compatibility promise, or recovery law. Record the decision +where a future reader will look for the concept: + +- update the owning topic for a conceptual boundary; +- update the owning architecture page for a cross-subsystem boundary; +- update the owning specification or invariant for a normative contract; +- add a semantically named `rationale.md` beside a larger concept when the + tradeoffs would otherwise overwhelm its current reference. + +Do not allocate a number merely to prove that a decision happened. The numbered +ADR sequence in `docs/adr/` is a closed historical archive. Existing ADRs remain +valuable evidence and may be linked, refined, or superseded, but new durable +decisions use semantic names in their owning current-documentation area. + +### Relationship contract + +When a decision relates materially to another decision, include the applicable +relationship near the top of the owning document: + +- **Supersedes:** the named older decision no longer governs the stated scope. +- **Superseded by:** the named newer decision now governs the stated scope. +- **Refines:** this decision adds precision without replacing the older one. +- **Depends on:** this decision requires another decision or contract to hold. +- **Related:** the documents illuminate the same boundary but neither governs + the other. + +Use descriptive links, not bare identifiers. A supersession must be recorded in +both directions so readers entering through either document can follow it. +Absence of a relationship line means no such relationship is claimed; it does +not mean “whatever has the larger number wins.” + +## Current truth, plans, and history + +Living references describe current implementation truth or clearly labeled +accepted contracts. GitHub owns change-local plans and status. Git history owns +the exact old text. + +Do not check in backlogs, cycle packets, retrospectives, review transcripts, +status ledgers, or roadmap checklists. A short checked-in redirect may remain +when an old stable path must route readers to its current owner. + +Historical reasoning must not masquerade as current behavior. Mark retained +historical documents clearly and link to the current owner that supersedes or +refines them. + +## Evidence and citations + +Strong claims should point to the smallest durable witness that establishes +them: + +- source or public API for ownership and shape; +- tests, fixtures, or golden vectors for behavior; +- a specification or invariant for normative law; +- an accepted architecture page for an unimplemented boundary contract; +- a historical decision for retained reasoning. + +Source links support an explanation; they do not replace one. Prefer +repository-relative links for checked-in sources. Use stable external +permalinks when the exact outside revision matters. + +Never claim that a command, test, visual inspection, review, or runtime path was +verified when it was not run or observed. + +## Examples and safety + +Examples must use supported behavior and enough context to interpret them. +Separate copyable commands from expected output. Do not put shell prompts in a +copyable command block. + +Put warnings before destructive, privileged, costly, or irreversible commands. +State the scope and consequence, and provide a safer check or recovery route +when one exists. + +## Writing and structure + +- Lead with the result, decision, warning, or essential condition. +- Prefer exact Echo terms and define unfamiliar ones at first use. +- Use active voice when it clarifies ownership. +- Use prose for causality and tradeoffs, lists for parallel facts, and tables + for genuinely two-dimensional comparisons. +- Use descriptive link text rather than “here” or a bare path. +- Treat length and style metrics as editorial signals, not universal merge + gates. + +## Maintenance loop + +For a meaningful change: + +1. Identify the canonical owner of the affected claim. +2. Name the smallest executable witness when behavior changes. +3. Update design rationale only when the tradeoff needs durable explanation. +4. Implement and validate the change. +5. Update the current owner after the behavior or accepted contract changes. +6. Add explicit decision relationships when governance changed. +7. Update `docs/README.md` when a durable route was added or moved. +8. Keep live follow-up work in GitHub. + +## Review checklist + +Before calling a documentation change done, verify that: + +- the page has one primary reader job; +- the durable claim has one canonical owner; +- current behavior and accepted-but-unimplemented design are distinguishable; +- plans and status remain in GitHub; +- durable decision relationships are explicit and bidirectional when they + supersede; +- strong claims have appropriate evidence anchors; +- new durable pages are linked from `docs/README.md`; +- internal links resolve; +- Markdown and whitespace checks pass. + +The objective is not uniform paperwork. The objective is a corpus in which a +reader can find what governs a concept, why it governs, what it replaced, and +what proves it without decoding a global number line. diff --git a/docs/README.md b/docs/README.md index 388aa1a9..196a6ae6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,6 +8,7 @@ causal history. Git history is the archive; GitHub owns live work and status. ## Start Here +- [Documentation standards](DOCUMENTATION_STANDARDS.md) - [Architecture outline](architecture/outline.md) - [Application contract hosting](architecture/application-contract-hosting.md) - [Local contract host quickstart](quickstart-local-contract-host.md) @@ -20,6 +21,7 @@ causal history. Git history is the archive; GitHub owns live work and status. - [There Is No Graph](architecture/there-is-no-graph.md) - [Continuum transport](architecture/continuum-transport.md) - [Echo optics adapter notes](architecture/echo-optics-adapter-notes.md) +- [Echo × Keep physical content boundary](architecture/echo-keep-physical-content-boundary.md) ## Case Studies @@ -40,10 +42,15 @@ causal history. Git history is the archive; GitHub owns live work and status. - [Runtime constellation](topics/RuntimeConstellation.md) - [Security posture and threat models](topics/security/README.md) -## Durable Decisions +## Decision History -- [ADR map](adr/README.md) -- [Repository knowledge model](adr/0012-repository-knowledge-model.md) +Current decisions are reached through the semantic architecture, topic, and +contract sections above and below. This section routes to the retained numbered +archive; it is not an allocation queue or a substitute for explicit +relationships. + +- [Legacy numbered decision archive](adr/README.md) +- [Superseded repository knowledge model](adr/0012-repository-knowledge-model.md) - [Echo/Continuum authority boundary](adr/0013-echo-continuum-authority-boundary.md) - [Generated rule authorship and footprints](adr/0014-generated-rule-authorship-and-footprints.md) - [Registry, provider, and host boundary](adr/0015-registry-provider-host-boundary.md) @@ -79,7 +86,11 @@ causal history. Git history is the archive; GitHub owns live work and status. ## Knowledge Ownership Current architecture belongs in architecture documents, specifications, -invariants, and living topics. Accepted durable decisions belong in ADRs. -Externally meaningful shipped behavior belongs in `CHANGELOG.md`. Live design, -priority, dependencies, review state, and follow-up work belong in GitHub -Issues, Projects, pull requests, and review threads. +invariants, and living topics. Durable decisions update the named document that +owns their concept and state relationships explicitly. Numbered ADRs are a +closed historical archive, not the current decision queue. Externally +meaningful shipped behavior belongs in `CHANGELOG.md`. Live design, priority, +dependencies, review state, and follow-up work belong in GitHub Issues, +Projects, pull requests, and review threads. The +[documentation standards](DOCUMENTATION_STANDARDS.md) define the complete +contract. diff --git a/docs/adr/0012-repository-knowledge-model.md b/docs/adr/0012-repository-knowledge-model.md index 19f43f5d..9e87f2af 100644 --- a/docs/adr/0012-repository-knowledge-model.md +++ b/docs/adr/0012-repository-knowledge-model.md @@ -3,9 +3,10 @@ # ADR 0012: Repository Knowledge Model After Method -- **Status:** Accepted +- **Status:** Superseded - **Date:** 2026-07-13 - **Decision owner:** James Ross +- **Superseded by:** [Documentation Standards](../DOCUMENTATION_STANDARDS.md) ## Context @@ -20,6 +21,15 @@ evidence-first engineering discipline. ## Decision +This section preserves the repository knowledge model accepted in July 2026. +The current policy is the semantically named +[Documentation Standards](../DOCUMENTATION_STANDARDS.md), which retains the +separation of current truth, motion, evidence, and history while replacing the +numbered ADR queue with concept-owned decisions and explicit relationships. + +The five-home model below is retained historical text from July 2026. It does +not govern current ownership; the Documentation Standards policy does. + Repository knowledge has five non-overlapping homes: 1. **GitHub issues, pull requests, and projects own motion.** Priority, current diff --git a/docs/adr/0020-retained-reading-storage-and-proof-boundary.md b/docs/adr/0020-retained-reading-storage-and-proof-boundary.md index 23e957dc..2dcd7343 100644 --- a/docs/adr/0020-retained-reading-storage-and-proof-boundary.md +++ b/docs/adr/0020-retained-reading-storage-and-proof-boundary.md @@ -5,6 +5,7 @@ - **Status:** Accepted - **Date:** 2026-07-13 +- **Refined by:** [Echo–Keep Physical Content Boundary](../architecture/echo-keep-physical-content-boundary.md) ## Context diff --git a/docs/adr/README.md b/docs/adr/README.md index a971974c..633089b3 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -1,23 +1,32 @@ -# Architecture Decision Records +# Legacy Architecture Decision Archive -Architecture Decision Records preserve decisions whose consequences outlive a -single issue or pull request. They explain why a boundary exists; they do not -track work, progress, priority, or release readiness. +These numbered records preserve Echo decisions accepted before the repository +adopted concept-owned documentation. They remain useful historical evidence, +but their numbers are locators only: sequence does not express dependency, +supersession, refinement, importance, or current authority. -## Contract +The archive closed after record 0026 on 2026-08-09. New durable decisions update +the semantically named topic, architecture document, specification, invariant, +or colocated rationale that owns the concept. The +[documentation standards](../DOCUMENTATION_STANDARDS.md) define that current +contract. -- Use a four-digit sequence and a short noun phrase: - `0002-example-boundary.md`. -- Give every new record a status: `Proposed`, `Accepted`, - `Partially superseded`, `Superseded`, or `Rejected`. -- Do not rewrite an accepted decision to conceal history. Add a new record and - mark the old record superseded. -- Record alternatives and consequences, not an implementation diary. +## Archive contract + +- Do not allocate another numbered record. +- Preserve accepted historical text; add a clear status or relationship note + when current documentation supersedes or refines it. +- Record supersession in both the historical record and its new semantic owner. +- Follow explicit descriptive links; never infer a relationship from numbers. - Keep work state in GitHub issues and pull requests. +Some older records predate the relationship contract and name only a status. +When one is materially revisited, add descriptive forward and reverse links +without rewriting its original reasoning. + ## Index | ADR | Status | Decision | @@ -33,7 +42,7 @@ track work, progress, priority, or release readiness. | [0009](ADR-0009-Inter-Worldline-Communication.md) | Superseded | Historical frontier-relative state-patch transport | | [0010](ADR-0010-observational-seek-and-administrative-rewind.md) | Accepted | Observational seek and administrative rewind | | [0011](ADR-0011-explicit-observation-contract.md) | Partially superseded | Explicit observation mechanics; ADR 0021 governs public optics | -| [0012](0012-repository-knowledge-model.md) | Accepted | Repository knowledge model after Method | +| [0012](0012-repository-knowledge-model.md) | Superseded | Historical repository knowledge model after Method | | [0013](0013-echo-continuum-authority-boundary.md) | Accepted | Echo and Continuum authority boundary | | [0014](0014-generated-rule-authorship-and-footprints.md) | Accepted | Generated rule authorship and footprint honesty | | [0015](0015-registry-provider-host-boundary.md) | Partially superseded | Compiler, registry, provider, and host responsibilities | diff --git a/docs/architecture/echo-keep-physical-content-boundary.md b/docs/architecture/echo-keep-physical-content-boundary.md new file mode 100644 index 00000000..0ab8507f --- /dev/null +++ b/docs/architecture/echo-keep-physical-content-boundary.md @@ -0,0 +1,360 @@ + + + +# Echo × Keep Physical Content Boundary + +- **Status:** Accepted for experimental conformance; production adoption is + not accepted. +- **Decision date:** 2026-08-09 +- **Implementation posture:** No Echo physical-content port or Keep adapter is + implemented on this branch. +- **Refines:** [Retained reading storage and proof boundary](../adr/0020-retained-reading-storage-and-proof-boundary.md) +- **Depends on:** [Durable external-action settlement](../adr/0026-durable-external-action-settlement.md) +- **Related:** [Keep authenticated reconstruction contract](https://github.com/flyingrobots/keep/blob/3bf7b9179db41e90620e6d1875c2d40222a2330b/docs/architecture/authenticated-reconstruction-contract.md) + +## Decision + +> Keep establishes physical content truth. Echo decides what that truth means +> causally. + +Keep may become an experimental physical backend for Echo. It must not replace +`echo-cas` until the identity bridge, consumer port, durable read aperture, +cross-store crash protocol, migration, and rollback gates have executable +evidence. + +Echo owns the substitution boundary: + +```text +echo-core + │ + ▼ +Echo-owned physical-content port + ├──────────────▶ existing echo-cas adapter + │ + └──────────────▶ echo-keep adapter ─────────▶ Keep +``` + +Keep must not depend on Echo, expose Echo concepts, or adopt the current weak +`BlobStore` contract as its foundational public API. Keep-specific types remain +inside the adapter and a physical-evidence envelope; they do not cross ordinary +Echo semantic APIs. + +[Echo issue #722](https://github.com/flyingrobots/echo/issues/722) owns +change-local integration sequencing and evidence gates. This page owns the +boundary regardless of which implementation phase is active. + +## Division of responsibility + +| Echo owns | Keep owns | Adapter owns | +| --------------------------------------------- | ------------------------------------ | ------------------------------------------------------ | +| Echo content identity and WSC hashes | Versioned logical `BlobId` | Witnessed Echo-ID to Keep-ID binding | +| Semantic coordinates and application meaning | `LayoutId` and physical realizations | Translation between Echo requests and Keep coordinates | +| Causal basis, law, and observer aperture | Chunking, segments, and catalogs | Quarantined output and receipt validation | +| Materialization intent and observation | Physical publication and recovery | Physical-evidence envelope for Echo | +| Retry, admissibility, and history publication | Retention generations and closure | Cross-store operation reconciliation | + +Keep receipts never grant Echo authority. Echo observations never prove Keep +presence, retention, or durability without matching Keep evidence. + +```text +Keep receipt + "These exact bytes were authenticated from this physical evidence." + +Echo observation + "Under this causal basis and law, this physical result matters this way." +``` + +One must not silently become the other. + +## Echo-owned consumer shape + +The first substitution boundary is a fallible, complete-object, +caller-output-oriented port. The following pseudostructure names semantic +obligations, not a frozen Rust ABI: + +```rust +fn reconstruct( + &self, + view: &PhysicalContentView, + target: EchoContentId, + destination: &mut dyn TransactionalContentDestination, +) -> Result; +``` + +`PhysicalContentView` is an Echo-owned opaque capability. A Keep adapter binds +it to one pinned Keep generation or immutable view. An `echo-cas` adapter may +support a weaker posture initially, but it must report unsupported evidence +rather than manufacture generation or absence claims. + +`TransactionalContentDestination` is likewise an Echo-owned semantic +capability, not an ordinary `Write` alias or a frozen ABI. It supplies private +staging plus an atomic commit operation. A destination that cannot guarantee +all-or-nothing visibility reports `CapabilityUnavailable` before +reconstruction. + +The initial port includes: + +- complete-object reconstruction; +- expected staged ingestion; +- explicit physical publication; +- authenticated success receipts; +- evidenced refusal only where the backend can prove it; +- operational failures that carry no content claim; +- quarantined output semantics. + +The initial port excludes: + +- range reads; +- an optional range argument; +- consumer-controlled Keep layouts unless required by a real consumer; +- generalized compaction controls; +- a broad retention-management trait; +- implicit backend fallback. + +Echo may retain a bounded materializing helper implemented over this port. The +helper is not Keep's foundational contract and must require an explicit byte +limit. + +## Output visibility + +An ordinary `Write` sink can fail after accepting a prefix. Keep may therefore +leave an untrusted prefix in the writer supplied by its adapter. That writer is +always the private staging writer of the transactional destination, never an +application-visible sink. + +The Echo adapter enforces: + +> No complete receipt, no application-visible bytes. + +The adapter follows one visibility protocol: + +1. Begin a destination transaction and obtain its private staging writer. +2. Reconstruct through the backend into that writer. +3. On any failure, abort; a staged prefix may remain but is never visible. +4. Seal the staging artifact and verify that the complete receipt binds the + requested target, pinned view, Keep identity, Echo identity, and exact + logical length. +5. Atomically commit the sealed artifact or return an operational failure. +6. Emit an Echo content observation only after commit succeeds. + +Commit changes the destination from its prior state directly to the complete +sealed artifact. A failed commit leaves the prior state visible and the new +artifact unpublished. Implementations may use an atomic same-filesystem rename +or return a sealed immutable handle; copying into an arbitrary live writer is +not a conforming commit. + +```text +backend reconstruction + │ + ▼ +unpublished temporary artifact + │ + ├── failure ──▶ discard; reveal nothing + │ + └── receipt ──▶ verify target, view, identities, and length; promote +``` + +Quarantine may be a bounded memory buffer for small content or a temporary +artifact for larger content. The rule must not imply unbounded whole-object +memory allocation. Crash cleanup may collect unpublished staging artifacts, +but recovery must never infer publication from their existence. + +## Decisions and failures + +Content truth and operational success are separate result planes: + +```rust +enum ReconstructionDecision { + Authenticated(ReconstructionReceipt), + Refused(ReconstructionRefusalReceipt), +} + +enum ReconstructionOperationError { + InputOutput, + ResourceLimit, + Cancellation, + CapabilityUnavailable, +} +``` + +These names are conceptual and not a frozen ABI. + +An authenticated refusal must bind enough evidence to support its proposition. +Absence is evidenced only for a known Keep `BlobId` binding and one pinned view +whose named membership-index contract certifies that it covers every published +logical `BlobId` admitted by that view. No Echo-to-Keep binding means +`CapabilityUnavailable`, not physical absence. + +A complete-view absence receipt binds: + +- the Echo identity, Keep `BlobId`, and exact pinned-view identifier; +- the versioned completeness predicate and authenticated view-root commitment; +- a target-bound non-membership witness under that root; and +- the retention guard or anchor that kept the root and witness closure valid + through verification. + +The backend may realize the witness with an authenticated ordered index, a +Merkle non-membership proof, or a complete canonical scan. The adapter must +verify the format-specific witness, completeness predicate, target, and view +root before constructing the backend-neutral refusal. Missing or unreadable +root records, index pages, retention evidence, or witness material are +operational failures. A timeout, exhausted resource limit, cancellation, or +unavailable capability likewise teaches Echo nothing about content truth. + +Echo may admit an evidenced refusal as an observation only under an Echo law +that explicitly accepts that refusal class and its physical aperture. A Rust +error alone is not a witnessed refusal. + +## Identity bridge + +Echo `BlobHash` and Keep `BlobId` are distinct typed identities. They must not +be cast, substituted, or equated because both currently use 32-byte BLAKE3 +digests. + +The adapter establishes their relation by applying both identity laws to one +exact source stream and then verifying reconstruction: + +```text + ┌── Echo identity law ──▶ EchoContentId +one exact stream ───┤ + └── Keep identity law ──▶ Keep BlobId +``` + +The resulting logical binding retains both identities and the exact logical +length. A separate realization binding names one or more lawful Keep layouts. +The persisted carrier, canonical bytes, and digest domain remain undecided. + +Keep's `stage_expected` can verify an expected Keep `BlobId`; it cannot by +itself prove an Echo-to-Keep identity relation. The adapter owns the second +identity calculation and the binding witness. + +## Semantic observation and physical evidence + +Ordinary Echo semantics and Keep realization provenance remain separate: + +```rust +struct ContentObservation { + content_id: EchoContentId, + logical_length: ByteLength, + proof_scope: CompleteObject, +} + +struct PhysicalEvidence { + keep_blob_id: KeepBlobId, + keep_generation: KeepGenerationId, + keep_layout_id: KeepLayoutId, + receipt_version: ReceiptVersion, +} +``` + +These are conceptual ownership examples, not frozen records. + +Different lawful Keep layouts for the same exact bytes produce the same +ordinary Echo content observation and different physical provenance. Echo must +not hash `LayoutId` into ordinary semantic state merely because a receipt +contains it. + +If no exact layout was requested, the Keep adapter may select any admitted +realization under a deterministic policy. If an exact layout was requested, it +must use that layout or refuse; it may not fall back to another layout. + +## Physical observer aperture + +Each Echo execution frame or work unit should hold one physical-content view: + +```text +Echo execution frame + ├── causal basis and graph view + └── PhysicalContentView + └── pinned Keep generation capability +``` + +Every physical read in the frame resolves through that same view. The adapter +must retain the underlying evidence for the view's lifetime and must not +silently advance it. + +New content produced during a frame remains staged or appears through an +explicit Echo-owned overlay. It becomes part of a later physical view only +after the causal and physical publication protocol completes. + +This prevents mixed-generation reads, mutable-`HEAD` coupling, and receipts +whose physical evidence aperture is unclear. + +## Cross-store invariant + +Keep publication and Echo WAL publication are separate durable transitions. +Their integration requires an explicit operation identity, provisional +physical retention, and recovery reconciliation. + +The governing invariant is: + +> Orphaned physical content is acceptable. A committed Echo reference to +> unavailable content is not. + +Echo must commit a non-authoritative physical-publication intent and operation +claim before Keep performs the publication effect. That intent and claim are +WAL coordination records: they cannot be read as an Echo content reference or +content observation. Keep then publishes under a durable reconciliation anchor +keyed by that operation identity. After durable reconciliation, Echo creates +the authoritative content reference and records the result as a settlement or +observation before any execution resumes on it. This is the +request-before-effect and settlement-before-resumption law from [ADR +0026](../adr/0026-durable-external-action-settlement.md). + +The reconciliation anchor is not a time-expiring lease. Keep must retain it +until an explicit recovery transition proves that long-term retention is +durable or that Echo durably settled abandonment before publication. Echo +unavailability cannot make the anchor expire. Finalization makes long-term +retention durable before releasing the provisional anchor. + +Issue #722 owns the executable crash matrix. The read port must not conceal the +cross-store state machine or convert “write Keep, then write Echo” into an +implicit protocol. + +## Fallback + +During an explicitly declared migration posture, Echo may consult the old +backend after a Keep miss only when policy records the fallback, re-verifies +the bytes, and schedules or performs an explicit backfill. + +Once Keep is authoritative, a Keep refusal is not permission to ask the old +store silently. Silent fallback would conceal the absence, corruption, and +retention failures the boundary exists to expose. + +## Current source posture + +Echo's current [`BlobStore`](../../crates/echo-cas/src/lib.rs) is synchronous +and materializing. Its `get` path collapses ordinary absence into `Option` and +returns a complete `Arc<[u8]>`. [`DiskTier`](../../crates/echo-cas/src/disk.rs) +already needs a separate fallible API, demonstrating that the existing trait +is too weak for durable physical evidence. + +The current [WSC CAS port](../../crates/warp-core/src/wsc/store.rs) returns +`Option>` and likewise collapses failure posture while requiring full +materialization. + +[ADR 0020](../adr/0020-retained-reading-storage-and-proof-boundary.md) already +requires byte identity, semantic reading identity, and proof identity to remain +distinct. This boundary extends that law to the Keep integration without +changing WSC wire identity. + +## Production adoption gate + +A production Keep backend, persisted binding format, changed WSC identity, or +permanent `echo-cas` replacement requires a separately reviewable accepted +change to Echo's named physical-content documentation after the conformance and +crash evidence exists. That change must refine or supersede this experimental +boundary explicitly and govern: + +- exact Echo and Keep identity preimages; +- binding carrier, encoding, versioning, and recovery; +- physical-view lifetime and retention; +- success and refusal receipt formats; +- output quarantine and promotion; +- cross-store crash recovery; +- toolchain and supported platforms; +- backfill, rollback, and fallback removal; +- compatibility and release posture. + +Until then, Keep is an experimental Echo backend, not Echo's sole durable +content authority. diff --git a/docs/invariants/FIXED-TIMESTEP.md b/docs/invariants/FIXED-TIMESTEP.md index f5fcc51b..5fe7a390 100644 --- a/docs/invariants/FIXED-TIMESTEP.md +++ b/docs/invariants/FIXED-TIMESTEP.md @@ -226,8 +226,9 @@ as uniform integers with no per-tick metadata. - Replay is structurally sound without recording per-tick time deltas. The quantum is a worldline parameter, not a per-entry field. - No variable-dt plumbing needs to exist in the codebase. If a future - use case demands variable dt, it requires a new ADR to relax this invariant - with explicit constraints. + use case demands variable dt, a separately reviewable durable decision must + update or supersede this invariant with explicit constraints and + relationships. ## Cross-references diff --git a/docs/plans/echo-keep-physical-cas-interop-plan.md b/docs/plans/echo-keep-physical-cas-interop-plan.md new file mode 100644 index 00000000..874f5743 --- /dev/null +++ b/docs/plans/echo-keep-physical-cas-interop-plan.md @@ -0,0 +1,11 @@ + + + +# Echo × Keep Integration Tracker + +This path does not own an implementation plan or work status. +[Echo issue #722](https://github.com/flyingrobots/echo/issues/722) owns the live +integration sequence, dependencies, evidence gates, and cutover work. The +[physical-content boundary](../architecture/echo-keep-physical-content-boundary.md) +owns current architectural truth. Git commit `fa943a6c0` preserves the original +feasibility exploration as historical source material. diff --git a/docs/topics/README.md b/docs/topics/README.md index 04493eed..bd7ceb35 100644 --- a/docs/topics/README.md +++ b/docs/topics/README.md @@ -5,8 +5,15 @@ Topics describe Echo's current architectural boundaries. They are neither historical design packets nor work trackers. Update a topic in the same change -that changes its boundary, and anchor strong claims to code, tests, specs, or an -ADR. +that changes its boundary, and anchor strong claims to code, tests, +specifications, invariants, accepted architecture, or retained historical +decisions. + +Topics are organized by named concepts, not a global chronology. When one +topic supersedes, refines, depends on, or materially relates to another +decision, state that relationship with descriptive links near the top of the +owning page. See the [documentation standards](../DOCUMENTATION_STANDARDS.md) +for the page and relationship contracts. ## Index diff --git a/tests/docs/test_adr_namespace.sh b/tests/docs/test_adr_namespace.sh deleted file mode 100755 index 4253337f..00000000 --- a/tests/docs/test_adr_namespace.sh +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: Apache-2.0 -# © James Ross Ω FLYING•ROBOTS - -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$repo_root" - -readonly legacy_adrs=( - "ADR-0001-warp-two-plane-skeleton-and-attachments.md" - "ADR-0002-warp-instances-descended-attachments.md" - "ADR-0003-Materialization-Bus.md" - "ADR-0004-No-Global-State.md" - "ADR-0005-Physics.md" - "ADR-0006-Ban-Non-Determinism.md" - "ADR-0007-BOAW-Storage.md" - "ADR-0008-Worldline-Runtime-Model.md" - "ADR-0009-Inter-Worldline-Communication.md" - "ADR-0010-observational-seek-and-administrative-rewind.md" - "ADR-0011-explicit-observation-contract.md" -) - -readonly current_adrs=( - "0012-repository-knowledge-model.md" - "0013-echo-continuum-authority-boundary.md" - "0014-generated-rule-authorship-and-footprints.md" - "0015-registry-provider-host-boundary.md" - "0016-continuum-transport-identity.md" - "0017-universal-little-endian-codec.md" - "0018-sessions-causal-posture-and-authority.md" - "0019-bunny-owns-reusable-geometry.md" - "0020-retained-reading-storage-and-proof-boundary.md" - "0021-public-optic-observation-boundary.md" - "0022-application-requested-causal-anchor-admission.md" - "0023-admitted-executable-operation-packages.md" - "0024-anchored-node-creation-from-absence.md" - "0025-scheduler-owned-executable-operation-actions.md" - "0026-durable-external-action-settlement.md" -) - -readonly current_adr_last=26 - -readonly superseded_legacy_adrs=( - "ADR-0003-Materialization-Bus.md" - "ADR-0005-Physics.md" - "ADR-0006-Ban-Non-Determinism.md" - "ADR-0009-Inter-Worldline-Communication.md" -) - -readonly partially_superseded_legacy_adrs=( - "ADR-0007-BOAW-Storage.md" - "ADR-0011-explicit-observation-contract.md" -) - -readonly collided_paths=( - "docs/adr/0001-repository-knowledge-model.md" - "docs/adr/0002-echo-continuum-authority-boundary.md" - "docs/adr/0003-generated-rule-authorship-and-footprints.md" - "docs/adr/0004-registry-provider-host-boundary.md" - "docs/adr/0005-continuum-transport-identity.md" - "docs/adr/0006-universal-little-endian-codec.md" - "docs/adr/0007-sessions-causal-posture-and-authority.md" - "docs/adr/0008-bunny-owns-reusable-geometry.md" -) - -tmp_dir="$(mktemp -d)" -trap 'rm -rf "$tmp_dir"' EXIT - -failures=0 -fail() { - echo "adr-namespace: $*" >&2 - failures=$((failures + 1)) -} - -for basename in "${legacy_adrs[@]}" "${current_adrs[@]}"; do - path="docs/adr/${basename}" - [[ -f "$path" ]] || fail "missing canonical record ${path}" -done - -for basename in "${superseded_legacy_adrs[@]}"; do - path="docs/adr/${basename}" - if ! grep -Eq -- '^- \*\*Status:\*\* Superseded$|^- Status: Superseded$' "${path}"; then - fail "superseded legacy ADR lacks superseded status: ${path}" - fi - if ! grep -F -- "(${basename})" docs/adr/README.md | grep -Fq -- '| Superseded'; then - fail "ADR index does not mark ${basename} superseded" - fi -done - -for basename in "${partially_superseded_legacy_adrs[@]}"; do - path="docs/adr/${basename}" - if ! grep -Eq -- '^- \*\*Status:\*\* Partially superseded$|^- Status: Partially superseded$' "${path}"; then - fail "partially superseded legacy ADR lacks partial status: ${path}" - fi - if ! grep -F -- "(${basename})" docs/adr/README.md | grep -Fq -- '| Partially superseded'; then - fail "ADR index does not mark ${basename} partially superseded" - fi -done - -for path in "${collided_paths[@]}"; do - if [[ -e "$path" ]]; then - fail "collided ADR path still exists: ${path}" - fi - - if git grep -Fq -- "$path" -- . \ - ':(exclude)tests/docs/test_adr_namespace.sh'; then - fail "live reference still uses collided ADR path: ${path}" - fi -done - -while IFS= read -r path; do - basename="${path##*/}" - case "$basename" in - ADR-[0-9][0-9][0-9][0-9]-*.md) - id="${basename#ADR-}" - id="${id%%-*}" - ;; - [0-9][0-9][0-9][0-9]-*.md) - id="${basename%%-*}" - ;; - *) - fail "non-canonical ADR filename: ${path}" - continue - ;; - esac - printf '%s|%s\n' "$id" "$path" >>"${tmp_dir}/records" -done < <(find docs/adr -maxdepth 1 -type f -name '*.md' ! -name README.md | sort) - -if [[ -s "${tmp_dir}/records" ]]; then - cut -d'|' -f1 "${tmp_dir}/records" | sort >"${tmp_dir}/actual-ids" - duplicate_ids="$(uniq -d "${tmp_dir}/actual-ids")" - [[ -z "$duplicate_ids" ]] || fail "duplicate ADR IDs: ${duplicate_ids//$'\n'/, }" -else - : >"${tmp_dir}/actual-ids" - fail "no canonical ADR records found" -fi - -for ((number = 1; number <= current_adr_last; number += 1)); do - printf '%04d\n' "$number" -done >"${tmp_dir}/expected-ids" - -if ! diff -u "${tmp_dir}/expected-ids" "${tmp_dir}/actual-ids" >"${tmp_dir}/id-diff"; then - cat "${tmp_dir}/id-diff" >&2 - fail "ADR IDs must be the unique contiguous range 0001 through $(printf '%04d' "$current_adr_last")" -fi - -for basename in "${legacy_adrs[@]}" "${current_adrs[@]}"; do - count="$(grep -Foc -- "(${basename})" docs/adr/README.md || true)" - [[ "$count" == "1" ]] || fail "README must link ${basename} exactly once (found ${count})" -done - -while IFS= read -r link; do - [[ -f "docs/adr/${link}" ]] || fail "README link does not resolve: docs/adr/${link}" -done < <(perl -ne 'while (/\]\(([^)#]+\.md)(?:#[^)]*)?\)/g) { print "$1\n" }' docs/adr/README.md) - -for basename in "${current_adrs[@]}"; do - id="${basename%%-*}" - path="docs/adr/${basename}" - if [[ -f "$path" ]] && ! grep -Eq -- "^# ADR ${id}:" "$path"; then - fail "H1 number does not match filename: ${path}" - fi -done - -for basename in "${legacy_adrs[@]}"; do - id="${basename#ADR-}" - id="${id%%-*}" - path="docs/adr/${basename}" - if [[ -f "$path" ]] && ! grep -Eq -- "^# ADR-${id}:" "$path"; then - fail "H1 number does not match filename: ${path}" - fi -done - -if ((failures > 0)); then - echo "adr-namespace: ${failures} violation(s)" >&2 - exit 1 -fi - -echo "adr-namespace: canonical IDs 0001 through $(printf '%04d' "$current_adr_last") are unique and indexed" diff --git a/tests/docs/test_documentation_model.sh b/tests/docs/test_documentation_model.sh new file mode 100755 index 00000000..1f801d75 --- /dev/null +++ b/tests/docs/test_documentation_model.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 +# © James Ross Ω FLYING•ROBOTS + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$repo_root" + +# This is an immutable archive inventory, not an allocator. Array order carries +# no architectural meaning; current relationships live in named documents. +readonly archived_records=( + "ADR-0001-warp-two-plane-skeleton-and-attachments.md" + "ADR-0002-warp-instances-descended-attachments.md" + "ADR-0003-Materialization-Bus.md" + "ADR-0004-No-Global-State.md" + "ADR-0005-Physics.md" + "ADR-0006-Ban-Non-Determinism.md" + "ADR-0007-BOAW-Storage.md" + "ADR-0008-Worldline-Runtime-Model.md" + "ADR-0009-Inter-Worldline-Communication.md" + "ADR-0010-observational-seek-and-administrative-rewind.md" + "ADR-0011-explicit-observation-contract.md" + "0012-repository-knowledge-model.md" + "0013-echo-continuum-authority-boundary.md" + "0014-generated-rule-authorship-and-footprints.md" + "0015-registry-provider-host-boundary.md" + "0016-continuum-transport-identity.md" + "0017-universal-little-endian-codec.md" + "0018-sessions-causal-posture-and-authority.md" + "0019-bunny-owns-reusable-geometry.md" + "0020-retained-reading-storage-and-proof-boundary.md" + "0021-public-optic-observation-boundary.md" + "0022-application-requested-causal-anchor-admission.md" + "0023-admitted-executable-operation-packages.md" + "0024-anchored-node-creation-from-absence.md" + "0025-scheduler-owned-executable-operation-actions.md" + "0026-durable-external-action-settlement.md" +) + +failures=0 +fail() { + echo "documentation-model: $*" >&2 + failures=$((failures + 1)) +} + +for required_path in \ + docs/DOCUMENTATION_STANDARDS.md \ + docs/README.md \ + docs/topics/README.md \ + docs/adr/README.md; do + [[ -f "$required_path" ]] || fail "missing policy owner ${required_path}" +done + +if ! grep -Fq -- 'closed historical archive' docs/DOCUMENTATION_STANDARDS.md; then + fail "documentation policy does not close the numbered ADR archive" +fi + +if ! grep -Fq -- 'Do not allocate a new numbered ADR.' AGENTS.md; then + fail "agent policy still permits numbered ADR allocation" +fi + +readonly root_guidance=( + README.md + GUIDE.md + CONTRIBUTING.md + ARCHITECTURE.md + ADVANCED_GUIDE.md +) + +if grep -Eiq \ + 'Architectural decisions live in|decisions live in \[ADRs|Record (only )?durable architectural decisions (as|in) ADRs|\[ADRs\].*durable decisions' \ + "${root_guidance[@]}"; then + fail "root guidance still treats the numbered ADR archive as current ownership" +fi + +readonly current_guidance=( + "${root_guidance[@]}" + docs +) + +if grep -ERiq --exclude-dir=adr \ + '(requires?|create|write|add|allocate) (a |an )?(new |separate )?ADR' \ + "${current_guidance[@]}"; then + fail "current documentation still routes a durable decision into a new ADR" +fi + +tmp_dir="$(mktemp -d)" +trap 'rm -rf "$tmp_dir"' EXIT + +printf '%s\n' "${archived_records[@]}" | sort >"${tmp_dir}/expected" +find docs/adr -maxdepth 1 -type f -name '*.md' ! -name README.md \ + -exec basename {} \; | sort >"${tmp_dir}/actual" + +if ! diff -u "${tmp_dir}/expected" "${tmp_dir}/actual" >"${tmp_dir}/archive-diff"; then + cat "${tmp_dir}/archive-diff" >&2 + fail "numbered ADR archive differs from its closed inventory" +fi + +for basename in "${archived_records[@]}"; do + count="$(grep -Foc -- "(${basename})" docs/adr/README.md || true)" + [[ "$count" == "1" ]] || \ + fail "archive index must link ${basename} exactly once (found ${count})" + + case "$basename" in + ADR-[0-9][0-9][0-9][0-9]-*.md) + id="${basename#ADR-}" + id="${id%%-*}" + expected_heading="# ADR-${id}:" + ;; + [0-9][0-9][0-9][0-9]-*.md) + id="${basename%%-*}" + expected_heading="# ADR ${id}:" + ;; + *) + fail "unexpected historical filename shape: ${basename}" + continue + ;; + esac + + if ! grep -Fq -- "$expected_heading" "docs/adr/${basename}"; then + fail "historical heading does not match filename: docs/adr/${basename}" + fi +done + +while IFS= read -r link; do + [[ -f "docs/adr/${link}" ]] || \ + fail "archive index link does not resolve: docs/adr/${link}" +done < <(perl -ne 'while (/\]\(([^)#]+\.md)(?:#[^)]*)?\)/g) { print "$1\n" }' docs/adr/README.md) + +if ! grep -Eq -- '^- \*\*Status:\*\* Superseded$' \ + docs/adr/0012-repository-knowledge-model.md; then + fail "historical repository knowledge model is not marked superseded" +fi + +if ! grep -F -- '(0012-repository-knowledge-model.md)' docs/adr/README.md | \ + grep -Fq -- '| Superseded'; then + fail "archive index does not mark repository knowledge model superseded" +fi + +if ((failures > 0)); then + echo "documentation-model: ${failures} violation(s)" >&2 + exit 1 +fi + +echo "documentation-model: semantic policy current; numbered ADR archive closed and indexed" diff --git a/tests/docs/test_knowledge_model.sh b/tests/docs/test_knowledge_model.sh index 02afc3eb..029b5273 100644 --- a/tests/docs/test_knowledge_model.sh +++ b/tests/docs/test_knowledge_model.sh @@ -8,6 +8,7 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "${repo_root}" readonly required_docs=( + "docs/DOCUMENTATION_STANDARDS.md" "docs/adr/README.md" "docs/adr/0012-repository-knowledge-model.md" "docs/adr/0013-echo-continuum-authority-boundary.md"