diff --git a/.adr-kit.yaml b/.adr-kit.yaml index de0b2e6..b11c744 100644 --- a/.adr-kit.yaml +++ b/.adr-kit.yaml @@ -1,5 +1,5 @@ # ADR kit configuration for repository-level decision governance. -adr_directory: docs/adr +adr_directory: adr filename_pattern: "ADR-[0-9]{4}-[a-z0-9-]+\.md" statuses: - Proposed diff --git a/.github/workflows/adr-governance.yml b/.github/workflows/adr-governance.yml index e7911b4..0540483 100644 --- a/.github/workflows/adr-governance.yml +++ b/.github/workflows/adr-governance.yml @@ -1,20 +1,25 @@ name: ADR Governance +permissions: + contents: read + on: pull_request: paths: - - 'docs/adr/**' + - 'adr/**' - '.adr-kit.yaml' - 'scripts/adr-governance.py' + - 'scripts/tests/**' - '.github/workflows/adr-governance.yml' push: branches: - main - master paths: - - 'docs/adr/**' + - 'adr/**' - '.adr-kit.yaml' - 'scripts/adr-governance.py' + - 'scripts/tests/**' - '.github/workflows/adr-governance.yml' jobs: @@ -25,5 +30,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false + - name: Run ADR governance regression tests + run: python3 -I -m unittest discover -s scripts/tests -p 'test_adr_governance.py' - name: Run ADR governance checks - run: python3 scripts/adr-governance.py + env: + GITHUB_EVENT_BEFORE: ${{ github.event.before }} + run: python3 -I scripts/adr-governance.py diff --git a/CLAUDE.md b/CLAUDE.md index e056a2a..e6920ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,10 @@ This repo documents the Autonomi 2.0 network. See `repo-registry.yml` for which - ant-quic (excluded — older ancestor/fork; use saorsa-transport instead) - Any repo not listed in the repo registry +## ADR governance + +Before changing architecture, protocols, storage formats, cryptography, network behaviour, public APIs, data models, or operational invariants, inspect `adr/`. Use `adr/TEMPLATE.md` for new or changed architectural decisions. Never edit an Accepted ADR; create a superseding ADR instead. Never mark an ADR Accepted autonomously. Validate ADR work with `python3 -I scripts/adr-governance.py` and the repository's ADR governance tests. + ## Audience and objectives ### Who these docs are for diff --git a/docs/adr/ADR-0001-adopt-architecture-decision-records.md b/adr/ADR-0001-adopt-architecture-decision-records.md similarity index 100% rename from docs/adr/ADR-0001-adopt-architecture-decision-records.md rename to adr/ADR-0001-adopt-architecture-decision-records.md diff --git a/adr/ADR-0002-adrs-live-at-repo-root-not-docs.md b/adr/ADR-0002-adrs-live-at-repo-root-not-docs.md new file mode 100644 index 0000000..0e98832 --- /dev/null +++ b/adr/ADR-0002-adrs-live-at-repo-root-not-docs.md @@ -0,0 +1,69 @@ +# ADR-0002: Architecture Decision Records live at repo-root `/adr`, not `docs/adr` + +- **Status:** Accepted +- **Acceptance:** Prospective — confirmed by Jim Collinson as decision owner during review of PR 73. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Reviewers:** Jim Collinson +- **Supersedes:** ADR-0001 (location clause only; ADR governance remains in force) +- **Superseded by:** none +- **Related:** ADR-0001 (adopt ADRs — its governance decision remains in force while this ADR supersedes its location clause), ADR-0008 (GitBook platform — why `docs/` is the published product), ADR-0012 (the developer skill kept out of `docs/` for the same reason); `.gitbook.yaml` (`root: ./docs/`), `.adr-kit.yaml` (`adr_directory`), `scripts/adr-governance.py` (`ADR_DIR`), `.github/workflows/adr-governance.yml` + +> Current (prospective) decision — made while adopting ADRs in this repo, not a retrospective reconstruction. It re-points the team standard's default ADR location for this repo's specific circumstances; the standard's mechanics are untouched. + +## Context + +The team ADR standard (ADR-0001, shipped via PR #56) defaults ADRs to `docs/adr/` — correct in a typical repo where `docs/` holds internal documentation. This repo is different: `.gitbook.yaml` sets `root: ./docs/`, so the **entire `docs/` tree is the published developer-documentation product**, synced to GitBook at `docs.autonomi.com/developers`. Placing ADRs under `docs/adr/` puts internal architecture records inside that published product tree. + +## Decision Drivers + +- Internal architecture records must not publish to, or surface on, the public developer-docs site — nav, search, or the auto-generated `llms.txt`. +- ADRs must stay out of the docs' machinery: the upstream-sweep scanner walks `docs/**/*.md` for verification blocks, and doc tooling (Diátaxis templates, terminology lint, link-checking) globs the same tree. +- A contributor browsing `docs/` as "the documentation" should not find architecture decisions mixed in with product pages. +- The team standard should still be honoured — its own `.adr-kit.yaml` `adr_directory` knob exists precisely so a repo can relocate the instance. +- Consistency with the existing decision to keep the developer skill at `skills/`, not `docs/skills/` (ADR-0012), for exactly this reason. + +## Considered Options + +1. **Keep ADRs at `docs/adr/`** (the team default). Rejected here: `docs/` is the published GitBook root, so ADRs would sit inside the product tree, risk leaking to the public site / `llms.txt`, and be swept by doc tooling. +2. **Keep `docs/adr/` but exclude it from GitBook** (SUMMARY omission or a `.gitbook.yaml` rule). Rejected: fragile — `root: ./docs/` keeps the files in scope, so it relies on GitBook silently ignoring unlisted files, and the conceptual mixing remains. +3. **Relocate ADRs to repo-root `/adr`.** Chosen. (`planning/adr/` was also considered and rejected: ADRs are formal, ratified decisions, distinct from the loose-thinking design docs that live in `planning/`.) + +## Decision + +Architecture Decision Records live at repo-root **`/adr`** in this repo. This is a repo-local *configuration* of the team standard, not a fork of it: + +- `.adr-kit.yaml` → `adr_directory: adr`. +- `scripts/adr-governance.py` → `ADR_DIR = Path("adr")`. +- `.github/workflows/adr-governance.yml` → path filters point at `adr/**`. +- Move the standard's own files (`TEMPLATE.md`, `TOOLING.md`, `README.md`, and `ADR-0001`) from `docs/adr/` to `/adr`. + +`/adr` is chosen over `planning/adr/` because ADRs are formal, ratified decisions — distinct from the loose-thinking design material (`implementation-plan.md`, `verification-workflow.md`, `routines/`, `sweeps/`) that already lives in `planning/`. + +## Consequences + +### Positive + +- Architecture records stay entirely out of the published product tree and its tooling. +- Mirrors the skill's out-of-`docs/` placement (ADR-0012): one consistent rule — product content in `docs/`, internal artifacts at the repo root. +- Still fully governed — the gate validates ADRs at `/adr` once `ADR_DIR` is re-pointed. + +### Negative / Trade-offs + +- Diverges from the team-standard default (`docs/adr`), so a contributor arriving from another WithAutonomi/Saorsa repo must notice the repo-local location — this ADR is the signpost, and `docs/adr/` is left empty/removed so nothing looks half-moved. +- Requires the three config points (`adr_directory`, `ADR_DIR`, workflow paths) to stay in agreement. The governance script fails closed when they diverge, but the workflow must still become a required check to prevent bypass at merge time. + +### Neutral / Operational + +- The change is small and repo-local; the shared standard's mechanics (template, lifecycle, immutability) are unchanged — only the configurable location differs. +- Decided as too minor to route through the standard's owner; recorded here so the deviation is explicit and auditable. + +## Validation + +- `scripts/adr-governance.py` (with `ADR_DIR=adr`) is the validation entrypoint for the `/adr` set; `docs/adr/` no longer exists. Making its workflow a required check is tracked as an implementation-conformance gap. +- Spot-check after the move: no ADR path appears in the GitBook nav, site search, or `llms.txt`. +- Review trigger: a change to `.gitbook.yaml`'s `root`, or a move to a different docs platform, may reopen the location question. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0003-verification-block-source-of-truth.md b/adr/ADR-0003-verification-block-source-of-truth.md new file mode 100644 index 0000000..717afa2 --- /dev/null +++ b/adr/ADR-0003-verification-block-source-of-truth.md @@ -0,0 +1,73 @@ +# ADR-0003: Verification-block source-of-truth model for developer docs + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0001 (Adopt ADRs); `planning/verification-workflow.md`; `planning/implementation-plan.md` §8; original build 2026-04-02 → 2026-04-22 (repo baseline `a1110ea`, "Reverify against current merged truth" `3eaf653`) + +> Retrospective ADR. The decision was taken and implemented across April 2026; this record captures it after the fact so the reasoning is preserved. Evidence is cited in **Related** and **Validation**. + +## Context + +The Autonomi developer docs describe fast-moving upstream code (`ant-sdk`, `ant-client`, `ant-node`, `ant-protocol`, `saorsa-*`, and others). Documentation that drifts from the code is worse than no documentation: it silently misleads developers. The repo needed a way to state, for every documented surface, *exactly which upstream commits that surface was verified against*, so drift is detectable mechanically rather than by re-reading everything by eye. + +A second tension: docs must describe the latest merged default-branch source truth, not the expected future product shape. Installation, download, package, and version surfaces have an additional constraint: they must describe artifacts a developer can actually install or download, even when merged source is newer. Without explicit rules, pages tend to drift toward aspirational descriptions of unmerged branches or present unreleased artifacts as available. But a launch-hardening pass needs the opposite of following moving branches — the ability to pin a set of pages to a fixed release target. + +## Decision Drivers + +- Drift must be **detectable by a machine**, not only by human review. +- Every documented surface must be **traceable to exact upstream commit SHAs**. +- Docs must default to **current merged default-branch source truth**, never future state; installation, download, package, and version surfaces must additionally stay within installable or released truth. +- A launch/release pass must be able to **pin** pages to fixed refs without inverting the day-to-day default. +- Provenance metadata must not leak into rendered prose. + +## Considered Options + +1. **No embedded provenance** — track freshness out-of-band (a spreadsheet, issue tracker, or reviewer memory). Rejected: unmaintainable, not machine-checkable, drifts immediately. +2. **Page-level "last reviewed" date only.** Rejected: a date says *when* someone looked, not *what upstream state* was true; it cannot detect that upstream moved. +3. **Per-surface verification records pinning `(source_repo, source_ref, source_commit, verified_date, verification_mode)`, with two explicit modes.** Chosen. + +## Decision + +We will embed **verification metadata** in the docs and skill sources as the single machine-readable record of what each documented surface was verified against. + +- Every rendered documentation surface carries one or more `` blocks in `docs/**/*.md`. Each block records the full declared schema: `source_repo`, `source_ref`, `source_commit` (an exact SHA), `verified_date`, and `verification_mode`. +- The skill uses equivalent YAML metadata rather than documentation comment blocks. `skills/start/SKILL.md` frontmatter carries `verified_date`, `verification_mode`, and a per-repo `verified_commits` SHA map. The runtime `skills/start/version.json` manifest mirrors only the fields needed at runtime or for external inspection, including `version`, `verification_mode`, and `verified_commits`; it deliberately omits `verified_date`, which records when the skill content was reviewed. +- Two verification modes are defined, and they are an **invariant of the model**: + - **`current-merged-truth`** (default): verify against the latest merged commit on the upstream default branch at audit time. Unmerged branches and PRs are out of scope. + - **`target-manifest`**: launch/release-hardening mode. Verify against refs pinned in `target-manifest.yml`; **do not** follow moving default branches for pages in this mode. +- A page presented as verified must never carry `source_commit: TBD`. +- Provenance/verification language lives only in the comment blocks and metadata — never in rendered body text. + +This model is the foundation the automation in ADR-0004 through ADR-0007 operates on: the scanner, the guards, and the routine all read and write these blocks and nothing else about freshness. + +## Consequences + +### Positive + +- Drift becomes a deterministic diff: compare each `source_commit` against upstream HEAD (see ADR-0004). No human re-reading required to *detect* staleness. +- Every verification record is auditable to exact SHAs, so a reviewer can reproduce the evidence for that documented surface. +- The default keeps docs aligned with merged source truth, while install and release surfaces remain honest about artifacts developers can obtain; the `target-manifest` escape hatch supports launch hardening without weakening the default. + +### Negative / Trade-offs + +- Authors and automation must keep the blocks accurate; a stale-but-present block is a false "verified" signal, so block integrity itself must be guarded (ADR-0006) and reachability-checked (ADR-0007). +- Multi-repo pages carry multiple blocks, adding markup weight to sources. + +### Neutral / Operational + +- The rendered-documentation block schema (`source_repo`, `source_ref`, `source_commit`, `verified_date`, `verification_mode`) and the skill metadata split described above become stable contracts that downstream tools depend on; changing either is itself an architectural change requiring a superseding ADR. +- `target-manifest` blocks are deliberately excluded from automated bumping (see ADR-0007) so a launch pin is never silently overwritten. + +## Validation + +- The scanner (`scripts/sweep_poll.py`) parses every block and fails closed on a malformed block (missing `source_repo`/`source_ref`/`source_commit`/`verification_mode`) or an unknown mode — so a schema violation is caught, not ignored. +- `sweep-sha-reachability` (CI) confirms every recorded SHA is reachable on the declared upstream ref. +- Review trigger: any change to the block schema, the mode set, or the "no `TBD`" rule must supersede this ADR rather than edit it. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0004-deterministic-scanner-model-audit-loop.md b/adr/ADR-0004-deterministic-scanner-model-audit-loop.md new file mode 100644 index 0000000..51d1025 --- /dev/null +++ b/adr/ADR-0004-deterministic-scanner-model-audit-loop.md @@ -0,0 +1,76 @@ +# ADR-0004: Tiered execution — deterministic detection, efficient-model metadata sweeps, frontier-model prose + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification blocks); `scripts/sweep_poll.py`; `planning/routines/upstream-sweep.md` §"Opus audit/write/verify loop"; build commit `a6bce6be` (2026-05-04); hardening commits `ddeb45a` (2026-05-09), `8c541eb` (2026-05-09), `f447f78`/`1ce7ef9`/`8348c72` (2026-05-11, session `019cz1M7pXuXFQykMZxfdXHm`) + +> Retrospective ADR. The decision was made during the implementation introduced on 2026-05-04 and hardened through 2026-05-11. Current implementation gaps are tracked separately. + +## Context + +The pipeline actually contains three kinds of work, each with a different cost/capability profile, and treating them as one wastes money and trust: + +1. **Drift detection** — for each verification block, resolve upstream HEAD and compare SHAs. A pure, repeatable computation. No model judgement at all. +2. **Metadata-only sweeps** — when a drifted upstream change has no developer-facing impact, the work is to audit the diff, confirm it is genuinely metadata-only, and re-stamp the SHA (see ADR-0006's `claude/sweep-*` track). This needs light judgement, but not deep source comprehension or authoring. +3. **Prose sweeps** — when a change alters real behaviour, the work is to read the upstream diff and source, judge developer-facing impact, and write correct, voice-consistent documentation (ADR-0006's `claude/prose-*` track). This needs a frontier model. + +Conflating these produces a system that is neither trustworthy (an LLM "detecting" drift is non-reproducible) nor efficient (running frontier-model inference to re-stamp a SHA that changed nothing a developer sees is pure cost). We also learned during hardening that a "detector" which silently swallows edge cases (missing token, an org that refuses fine-grained PATs, a null value in a map) is dangerous: it reports "no drift" when it actually failed to look. The detector therefore has to be strict and fail-closed, which is far easier to guarantee in deterministic code than in a model prompt. + +## Decision Drivers + +- Drift detection must be **deterministic and reproducible** — the same inputs give the same report every run. +- Detection must **fail closed**, never fail silent, on auth/network/parse/shape errors. +- Prose audit, rewriting, and verification need **frontier-model judgement** and cannot be scripted. +- Cheaper work must not pay frontier-model prices: **match the execution tier to the job**. +- Each tier should be independently testable and independently hardenable. + +## Considered Options + +1. **Model does everything**, including "notice what looks out of date." Rejected: non-reproducible, no audit trail, easy to hallucinate freshness, expensive to run over the whole repo. +2. **Script does everything**, including regenerating prose via templates. Rejected: cannot judge developer-facing impact or write correct, voice-consistent prose from an upstream diff. +3. **One frontier model for all model work** (detection stays scripted, but a single frontier model handles both metadata sweeps and prose). This is what the routine ran at build time. Retained as workable, but rejected as the target: it pays frontier prices to re-stamp SHAs that change nothing developer-facing. +4. **Tiered execution: deterministic detection, an efficient model for metadata sweeps, a frontier model for prose.** Chosen. + +## Decision + +We will run the pipeline in **three tiers, matching cost and capability to the work**: + +- **Detection — `scripts/sweep_poll.py`, deterministic, no model.** It walks every verification block in `docs/**/*.md`, `version.json`, and `SKILL.md` frontmatter; resolves `(repo, ref)` via `repo-registry.yml` + GitHub default branch; and emits a per-record JSON drift report. It **fails closed** on auth, network, parse, shape, and unknown-mode errors (see ADR-0007), deliberately skips `target-manifest` blocks into a separate array, and never writes docs. Its report is a **candidate list, not a directive** — SHAs are bumped only after a per-page audit succeeds. +- **Metadata-only sweeps — an efficient model is sufficient.** Auditing a diff, confirming it carries no developer-facing change, and re-stamping the SHA (the `claude/sweep-*` track, ADR-0006) does not require a frontier model; an efficient model (Sonnet-class) is an appropriate floor. +- **Prose sweeps — a frontier model is required.** Reading upstream source, judging developer-facing impact, and writing/verifying documentation prose (the `claude/prose-*` track, ADR-0006) requires a frontier model, provider-agnostic (a Claude Opus-class or GPT-5.x-class model). There is no subagent layer for this work. +- **Model names are deliberately de-versioned in this decision.** The requirement is the *tier* ("efficient" vs "frontier"), not a specific model. For provenance: at build time the prose floor was specified as Opus 4.7+ (or GPT-5.5/5.6+), raised to Opus 4.8+ on 2026-06-18 (session `011PdDcd7X5C1aSz7sYqjSnz`); the current frontier is a later generation again. The floor should track "the current frontier," not a frozen version string. + +## Consequences + +### Positive + +- Detection is reproducible and cheap; a human can re-run the scanner and get the identical drift report. +- Fail-closed detection means "no drift" genuinely means "looked, found nothing," not "failed to look." +- Prose quality is owned by a frontier model competent to write it, gated by audit and verification. +- Cost tracks value: the common case (no-drift and metadata-only re-stamps) never pays frontier-model prices; frontier inference is spent only where prose is actually rewritten. +- The tiers harden independently — most of the May 9–11 fixes were scanner shape/HTTP robustness with zero change to the prose loop. + +### Negative / Trade-offs + +- Frontier-model prose is the expensive tier, with cost and availability implications on any run that rewrites prose. +- Tiering adds routing complexity: the metadata-vs-prose classification must be reliable, or a prose-impacting change could be handled by the cheaper tier. Classification is the routine's job (see the page-batching rule) and ambiguous records fail closed to human review (ADR-0007), which bounds this risk. +- Multiple artifacts to maintain (scanner code + routine prompt), and potentially more than one model configured. + +### Neutral / Operational + +- The scanner's JSON output schema (`status`, `records`, `errors`, `target_manifest_skipped`, `notices`) is a contract consumed by the routine; additive fields are safe, shape changes are not. +- Which concrete models fill the "efficient" and "frontier" tiers is routine config (see ADR-0005), not a repo change; only the *tier requirement* is recorded in the prompt and policy. + +## Validation + +- Re-running `scripts/sweep_poll.py` must return `status: "ok"` and a reproducible `records` list; the routine re-runs it as a post-write check. +- Scanner unit behaviour is exercised by the shape/type guards added in `ddeb45a` and `8c541eb` (non-string SHA, null/empty `verified_commits`, empty frontmatter all fail closed). +- Review trigger: any move of audit/verification responsibility into the scanner, or of detection into the model, supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0005-hosted-scheduled-routine-over-repository-dispatch.md b/adr/ADR-0005-hosted-scheduled-routine-over-repository-dispatch.md new file mode 100644 index 0000000..fae4b50 --- /dev/null +++ b/adr/ADR-0005-hosted-scheduled-routine-over-repository-dispatch.md @@ -0,0 +1,78 @@ +# ADR-0005: Subscription-billed daily scheduled routine as the interim trigger, over repository_dispatch webhooks and API-billed CI + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0004 (execution tiers); `planning/routines/upstream-sweep.md` §"Trigger shape" / §"Alignment with implementation-plan.md Section 8"; `planning/implementation-plan.md` §8; build commit `a6bce6be` (2026-05-04) + +> Retrospective ADR. The scheduled-run architecture shipped 2026-05-04; execution-venue and billing details are folded in here. + +## Context + +`planning/implementation-plan.md` §8 describes the eventual "push" architecture: upstream repos install a `notify-docs.yml` workflow that fires a `repository_dispatch` at the docs repo on merge, which collates per-event and opens targeted PRs. That design depends on write access to every upstream repo to install the notifier — a cross-org dependency we do not control and cannot land quickly. + +We needed drift handling working **now**, across upstream repos in orgs we do not own (e.g. `saorsa-labs`), without waiting on notifier installation anywhere. Two further constraints shaped the answer: the prose tier needs a frontier model (ADR-0004), and **there is no budget for API credits** — the automation has to run on the frontier-model access the team already pays for through ordinary subscriptions, not metered API billing. + +## Decision Drivers + +- Must work **without** installing anything in upstream repos. +- Must run **unattended on a schedule** with a bounded worst-case drift latency. +- Must run the prose tier on a **frontier model** (ADR-0004), which GitHub Actions runners cannot host. +- **No API-credit budget:** frontier-model work must run against **pre-existing subscription-backed access**, not metered API billing. +- Must be **forward-compatible** with the eventual `repository_dispatch` arm, not a throwaway. +- Prompt/behaviour must stay **version-controlled and reviewable**, not buried in a hosting UI. + +## Considered Options + +1. **Wait for `repository_dispatch` / `notify-docs.yml`.** Rejected as the *only* path: blocks all drift handling on cross-org installs we do not control. +2. **GitHub Actions cron, or the GitHub-installed Claude app, running the model in CI.** Rejected: Actions runners cannot host the frontier model, and the GitHub-app path is **API-billed** — materially more expensive, with no budget for it. It also couples the run to CI infrastructure and its secret/quota constraints. +3. **Subscription-billed hosted scheduled agent routine that polls daily**, calls the deterministic scanner, and runs the tiered model work under a team member's subscription-backed access. Chosen as the interim arm, sitting alongside (not replacing) the eventual push arm. + +## Decision + +We will run the sweep as a **scheduled agent routine that polls once per day using a team member's subscription-backed access** rather than metered API credits. + +- **Cadence:** daily, off-peak (≈09:00 UTC), comfortably above the one-hour minimum interval for hosted routine schedules. Worst-case drift latency is ~24h; a missed slot is recovered by the next day's run. +- **Execution venue:** the sweep runs as a **Claude Code Remote routine** in a managed hosted environment under **Jim's Claude subscription/account**. It is scheduled remotely and executes in an ephemeral environment with a fresh repository clone. Durable outputs are written to GitHub as branches, pull requests, issues, and comments. GitHub access uses credentials available in the hosted routine environment, and `ANTHROPIC_API_KEY` is deliberately left **unset** so runs never silently fall through to API billing. +- **Billing model:** model execution uses subscription-backed access rather than metered API billing. This was chosen because the required frontier-model capability was available through an existing subscription, while viable automation alternatives at implementation time either required raw API expenditure or were not yet available. +- **Model tier is per ADR-0004:** the frontier model is spent only on prose; metadata sweeps can run on an efficient model. Concrete models are routine config, not repo config. +- **Prompt by reference, not paste:** the routine UI carries only a ~3-line bootstrap instructing the agent to read `planning/routines/upstream-sweep-prompt.md` from the cloned repo and follow it exactly. Prompt changes therefore ship via normal PR review — no routine edit. +- **Positioned as interim, not terminal:** this is the hosted-scheduled, polling equivalent of implementation-plan §8 Tiers 1+2. When `notify-docs.yml` rolls out upstream, the same routine can grow a webhook-receiver arm without invalidating the daily-poll v1. + +## Consequences + +### Positive + +- Drift handling works immediately across upstreams in orgs we do not control, with zero upstream installation. +- Frontier-model prose runs at **no metered API cost** — it draws on subscription access the team already holds, which is what makes a daily cadence affordable. +- The frontier tier runs in a venue that supports it. +- Behaviour is version-controlled (prompt-by-reference), so the routine's logic is reviewable and auditable in git. +- No dependency on credentials stored on Jim's local machine; GitHub credentials live in the hosted routine environment. + +### Negative / Trade-offs + +- Up to ~24h latency versus a push architecture's near-real-time reaction. +- Polling re-scans the whole repo daily even when nothing changed (cheap, but not free). +- **Single-account dependency (bus factor):** the present routine depends on Jim's Claude subscription/account and its cloud-side routine configuration. If the account, schedule, selected model, or credentials become unavailable, runs stop or fail closed. Moving to shared ownership or diversifying across additional subscription-backed providers remains unresolved. +- Execution-venue config (schedule, model tiers, any secrets) lives **outside** the repo in the hosting UI, so it cannot be fully captured in version control — only the behaviour (the prompt) is. +- Dependence on hosted-routine availability and plan quota (a missed slot degrades to next-day recovery). + +### Neutral / Operational + +- Concrete model choices for the efficient/frontier tiers (ADR-0004) and the schedule are set in the routine config, not the repo; only the *requirement* is recorded in the prompt/policy. +- Credential precedence for the scanner's reads is documented in `upstream-sweep.md` (`GITHUB_TOKEN` → `gh auth token` → anonymous REST → `git ls-remote`). The hosted routine may use a configured token or connected app credentials; writes remain scoped to the docs repo. +- This decision does not bind the routine permanently to one provider. Future subscription-backed provider lanes, independent models, or multimodal capabilities can be considered without abandoning the core subscription-over-metered-API billing decision. Concrete providers and model versions remain operational configuration. + +## Validation + +- First-run check: inspect the hosted routine's run history after the scheduled trigger. A completed run may exit silently when it finds no drift; PRs or manual-review issues appear only when the outcome requires them. A missing run indicates an account or schedule problem, while a failed run or failure issue carries the execution diagnostic. +- Billing check: `ANTHROPIC_API_KEY` must remain unset in the routine environment so runs stay on subscription billing; a run that switches to API billing is a misconfiguration. +- Forward-compatibility check: the eventual `notify-docs.yml` arm must be addable without changing the daily-poll behaviour; if it forces a rewrite, revisit this ADR. +- Review trigger: adopting the `repository_dispatch` push arm as primary or moving from subscription-backed execution to metered API billing supersedes this ADR. Adding subscription-backed provider lanes or shared ownership does not supersede the core decision. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0006-separate-metadata-and-prose-update-tracks.md b/adr/ADR-0006-separate-metadata-and-prose-update-tracks.md new file mode 100644 index 0000000..ec0ad52 --- /dev/null +++ b/adr/ADR-0006-separate-metadata-and-prose-update-tracks.md @@ -0,0 +1,74 @@ +# ADR-0006: Separate metadata and prose update tracks + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification blocks), ADR-0004 (execution tiers), ADR-0009 (review and automation boundary), ADR-0010 (authoring contract), ADR-0014 (skill maintenance); `planning/routines/upstream-sweep.md`; the sweep guard and SHA-reachability workflows; commits `e5869c5` (2026-05-05), `7b23c92` (2026-05-08), `ddeb45a` (2026-05-09) + +> Retrospective ADR. The metadata-only track shipped 2026-05-04; the prose-impacting track and its enforcement were added 2026-05-05 and hardened through 2026-05-09. + +## Context + +Upstream drift has two materially different forms. Some changes leave developer-facing content accurate and require only verification provenance to move. Other changes alter a documented surface and require rendered prose or examples to change. + +Combining both forms in one undifferentiated update makes low-risk provenance refreshes expensive to review and lets higher-risk prose changes hide in metadata noise. Because automated routines author these updates, the distinction cannot depend on author discipline alone. + +## Decision Drivers + +- Metadata-only refreshes must remain quick to verify without weakening confidence in their provenance. +- Developer-facing changes require stronger review than mechanical provenance movement. +- Automated updates need mechanically enforced boundaries between the two change classes. +- Skill content, release metadata, and verification provenance must describe one coherent state. +- Any changed verification commit must remain reachable from its declared source. + +## Considered Options + +1. **Use one update track for all drift.** Rejected: reviewers cannot distinguish mechanical provenance movement from developer-facing changes without reading every update in full. +2. **Classify updates only through labels or author declarations.** Rejected: declarations can be missing or inaccurate and do not prevent one change class from crossing into the other. +3. **Use mutually exclusive, mechanically enforced metadata-only and prose-impacting tracks.** Chosen. + +## Decision + +Routine updates use two mutually exclusive tracks: + +- The **metadata-only track** may refresh verification provenance but must not change rendered prose or other developer-facing content. +- The **prose-impacting track** carries changes to rendered content and receives stronger review treatment appropriate to developer-facing claims. + +Each track has a mechanically enforced change envelope. The enforcement must reject changes that cross the selected track's boundary and must verify that changed source commit identifiers are reachable from their declared sources. + +Skill maintenance follows the same separation. A skill body change and its release metadata form one coherent release state. A pure verification refresh may move provenance without changing the skill's content release version or release history. + +## Consequences + +### Positive + +- Review effort matches the risk of the update. +- Prose cannot hide inside a metadata-only refresh. +- Mechanical enforcement applies consistently regardless of whether a routine or a person authors the change. +- Skill content, release identity, and provenance cannot silently describe different states. +- Changed verification commits are checked against source history. + +### Negative / Trade-offs + +- Two update envelopes and a reachability check must be maintained. +- A single upstream change can produce separate updates when it has both metadata-only and prose impact across different artifacts. +- Classification still requires judgement before an update enters either envelope. + +### Neutral / Operational + +- Branch conventions, workflow names, allowed paths, batching rules, review gestures, and CI implementation details are mutable mechanics documented in `planning/routines/upstream-sweep.md` and the guard workflows. +- The historical commits linked above preserve provenance for the original implementation and its hardening. + +## Validation + +- Automated checks reject rendered-content changes from the metadata-only track and reject incoherent skill content, release, or provenance state. +- Changed verification commits are checked for reachability from their declared sources. +- Prose-impacting updates receive the stronger review treatment defined by the current review workflow. +- Review trigger: changing the mutually exclusive track model, weakening mechanical enforcement, removing stronger prose review, or allowing incoherent skill release state requires a superseding ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0007-fail-closed-and-github-artifact-topology.md b/adr/ADR-0007-fail-closed-and-github-artifact-topology.md new file mode 100644 index 0000000..499c07b --- /dev/null +++ b/adr/ADR-0007-fail-closed-and-github-artifact-topology.md @@ -0,0 +1,74 @@ +# ADR-0007: Fail-closed operation and the GitHub artifact topology + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0004 (scanner/model split), ADR-0006 (PR envelopes); `scripts/sweep_poll.py`; `planning/routines/upstream-sweep.md` §"GitHub artifacts the routine produces" / §"Fail-closed semantics" / §"Open-PR collision handling" / §"Manual-review issue de-duplication"; commits `a6bce6be` (2026-05-04), `f447f78`/`1ce7ef9`/`8348c72` (2026-05-11, session `019cz1M7pXuXFQykMZxfdXHm`) + +> Retrospective ADR. Shipped 2026-05-04 and materially hardened on 2026-05-11 after live HEAD-resolution failures against `saorsa-labs` repos. + +## Context + +An unattended daily routine (ADR-0005) with write access to the docs repo will, over time, hit every failure mode: rate limits, an org that refuses fine-grained PATs, a garbage-collected SHA, a force-push, a malformed block, a network blip. Two failure behaviours are unacceptable: (1) **failing silent** — reporting "no drift" when it actually failed to look, which lets real drift rot undetected; and (2) **flooding** — opening duplicate PRs/issues every day for the same unresolved condition. We also had a concrete incident: the scanner could not read public `saorsa-labs` repos via the authenticated REST path (org-level FG-PAT refusal) *or* the anonymous retry (shared-sandbox-IP quota), and earlier diagnostics misreported public repos as private. + +The routine needed a disciplined, predictable relationship between run outcomes and the GitHub artifacts it produces, and a strict fail-closed posture with diagnostics good enough to triage from the issue body alone. + +## Decision Drivers + +- Never fail silent: an error must **abort and surface**, never masquerade as "no drift." +- Never flood routine output: open-PR collisions and unresolved per-record manual reviews must **deduplicate**, not re-open daily. Whole-run failure issues are the deliberate exception because each failed run is a separate observation requiring triage. +- Issues are for **things a human must act on**; healthy runs should leave no noise. +- Diagnostics must distinguish rate-limit vs policy-refusal vs transient outage **from the issue body alone**. +- Reads must degrade through fallbacks before giving up; writes stay scoped to the docs repo only. + +## Considered Options + +1. **Fail-open / best-effort scanning** (skip unreadable repos, continue). Rejected: this is exactly the silent-failure mode; the skip path was fail-open and was removed in `1ce7ef9`. +2. **Emit an artifact for every run** (including no-drift status comments). Rejected: daily noise trains reviewers to ignore the routine; healthy runs should be silent. +3. **Strict fail-closed scanner + a fixed outcome→artifact topology with fingerprint dedup and serial-cadence collision handling.** Chosen. + +## Decision + +We will operate the routine **fail-closed**, with a fixed mapping from run outcome to GitHub artifact. + +- **Scanner fail-closed (whole run aborts):** GitHub 4xx/5xx after fallbacks exhausted, network timeout, malformed verification block, unknown/missing `verification_mode`, unparseable registry/frontmatter, or unknown `source_repo` → the routine opens **one fresh `upstream-sweep-failure` issue** carrying the JSON diagnostic (response body, rate-limit state, `x-github-request-id`, `retry-after`, and the `git ls-remote` fallback outcome) and opens **no PR**. Failure issues are **not** auto-closed and **not** deduplicated — each is a discrete observation a human triages and closes deliberately. +- **Read fallback ladder** (before any fail-close): `GITHUB_TOKEN` → `gh auth token` → anonymous REST (with an authenticated-403→anonymous retry) → unauthenticated `git ls-remote` against the public clone URL (a separate code path that bypasses both org FG-PAT policy and REST anonymous limits for public repos). Writes use `gh` and target **only** `withautonomi/autonomi-developer-docs`. +- **Per-record fail-closed (one page deferred, run continues):** if both SHA fetches *and* the compare API fail for a record → that page is held back as a **`upstream-sweep-manual-review`** issue; the rest of the run proceeds. Manual-review issues also cover audit ambiguity (page-batching cases 3/4/5 from ADR-0006). +- **Outcome → artifact topology:** no drift → **nothing** (silent exit; the run log is the only trace). Drift, all clean → sweep PR (+ prose draft PR if prose changed); the PR bodies are the run summary. Drift + ambiguity → PRs plus one manual-review issue per deferred record. Scanner/step error → one failure issue, no PR. +- **Serial cadence via open-PR collision:** before opening anything, list open PRs (`--limit 1000`, client-side prefix filter on `claude/sweep-*`/`claude/prose-*`) and **exit silently if any are open** — the open PR is itself the signal; drift is re-detected next run. This forces one-PR-at-a-time and prevents accumulation. +- **Fingerprint dedup for manual-review issues:** a deterministic `Fingerprint:` line (record location + `recorded_sha..head_sha`) is matched client-side across all open issues (no label filter, because labels are best-effort); an unmoved record **reuses** its existing issue (adding a run-trail comment) instead of opening a duplicate; a moved `head_sha` is a new event with a new issue. +- **Best-effort labels:** the routine attempts `upstream-sweep-failure` / `upstream-sweep-manual-review` labels but falls back to an unlabeled issue (with an in-body note) rather than aborting on label-write permission gaps. Only a real issue-creation failure (auth/network/permission) aborts. + +## Consequences + +### Positive + +- "No drift" is trustworthy — it can only mean the scanner looked and found nothing. +- Reviewers get **zero noise on healthy days** and a precise, self-contained diagnostic on failures. +- No PR/issue floods: collision handling serializes PRs; fingerprinting serializes manual-review issues. +- The `git ls-remote` fallback resolves the real `saorsa-labs` incident that motivated the hardening. + +### Negative / Trade-offs + +- Strictness can abort a whole run on a single transient error; recovery is next-day (acceptable given ~24h latency, ADR-0005). +- Failure issues are intentionally *not* deduplicated, so a persistent fault produces one issue per day until a human fixes and closes them. +- Serial cadence means a long-open PR blocks new drift detection surfacing until it merges/closes. + +### Neutral / Operational + +- Two distinct issue classes (`-failure` aborts the run; `-manual-review` is per-record and non-blocking) with different lifecycles a human must understand. +- The scanner's diagnostic schema and the fingerprint format become contracts; changing them affects dedup correctness. + +## Validation + +- The scanner returns a non-zero exit / `status: "error"` on every fail-closed condition; the routine treats any non-`ok` status as abort-and-open-failure-issue. +- The `git ls-remote` fallback was **verified live** against `saorsa-labs/saorsa-core` from the routine sandbox (`8348c72`). +- Dedup is checkable: a record whose `head_sha` has not advanced must not spawn a second open manual-review issue across consecutive runs. +- Review trigger: changing the outcome→artifact topology, the fallback ladder, the collision rule, or the fingerprint format supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0008-gitbook-github-dual-audience-docs-platform.md b/adr/ADR-0008-gitbook-github-dual-audience-docs-platform.md new file mode 100644 index 0000000..92b5182 --- /dev/null +++ b/adr/ADR-0008-gitbook-github-dual-audience-docs-platform.md @@ -0,0 +1,77 @@ +# ADR-0008: GitBook + GitHub as the docs platform, serving a dual human and LLM audience + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification blocks), ADR-0006 (PR tracks), ADR-0010 (CLAUDE.md contract), ADR-0011 (IA & content design carried on this platform); `CLAUDE.md` §"Audience and objectives" and §"Repository purpose"; `.gitbook.yaml`; `docs/SUMMARY.md`; publishes to `docs.autonomi.com/developers` + +> Retrospective ADR. This platform choice predates and underlies the automation; it is recorded here because everything else (source-of-truth model, sweeps, prose PRs) assumes it. + +## Context + +The developer documentation has **two first-class audiences at once**, and the platform has to serve both without forking the content: + +- **Humans** — developers from indie hackers to enterprise teams — who need readable, navigable, usable docs with a familiar information architecture. +- **AI agents / LLMs** — which need the same content in a form they can ingest, parse, and act on programmatically, and which increasingly are the thing reading docs on a developer's behalf. + +`CLAUDE.md` already encodes this dual mandate explicitly: the audience is "Human developers building applications" **and** "AI agents consuming documentation via llms.txt to interact with the network programmatically." We needed a delivery platform that treats machine-readability as a primary output, not an afterthought, while not forcing us to build and maintain a documentation web app of our own. + +## Decision Drivers + +- Content must be **first-class for both humans and LLMs** from a single source — no separate machine copy to drift. +- Source format must be **LLM-native** and diff-friendly so both people and agents can contribute auditably. +- Contribution and change control must run through **GitHub** (branches, PRs, review) — the substrate the automation in ADR-0005/0006 already depends on. +- Must expose the **whole corpus to machines** (an `llms.txt`, per-page Markdown renderings, SEO-aware structure) while keeping a human IA/navigation. +- Should let the team **focus on content, not on building an interface**, using tooling that already works today. + +## Considered Options + +1. **Bespoke docs web app** (custom-built site over the Markdown). Rejected for now: forces us to build and maintain an interface alongside the content, for capabilities GitBook already provides; revisitable later. +2. **Plain static-site generator** (e.g. a generic SSG) on GitHub Pages. Rejected: we would have to assemble `llms.txt`, per-page Markdown endpoints, and SEO structure ourselves, and re-solve navigation/IA. +3. **GitBook synced to GitHub via Git Sync.** Chosen. Markdown source in Git; GitBook renders the human site and the machine surfaces. + +## Decision + +We will publish the docs through **GitBook, Git-synced to the GitHub repository**, with Markdown as the single source of truth. This choice is made for the following properties, all of which serve the dual audience: + +1. **Markdown source** — natively ingestible by LLMs and human-readable, and the format the whole verification/sweep model (ADR-0003/0006) operates on. +2. **Already in place and functional** on the website — no migration cost to start. +3. **Contribution via GitHub** — team members *and* agents contribute reliably and auditably through branches and PRs, which is exactly what the automation relies on. +4. **Machine surfaces are generated for us** — LLM-tuned per-page Markdown renderings, and automatically assembled SEO-aware structures including `llms.txt`, so the corpus is discoverable and ingestible by models. +5. **Dual access shape** — a human-familiar IA and navigation for people, while machines can grab the entire corpus via `llms.txt`, the GitHub repo, and the per-page Markdown renderings. +6. **Content focus** — pre-existing tooling means the team maintains documentation, not a documentation interface. + +We may in future move to a native or custom-designed UI; GitBook's GitHub integration is sufficient for all of the above **now**, and a later UI change does not invalidate the Markdown-in-Git source model. + +## Consequences + +### Positive + +- One Markdown source serves humans and LLMs; no separate machine copy to keep in sync. +- The GitHub-centric flow is what makes agent contribution (ADR-0005/0006) and auditable review possible at all. +- `llms.txt` + per-page Markdown make the corpus first-class for model ingestion without extra engineering. +- No interface to build or run; effort goes into content and accuracy. + +### Negative / Trade-offs + +- A dependency on GitBook's product and its Git Sync semantics (e.g. GitBook-specific `{% tabs %}` syntax in the Markdown, per `CLAUDE.md`). +- Some presentation is owned by GitBook, not us, until/unless we move to a custom UI. +- Machine surfaces (`llms.txt`, SEO structure) are generated by the platform, so their exact shape is partly outside our control. + +### Neutral / Operational + +- The Diátaxis IA (Getting Started → Core Concepts → How-to → Reference) and `SUMMARY.md` navigation are maintained for humans; `CLAUDE.md` requires each page to be self-contained so it stands alone when an agent pulls it via `llms.txt`. +- A future custom UI is an open option, not a commitment; this ADR would be superseded if we adopt one. + +## Validation + +- GitBook publishes the human developer documentation at `docs.autonomi.com/developers`, includes the developer corpus in the site-wide `docs.autonomi.com/llms.txt`, and serves per-page Markdown representations. +- Self-containment is enforced by review against `CLAUDE.md` (no "see above" cross-references), so a single page pulled by an agent is intelligible alone. +- Review trigger: adopting a custom/native UI, or moving off GitBook/Git Sync, supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0009-human-review-gate-path-to-full-automation.md b/adr/ADR-0009-human-review-gate-path-to-full-automation.md new file mode 100644 index 0000000..2b8ad29 --- /dev/null +++ b/adr/ADR-0009-human-review-gate-path-to-full-automation.md @@ -0,0 +1,78 @@ +# ADR-0009: Human review until independent panel automation is proven + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0005 (routine venue and billing), ADR-0006 (update tracks), ADR-0007 (fail-closed topology), ADR-0010 (repository authoring policy); `planning/adr-implementation-conformance.md` + +> Retrospective ADR capturing both the current safety boundary and the accepted future architecture. The independent AI review panel and automatic progression are intended architecture, not yet built. + +## Context + +The destination is a fully automated pipeline in which upstream changes can progress into accurate documentation without routine human intervention. Accuracy must remain demonstrable and tested rather than assumed. + +The present system does not have an automated reviewer with enough independent evidence to replace human review safely. This ADR preserves the human boundary while recording the intended panel architecture so the interim control is not mistaken for the destination. + +## Decision Drivers + +- Wrong documentation is worse than stale documentation, so uncertain changes must not progress automatically. +- The end state removes routine human intervention without removing independent review. +- Review must test changes against source evidence and repository policy, not rely on the generator's confidence. +- Disagreement and uncertainty need a human escalation path. +- Review independence must reduce correlated errors between generation and approval. +- Automation must advance only on measured evidence. + +## Considered Options + +1. **Allow automatic progression without an independent review architecture.** Rejected: structural checks alone do not establish the accuracy of developer-facing claims. +2. **Keep human review permanently.** Rejected as the destination: it makes routine maintenance depend on human availability. +3. **Keep human review as the current safety boundary and replace it only with a proven independent AI review panel that escalates uncertainty.** Chosen. + +## Decision + +Human review remains the merge safety boundary until the intended independent AI review panel is implemented and has met a defined evidence threshold. + +The accepted future architecture is: + +- multiple independent AI reviewers assess each change against source-of-truth evidence and repository policy; +- panel lanes are independent across providers or model families so one generator or model does not approve its own work through a correlated review; +- a clean change progresses automatically when the panel agrees and the required evidence is present; +- disagreement, uncertainty, insufficient evidence, and failed checks escalate to a human rather than progressing automatically; and +- the metadata-only and prose-impacting tracks may cross the evidence threshold independently. + +The panel architecture and automatic progression are not yet built. ADR-0005 remains authoritative for execution venue and billing constraints; those operational concerns do not define the panel's reviewer lanes here. + +## Consequences + +### Positive + +- Human review protects accuracy until an independently reviewed replacement is proven. +- The automated destination remains an explicit architectural commitment. +- Independent reviewers reduce correlated blind spots between generation and approval. +- Human attention is reserved for disagreement, uncertainty, and incomplete evidence once the panel exists. +- Each update track can advance without weakening the safety boundary for the other. + +### Negative / Trade-offs + +- Human review remains a throughput constraint until the panel is implemented and proven. +- A genuinely independent panel is more complex than a single automated reviewer. +- Measuring readiness and maintaining escalation paths add operational work. + +### Neutral / Operational + +- Rollout order, evidence windows, numerical thresholds, concrete providers, model versions, credentials, and review harnesses belong in the panel specification and implementation plan. +- Replacing human control does not alter the verification source-of-truth model, update-track envelopes, or fail-closed topology. + +## Validation + +- Until the panel is built and proven, routine changes require human approval to progress. +- A future panel specification defines measurable evidence, reviewer independence, agreement handling, and human escalation before automatic progression is enabled. +- Each track must meet the specified evidence threshold independently. +- Review trigger: weakening human control before the panel qualifies, removing reviewer independence or human escalation, or abandoning the fully automated destination requires a superseding ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0010-claude-md-authoring-constraints-contract.md b/adr/ADR-0010-claude-md-authoring-constraints-contract.md new file mode 100644 index 0000000..1d89ad9 --- /dev/null +++ b/adr/ADR-0010-claude-md-authoring-constraints-contract.md @@ -0,0 +1,74 @@ +# ADR-0010: CLAUDE.md as the enforced authoring-constraints contract + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification model — CLAUDE.md restates it), ADR-0006 (prose track that must satisfy this contract), ADR-0008 (dual audience), ADR-0009 (AI panel reviews against it), ADR-0011 (the IA/content design this contract enforces operationally); `CLAUDE.md` (repo root); terminology lint / `prose-guard` + +> Retrospective ADR. `CLAUDE.md` has governed authoring since the repo baseline (2026-04-02); this records its role as a binding contract rather than advisory notes. + +## Context + +The docs are written and reviewed mostly by AI agents on a daily cadence (ADR-0005/0006). Agents are fluent but not automatically consistent: left unconstrained they drift in voice, invent plausible-but-wrong terminology, reach for banned filler ("simply", "just", "leverage"), leak provenance language ("upstream", "the README says", "current merged truth") into rendered prose, or restructure pages ad hoc. For a corpus that must read as one coherent product **and** be reliably parsed by other LLMs (ADR-0008), that variance is a correctness problem, not a style preference. The constraints therefore need to live in one authoritative, machine-loadable file that every authoring and reviewing agent reads — and parts of it need to be **mechanically enforced**, not merely requested. + +## Decision Drivers + +- One **authoritative, version-controlled** source of authoring rules, loadable by any agent (Claude Code, OpenCode, reviewers) and by CI. +- Terminology and prohibited-word rules must be **machine-checkable**, because human review will not catch every slip at daily volume. +- Rules must cover **voice, terminology, structure, code examples, refusal, and audience priority** — the full surface that keeps the corpus coherent and machine-parseable. +- Must bind **both writing and review**, so the panel in ADR-0009 checks against the same contract the author used. + +## Considered Options + +1. **Informal style notes** in a wiki or README section. Rejected: advisory, unenforced, drifts, and not reliably loaded by agents. +2. **Per-page or per-agent prompt conventions.** Rejected: inconsistent, unversioned, impossible to audit or lint against. +3. **A single root `CLAUDE.md` as a binding contract, with its terminology/word rules enforced by CI.** Chosen. + +## Decision + +We will treat **`CLAUDE.md` at the repo root as the binding authoring-constraints contract** that every authoring and reviewing agent must follow, and enforce its mechanical parts in CI. Its scope is: + +- **Repository purpose & scope** — source of truth for `docs.autonomi.com/developers`; Autonomi 2.0 only; explicit exclusions (Autonomi 1.0/MaidSafe, `ant-quic`); in-scope repos governed by `repo-registry.yml` / `component-registry.yml`. +- **Audience & priority hierarchy** — dual human + LLM audience (ADR-0008); practical usage ("what can I do?") outranks internals ("how does it work?"). +- **Source-of-truth restatement** — the two verification modes and the `source audit → draft → verify` workflow (the authoring-facing face of ADR-0003), plus refusal rules (no `source_commit: TBD`, no inferred endpoints/types, historical memos are not authoritative). +- **Voice & tone** — second person, present tense, active voice; explain "why" before "how"; lead with the user task, not repo/implementation names; keep provenance language (`upstream`, `current-merged-truth`, "the README says") out of rendered prose. +- **Prohibited words/phrases** — e.g. "simply", "just", "easy", "leverage", "utilize", "in order to", "we", "please", "it should be noted that". +- **Terminology lockfile** — canonical terms with prohibited alternatives and definitions (e.g. `DataMap`, `antd`, `Autonomi Network Token (ANT)`, `ML-DSA-65`), explicitly "used by both agents (when writing) and CI (when linting)." +- **Page templates** — required structure per Diátaxis page type (Getting Started / Core Concept / How-to / Reference / Language Binding), including the mandatory verification block. +- **Code-example rules** — complete and runnable; `{% tabs %}` with cURL first; language-tagged fences. +- **Drafting & review procedures** — new-page, update-page, and reviewer checklists (the reviewer checklist is the seed of ADR-0009's panel). + +The mechanically-checkable parts (terminology lockfile, prohibited words, template/verification-block presence) are enforced by the terminology lint / `prose-guard` layer; the judgement parts (voice, structure, priority) bind agents and reviewers by contract. + +## Consequences + +### Positive + +- The corpus reads as one product and stays reliably machine-parseable, regardless of which agent wrote a given page. +- Terminology and banned-word violations are caught by CI at daily volume, not left to human vigilance. +- Authors and the ADR-0009 review panel are held to the **same** contract, so review is objective rather than taste-based. +- New agents/harnesses onboard by reading one file. + +### Negative / Trade-offs + +- `CLAUDE.md` is large and must be kept current; a stale rule silently mis-shapes every page written against it. +- Hard terminology/word rules can produce false positives (a legitimately-quoted banned word), needing occasional escaping or rule refinement. +- Centralisation makes `CLAUDE.md` itself a high-value change surface — edits to it are effectively edits to every future page. + +### Neutral / Operational + +- `CLAUDE.md` restates parts of the source-of-truth model (ADR-0003) for the author's convenience; the ADR remains the decision of record and the two must not diverge. +- The terminology lockfile is a living list; adding a term is routine, but changing the *enforcement contract* (what CI blocks) is an authoring-policy change worth noting here. + +## Validation + +- CI (terminology lint / `prose-guard`) fails a PR that violates the lockfile, uses a prohibited word, or omits a required verification block. +- Reviewers (human today, panel in ADR-0009) check voice, structure, template conformance, and prohibited phrasing against `CLAUDE.md`. +- Review trigger: a material change to the authoring contract — new prohibited/terminology rules that CI enforces, or a change to the template set or audience priority — should be reflected here (and may warrant a superseding ADR if it changes the enforcement model itself). + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0011-information-architecture-and-content-design.md b/adr/ADR-0011-information-architecture-and-content-design.md new file mode 100644 index 0000000..e27fd20 --- /dev/null +++ b/adr/ADR-0011-information-architecture-and-content-design.md @@ -0,0 +1,83 @@ +# ADR-0011: Documentation information architecture and content design — route-first navigation with Diátaxis, dual-audience pages + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification trails per page), ADR-0008 (platform & dual audience), ADR-0010 (CLAUDE.md enforces this design operationally); `planning/information-architecture.md` (historical design context and per-page briefs); the route-first IA implementation arc (2026-04-17 → 04-21, PR #9 `ia/route-first-docs`) + +> Retrospective ADR. The IA and content-design decisions were made and iterated across late March → April 2026. This ADR records the governing information-architecture and content-design invariants; earlier planning documents preserve the design history rather than defining the current decision. + +## Context + +Publishing platform (ADR-0008) and authoring mechanics (ADR-0010) do not decide the *shape and quality* of the documentation: how it is organised, how a reader moves through it, and what makes a page good. That is a distinct design decision, and it was deliberated in its own right — the site was first laid out Diátaxis-section-first (`getting-started/`, `core-concepts/`, `how-to-guides/`, `sdk-reference/`, `cli-reference/`) and then deliberately reorganised into a **route-first** tree (`docs/sdk/`, `docs/mcp/`, `docs/cli/`, `docs/rust/`, `docs/guides/`, `docs/reference/`) with Diátaxis expressed *inside* each interface. + +The design has to satisfy two readers with different consumption modes at once (the same dual audience as ADR-0008, but here it constrains *page and tree design*, not platform): + +- **Humans** read sequentially and learn a journey — from "Hello World" to production — jumping to reference as needed. +- **AI agents** fetch specific pages by semantic search via `llms.txt` and need each page to stand alone, with no "see the previous chapter" dependencies. + +A developer also arrives already committed to an interface (SDK, MCP, CLI, or Direct Rust); organising primarily by Diátaxis type forces them to hop between top-level sections to follow one interface's story. + +## Decision Drivers + +- Serve both a **human learning journey** and **agent page-level fetch** from one tree. +- Match the reader's real entry point: they pick an **interface** first, then a task type. +- Keep the taxonomy **proven and predictable** (Diátaxis) rather than bespoke. +- Make each page **self-contained** and single-topic so LLM ingestion and hyperlinking stay clean. +- Foreground **practical usage** over internals; ship **correct** pages over comprehensive-but-speculative ones. + +## Considered Options + +1. **Diátaxis-section-first** (top-level Getting Started / Core Concepts / How-to / Reference, interfaces mixed within). The original layout. Rejected: a reader committed to one interface must hop across top-level sections to follow its thread; interface material gets diluted. +2. **Reference-only / API dump.** Rejected: serves neither the human learning journey nor conceptual understanding; agents get endpoints but no task or concept framing. +3. **Route-first (by interface) with Diátaxis expressed within each interface**, plus shared `guides/` and `reference/` areas and tool-neutral Core Concepts. Chosen. + +## Decision + +We will structure and write the docs to the following design, which is the standard the docs must keep meeting: + +- **Taxonomy: Diátaxis.** Getting Started → Core Concepts → How-to Guides → Reference is the documentation-type framework. +- **Navigation: route-first.** The tree is organised primarily by the four developer interfaces — **SDK, MCP, CLI, Direct Rust** — with Diátaxis types expressed *within* each interface, plus shared `guides/` and `reference/` areas. Landing pages sit first in a section with child pages nested beneath; navigation stays **hierarchical but flat** (max ~3 levels; every page a distinct URL). +- **Interface purity.** A page covers one interface unless it is explicitly a comparison/chooser page. **Core Concepts stay tool-neutral.** Getting Started and How-to pages state which interface/route they cover, why you'd choose it, and where the alternatives live. +- **Page design for dual consumption.** **One concept per page**, and every page is **self-contained** — no "as discussed above"; related pages are linked at the end, not relied on inline. An agent pulling a single page via `llms.txt` must understand it alone. +- **Content priority.** **Practical usage ("what can I do?") outranks internals ("how does it work?")**; internals appear as "under the hood" context or dedicated concept pages, never as the primary framing of a task page. +- **Interface stance.** **ant-sdk is the primary interface**; ant-client (Rust/CLI) is the advanced/direct alternative. The **REST API reference is the canonical shared surface** that all language bindings wrap ("learn one, know them all"). +- **Quality bar: correctness over coverage.** Better to ship fewer verified, accurate pages than many speculative ones; every page carries its verification trail (ADR-0003). + +This ADR is the decision and its invariants. Concrete navigation, page names, and page inventory may evolve as the documentation grows. A change requires a superseding ADR when it changes the route-first navigation model, Diátaxis taxonomy, interface set, interface-purity rules, self-containment requirement, practical-first priority, primary-interface stance, or canonical shared-reference model. + +## Consequences + +### Positive + +- A developer follows one interface's full story (concept → how-to → reference) without leaving its section. +- Self-contained, single-topic pages serve agent fetch and human scanning equally, and keep `llms.txt` ingestion clean. +- Diátaxis gives writers (human and AI) a predictable slot for every page, reducing structural drift. +- Practical-first framing and correctness-over-coverage keep the corpus useful and trustworthy rather than exhaustive and speculative. + +### Negative / Trade-offs + +- Route-first duplicates some Diátaxis scaffolding across interfaces (each interface has its own getting-started/how-to shape), which is more surface to keep parallel. +- Interface purity requires chooser/comparison pages to route readers who haven't picked an interface yet. +- Self-containment means some deliberate repetition across pages rather than cross-references. +- A shared canonical REST reference must stay accurate for *all* bindings at once; drift there is high-blast-radius (mitigated by ADR-0003/0006). + +### Neutral / Operational + +- Many of these principles are enforced operationally by `CLAUDE.md` (ADR-0010): per-Diátaxis-type page templates, interface-purity and tool-neutral-concepts rules, self-containment, and the practical-first hierarchy. This ADR is the design of record; CLAUDE.md is its enforcement, and the two must not diverge. +- The concrete tree and page set evolve (pages added/retired post-launch); such changes follow this design without needing a new ADR unless they change the design itself. +- A future custom UI (ADR-0008) would render this IA differently but does not change the taxonomy or page-design invariants. + +## Validation + +- Review `docs/SUMMARY.md` and the page tree as implementations of this ADR: interfaces remain route-first, Diátaxis types remain within each interface, shared concepts remain tool-neutral, and navigation remains hierarchical but flat. These files are validation targets, not the source of the decision. +- Review (human today; the ADR-0009 panel later) checks each page for interface purity, single-topic self-containment, correct Diátaxis slot, and practical-first framing — against `CLAUDE.md`. +- Correctness-over-coverage is observable: pages ship verified (ADR-0003) rather than speculative; unverifiable pages are deferred or stubbed, not guessed. +- Review trigger: changing the route-first navigation model, Diátaxis taxonomy, interface set, interface-purity rules, self-containment requirement, practical-first priority, primary-interface stance, or canonical shared-reference model supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0012-developer-skill-colocated-versioned-artifact.md b/adr/ADR-0012-developer-skill-colocated-versioned-artifact.md new file mode 100644 index 0000000..9c8f472 --- /dev/null +++ b/adr/ADR-0012-developer-skill-colocated-versioned-artifact.md @@ -0,0 +1,95 @@ +# ADR-0012: The Autonomi Developer skill — purpose, single-file format, in-repo location, and vendor-agnostic distribution + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (shared source-of-truth/verified_commits), ADR-0008 (dual audience — the skill is the agent-facing extreme), ADR-0011 (scope mirrors the SDK/CLI/Rust/MCP interface set), ADR-0013 (freshness & content tiering), ADR-0014 (maintenance & registry wiring); the reconstructed origin decision log (two Opus-4.7-era sessions, skill genesis); `skills/start/` (`SKILL.md`, `version.json`, `CHANGELOG.md`, `MAINTAINING.md`); `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`; `docs/developer-skill.md`; sibling repo `WithAutonomi/skills` + +> Retrospective ADR reconstructed from the origin design sessions plus the current repo. Where origin intent and current repo state differ, both are recorded and the difference is called out (most sharply on naming, below). This ADR covers *what the skill is, how it is formatted, where it lives, and how it is distributed*; freshness is ADR-0013 and maintenance is ADR-0014. + +## Context + +### Why the skill exists + +The skill's purpose is to **teach an AI agent to build on top of the Autonomi network** so the agent can begin and immediately see value, and gain the expertise to build applications, tools, and services on Autonomi — for itself and, by extension, its human operators. It is **developer knowledge**: developing, creating, and building *on top of* Autonomi's features, tools, and limitations. + +Crucially, the value is **agent-side opinion**, not a second copy of the reference docs. Autonomi exposes four developer paths (SDK, MCP, CLI, Direct Rust) and the choice between them is meaningful; the network enforces strict terminology; and mainnet operations move real money. A skill can carry the opinion a raw doc fetch cannot: which path to recommend, which words are wrong, and what to warn about. That is the job. + +### What it is and isn't (three-way boundary) + +- **It is** — knowledge for **building on top of** Autonomi (SDK, daemon, CLI, Direct Rust, MCP). +- **It is not** — a skill for **using** the network as an **end user / operator** (running nodes, uploading/managing data as a consumer). That surface is a *separate* skill — the `autonomi` skill now living in the sibling `WithAutonomi/skills` repo (see Naming). +- **It is also not** — a skill for **developing the network itself** / contributing to the Autonomi codebase. Building *on* Autonomi is in scope; building *Autonomi* is not. + +## Decision Drivers + +- **Purpose first:** an agent must go from zero to *building on Autonomi with value* from the skill alone — carrying opinion (path choice, terminology, mainnet warnings), not just facts. +- **Cross-agent reach:** the artifact must be installable by anyone, across runtimes — not tied to one vendor. +- **One source of truth:** the skill must not drift from the docs it derives from, and must reuse their verification machinery rather than duplicate it. +- **Low-friction iteration** during the pre-1.0 verification-and-hardening phase. +- **Precedent/consistency** with the sibling `x0x` skill in the same problem space. +- **Bounded to "build on top of"** — no bleed into end-user/operator use or core contribution. + +## Considered Options + +**Should it exist (Phase 1).** +1. *Do nothing* — rely on agents fetching the live docs at query time. Rejected: no agent-side opinion about which of the four paths to pick, no terminology enforcement, no mainnet warnings, inconsistent results across agents. +2. *Ship a Claude Code plugin as the primary artifact.* Rejected as primary: locks the skill to one runtime; Autonomi wants OpenCode/Cursor/Windsurf/etc. reach. A plugin may still *wrap* the skill later. +3. *Ship a portable skill.* Chosen. + +**Format (Phase 2).** +1. *Single `SKILL.md`* in a folder with `version.json` + `CHANGELOG.md`. Chosen. +2. *`.skill` archive* with `references/` + `scripts/` (progressive disclosure). Rejected: over-engineered for a bounded surface; the sibling `x0x` skill settled on single-file, and consistency beats theoretical elegance. Revisit only if the file grows past ~1500 lines. +3. *Plugin* wrapping either. Rejected as primary (see Phase 1). + +**Location (Phase 5).** +1. *`docs/skills/…`* inside the GitBook-synced tree. Rejected: would publish an agent-instruction document to `docs.autonomi.com`, confuse human readers, and subject the skill to Diátaxis page-template + docs verification rules that don't fit it. +2. *Separate repo.* Rejected *at genesis*: fragments the source of truth — the skill mirrors `CLAUDE.md` terminology and depends on `repo-registry.yml` / `component-registry.yml`, so co-location lets one PR update skill and context together. (Note: this is now partially revisited — see Consequences — as a *future* migration target, not a genesis choice.) +3. *`skills//` at repo root.* Chosen. + +## Decision + +- **Ship a portable, single-file `SKILL.md`** (alongside `version.json` and `CHANGELOG.md`) as the canonical artifact, matching the `x0x` precedent. The skill carries agent-side opinion (path selection, terminology enforcement, mainnet/real-money warnings), not a duplicate of the reference docs. +- **Locate it at `skills//` at the repo root**, a sibling of `docs/`, `planning/`, `reference/` — co-located with the `CLAUDE.md` style guide and both registries it depends on, and **excluded from GitBook publish** (GitBook scope is `docs/` only) so public docs readers never land on agent instructions. +- **Distribute it vendor-agnostically from a canonical source, fanned out across channels.** The canonical artifact is the portable `SKILL.md` (plus its `version.json` manifest) at a stable raw URL, not any particular plugin, marketplace, agent runtime, or installation mechanism. The first-class packaged distribution currently uses the Claude plugin/marketplace (`.claude-plugin/` → marketplace `withautonomi`, plugin `developer`, giving `/developer:start`), while compatible agent runtimes can consume the raw artifact manually. Distribution should evolve toward multiple supported channels, potentially including **skills.sh**, manual installation, Claude's plugin ecosystem, and other compatible registries or runtimes. Claude is one adapter, not the definition of the artifact. +- **Canonical identity: `autonomi-developer`.** The canonical, vendor-neutral, self-describing machine identifier is `autonomi-developer`, with **Autonomi Developer Skill** as its human-facing name. It is accurate across all four paths (not just SDK) and distinct from a user-facing skill. Distribution adapters may provide channel-specific aliases, but those aliases do not redefine the canonical artifact or identity. + +The genesis rationale: `autonomi-developer` is vendor-neutral, self-describing, correct across all four paths (not just SDK), and leaves a distinct name free for a future user-facing skill (rejected `autonomi` as too broad, `autonomi-sdk` as too narrow, `autonomi-development-skill` as clunky). That reserved user-facing slot has since been taken by the `autonomi` skill in the sibling `WithAutonomi/skills` repo, which confirms this one should keep the *developer* identity. + +The repo currently ships the skill on-disk as `skills/start/` (`name: start`, plugin `developer`, `/developer:start`). Commit `2b290ef` introduced those identifiers to produce a concise Claude slash command instead of `/autonomi-developer:autonomi-developer`, prioritizing that channel's invocation shape over the vendor-neutral on-disk identity. These are Claude-channel implementation details and do not replace the canonical `autonomi-developer` identity. Whether to realign the path or migrate the artifact to `WithAutonomi/skills` is tracked in `planning/skill-open-questions.md`. Realigning the implementation with the canonical identity does not require a new ADR, but changing the stable raw path requires the major-version and migration treatment in ADR-0013; choosing a different identity or location requires a superseding ADR (see Validation). The automation ADRs (0005–0007) refer to the literal `skills/start/` path because that is what the routine touches today. + +## Consequences + +### Positive + +- The skill carries opinion agents can't get from a raw doc fetch: path choice, terminology, real-money warnings. +- Single-file `SKILL.md` diffs and reviews cleanly in one PR, needs no build step, and any agent that reads Markdown can ingest it. +- Co-location keeps the skill honest against `CLAUDE.md` and the registries, and lets one PR move skill + context together. +- The portable artifact is already manually consumable by compatible runtimes; multi-channel distribution can improve discoverability and installation without creating another canonical copy. + +### Negative / Trade-offs + +- **Naming and packaging debt:** the on-disk name (`start`) and first-class packaged distribution reflect one vendor channel, not the canonical `autonomi-developer` identity or intended multi-channel distribution — tracked in `planning/skill-open-questions.md`. Bringing the implementation into conformance does not change this decision. +- Single-file **size ceiling** (~1500 lines): if the surface outgrows it, revisit `.skill` with `references/` (partly mitigated by content tiering, ADR-0013). +- No place for bundled machine artifacts (helper scripts) in a single file — not needed today. +- Co-location couples skill release hygiene to the docs repo's PR flow; a future move to `WithAutonomi/skills` would decouple them but re-open the "one PR for skill + context" benefit. + +### Neutral / Operational + +- The repo doubles as a Claude marketplace (`.claude-plugin/`); that is one realised channel, not the artifact's definition. +- A future migration to `WithAutonomi/skills` is anticipated but not decided here; it would supersede the location and stable manifest-URL decisions while preserving the canonical identity and vendor-neutral distribution invariant unless a later ADR explicitly changes them. +- `MAINTAINING.md` (skill-local) and the registries carry the operational detail; ADR-0014 records the maintenance decision. + +## Validation + +- The canonical `SKILL.md` is installable across channels: the Claude path (`/plugin marketplace add …` → `/plugin install developer@withautonomi` → `/developer:start`) and the vendor-neutral path (fetch the raw `SKILL.md` and install it directly). +- Scope check (human today; the ADR-0009 panel later): reviewers reject material that teaches end-user/operator usage or core-codebase contribution. +- Naming/location review trigger: choosing `start` or another identifier as the canonical identity, or migrating the skill to `WithAutonomi/skills`, supersedes the relevant parts of this ADR. Realigning the implementation to `autonomi-developer` does not, but its stable raw URL move is a major version change under ADR-0013. +- Distribution review trigger: narrowing to a single vendor/channel supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0013-skill-freshness-mechanism-and-content-tiering.md b/adr/ADR-0013-skill-freshness-mechanism-and-content-tiering.md new file mode 100644 index 0000000..75c94bb --- /dev/null +++ b/adr/ADR-0013-skill-freshness-mechanism-and-content-tiering.md @@ -0,0 +1,89 @@ +# ADR-0013: Skill freshness — three-part staleness defence and content tiering + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification blocks/verified_commits), ADR-0012 (the skill this protects), ADR-0014 (maintenance closes the human-paced loop), ADR-0005 (the daily routine now carries stamp refreshes); origin decision log Phases 3, 6, 7; `skills/start/SKILL.md` frontmatter, `skills/start/version.json` (`canonical_skill_url`, `canonical_docs_url`) + +> Retrospective ADR reconstructed from the origin design sessions. Freshness was identified there as "the load-bearing decision of the whole design": a skill that goes stale actively misleads users within weeks. This ADR records how staleness is defended against; ADR-0014 records how new versions are cut. + +## Context + +Autonomi's codebase changes almost daily, and a skill has **no runtime — it cannot self-update**. A shipped snapshot decays fast, and a stale skill is worse than none: it confidently gives wrong endpoints, flags, and commands. So freshness has to be *designed into* the artifact, defending against several distinct failure modes at once: the maintainer forgetting to re-verify, the user's agent not auto-updating, the agent being offline, and fast-moving API detail drifting between releases. + +## Decision Drivers + +- A stale skill must, at minimum, **know it is stale and say so**. +- The defence must **degrade gracefully** — no single point (a network call, a maintainer, a fetch) whose failure silently reintroduces stale answers. +- **Fast-moving detail must not live in the snapshot** — it should be fetched fresh at query time. +- **No new infrastructure** to run (no bespoke server, no subdomain) at this stage. + +## Considered Options + +**Freshness mechanism (Phase 3).** +1. *Static content, periodic re-release, no runtime check.* Rejected: Autonomi's velocity makes stale-harm certain. +2. *A dedicated skill-owned MCP server* serving live content. Rejected: over-engineering — infrastructure the docs team must run and the user must install, duplicating what `docs.autonomi.com` already serves. +3. *Rely on the existing `antd-mcp`.* Rejected: `antd-mcp` talks to a running daemon (network/wallet/upload state), not documentation — wrong tool. +4. *Rely solely on GitBook's `/~gitbook/mcp`.* Rejected as the sole mechanism: requires every consumer's agent to know about GitBook MCPs, isn't universally available, and doesn't help the skill notice *its own* staleness. +5. *Runtime freshness check + content tiering.* Chosen. + +**Manifest hosting (Phase 6).** +1. *A `docs.autonomi.com/...` URL* (requires duplicating a copy into `docs/` for GitBook to sync). Rejected: re-publishes the manifest publicly and creates a two-copies divergence problem, inverting ADR-0012's location decision. +2. *A dedicated `skills.autonomi.com` subdomain.* Rejected as premature: no ops team, no second consumer yet to justify it. +3. *GitHub raw URL.* Chosen. +4. *GitHub Pages / CDN* in front of raw. Rejected as unnecessary for the current traffic (one read per activation). + +**Content boundary (Phase 7).** +1. *Bundle everything* (full REST/CLI/binding surface pasted in). Rejected: 5000+ lines, blown context budgets, a new release on every trivial upstream change, and a two-copies problem with the docs. +2. *Pointer everything* (thin router). Rejected: strips the skill of its value-add — the opinion (path choice, terminology, warnings) is not in a REST reference. +3. *Tiered — bundle stable canon, point at fast-moving surfaces.* Chosen. + +## Decision + +We will defend skill freshness with a **three-part mechanism that degrades gracefully**, plus a **content-tiering rule** that keeps fast-moving detail out of the snapshot. + +**1. Frontmatter fingerprint (passive).** Every release pins `version`, `verification_mode`, a per-repo `verified_commits` SHA map, and `verified_date` in the `SKILL.md` frontmatter. The runtime `version.json` manifest mirrors the fields needed for runtime and external inspection, including `version`, `verification_mode`, and `verified_commits`; it deliberately does not carry `verified_date`. That date describes when the skill content was reviewed and belongs only in `SKILL.md`. Even with no network, a maintainer can inspect the skill itself to see exactly what was verified, and when. + +**2. Runtime version check (active).** The skill's first instruction to the loading agent is to fetch a stable-URL `version.json`, compare its `version` to the skill's own, and **warn the user if a newer version exists**. If the fetch fails (offline, blocked), the skill **continues silently** — the check never blocks use. + +**3. Content tiering (structural).** Stable canon is **bundled**; fast-moving surfaces are **pointers** to the live docs, and the skill instructs the agent to *fetch the live page rather than fabricate*. So even if (1) and (2) are ignored or fail, drift in fast-moving areas causes a fresh fetch, not a wrong answer. + +- **Bundled (changes on network-architecture cadence):** what Autonomi is; which developer path to pick and when; core concepts at high level (data types, keys/addresses/DataMaps, self-encryption, PQC, payment model); the terminology lockfile (mirrors `CLAUDE.md`); common errors/diagnostics; agent behaviour rules (don't invent APIs, warn on mainnet, silently correct terminology); the test/devnet/mainnet path at high level. +- **Pointered (changes on release cadence) → `docs.autonomi.com/developers/…`:** REST endpoints, gRPC services, daemon command reference, per-language binding APIs, CLI command reference, Rust library reference, install commands/versions, MCP tool list, wallet setup. +- **Boundary rule:** anything **regenerable from source** (endpoint shapes, signatures, flag lists) is fast-moving → pointer; anything describing **network-level design intent** (data model, payment concept, PQC posture) is stable → bundle. + +**Manifest hosting.** The `version.json` is served from a **GitHub raw URL** on the docs repo (currently `raw.githubusercontent.com/WithAutonomi/autonomi-developer-docs/main/skills/start/…`), recorded in the skill via `canonical_skill_url` / `canonical_docs_url`. Zero new infrastructure. **Moving that URL (repo rename or path change) is a breaking change → major version bump**, and the base URL is treated as a stability contract. + +## Consequences + +### Positive + +- The three parts fail independently: a missed re-verification is caught by the runtime warning; a failed/absent runtime check is caught by pointer-fetch-on-demand; an offline agent still gets correct path/terminology/warnings from the bundle. +- Skill releases track *conceptual* change, not routine API drift — far fewer releases, far less churn. +- No infrastructure to run; the manifest rides GitHub raw. + +### Negative / Trade-offs + +- The runtime check adds an on-activation HTTP request; airgapped/restricted agents skip it (acceptable — silent continue). +- Pointer content requires the agent to have **web-fetch** and requires `docs.autonomi.com` to stay up; a domain move breaks every pointer (same stability-contract mitigation as the manifest URL). +- Fields needed in both the skill frontmatter and runtime manifest — including `version`, `verification_mode`, and `verified_commits` — must be kept in sync (ADR-0014 checklist). `verified_date` is intentionally not duplicated, avoiding unnecessary date synchronization. +- The bundle/pointer boundary is a judgement call that must be applied consistently. + +### Neutral / Operational + +- The GitHub-raw path currently embeds `skills/start/`; if the skill is renamed or migrated (ADR-0012), the manifest URL changes and — by the rule above — that is a major bump. +- Since genesis, the daily upstream-sweep routine (ADR-0005) has taken over the *stamp-refresh* half of keeping `verified_commits` current, so part 1 is now machine-maintained rather than purely manual. + +## Validation + +- The runtime instruction is present in `SKILL.md` and points at a resolvable `version.json`; a stale install produces a user-visible warning, and a failed fetch produces silence, not an error. +- No published (non-draft) release may carry a placeholder SHA (`TBD-…`) in `verified_commits` (ADR-0014 gate). +- Content-boundary review: reviewers confirm regenerable-from-source detail is pointered, not bundled. +- Review trigger: changing the freshness mechanism, the manifest host, or the bundle/pointer boundary supersedes this ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md b/adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md new file mode 100644 index 0000000..565c701 --- /dev/null +++ b/adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md @@ -0,0 +1,82 @@ +# ADR-0014: Maintain the skill through the docs verification system + +- **Status:** Accepted +- **Acceptance:** Retrospective — this ADR records a decision made before the ADR process existed. The original decision owner confirms it as a faithful account; current implementation gaps are tracked separately. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification model), ADR-0005 (scheduled maintenance), ADR-0006 (update tracks and coherent releases), ADR-0009 (review automation), ADR-0010 (authoring contract), ADR-0012 (the skill), ADR-0013 (freshness and stable URLs); `planning/verification-workflow.md`; `skills/start/MAINTAINING.md`; `repo-registry.yml`; `component-registry.yml`; the sweep guard and SHA-reachability workflows + +> Retrospective ADR reconstructed from the origin design sessions. Operational maintenance has evolved since the decision; mutable mechanics are documented outside this record. + +## Context + +The developer skill must stay accurate as its source repositories change. The docs repository already has a verification workflow and machine-readable registries that connect source components to maintained artifacts. A separate skill-only maintenance system would duplicate that model and let documentation and skill verification drift apart. + +The skill also has two metadata surfaces with different responsibilities: author-facing frontmatter attached to the skill content and a runtime manifest intended for external inspection. Their shared provenance and release identity must remain coherent without forcing content-review metadata into the runtime manifest. + +## Decision Drivers + +- Documentation and skill maintenance should use one source-audit and verification model. +- The registry must answer which source components require skill re-verification. +- Skill content must carry review provenance appropriate to an authored artifact. +- The runtime manifest must expose the release and provenance state needed by runtimes and external consumers. +- Content, release identity, and verification provenance must not contradict one another. +- Pure verification movement must not create a content release. + +## Considered Options + +1. **Create a separate maintenance workflow for the skill.** Rejected: it duplicates verification machinery and lets docs and skill maintenance diverge. +2. **Treat the skill as a documentation page in the existing page mapping.** Rejected: skills and rendered documentation have different artifact and metadata contracts. +3. **Reuse the docs verification workflow, add an explicit component-to-skill dependency relationship, and keep distinct skill and runtime metadata responsibilities.** Chosen. + +## Decision + +The skill reuses the docs repository's `source audit → draft → verify` workflow. The repository and component registries determine the relevant sources and re-verification scope. + +`feeds_skills` is the explicit component-to-skill dependency relationship. It remains distinct from page relationships so tooling can determine which skills a component affects without treating a skill as a Diátaxis page. + +Skill frontmatter and the runtime manifest have distinct responsibilities: + +- skill frontmatter records provenance attached to the reviewed content, including when that content was verified; and +- the runtime manifest exposes the release identity and verification provenance required for runtime and external inspection, without duplicating content-review metadata that has no runtime responsibility. + +Fields shared by those surfaces must agree, and the skill body, release identity, release history, and verification provenance must form one coherent state. A pure verification refresh that changes no described surface updates provenance without changing the content version or release history. + +Breaking changes to the runtime manifest's shape or to a stable manifest URL receive major-version treatment. Other release classification and the exact maintenance procedure remain defined by the skill's operational maintenance contract. + +Operational mechanics live in `skills/start/MAINTAINING.md`, `repo-registry.yml`, `component-registry.yml`, and the guard and reachability workflows. Those sources may evolve without rewriting this decision, provided they preserve these invariants. + +## Consequences + +### Positive + +- Documentation and skill verification use one maintenance model. +- `feeds_skills` provides a machine-readable dependency relationship without conflating artifact types. +- Content review metadata stays with the content while runtime metadata stays focused on runtime and external needs. +- Pure provenance refreshes do not imply a new content release. +- Breaking manifest and stable-URL changes remain visible through major-version treatment. + +### Negative / Trade-offs + +- Registry mappings and verification metadata must remain consistent. +- Shared release and provenance fields require synchronization across the two metadata surfaces. +- Coupling skill maintenance to the docs verification system means changes to that system can affect both artifact types. + +### Neutral / Operational + +- The set of components feeding the skill, exact files that move together, field-level checklists, guard behavior, and maintenance sequencing are mutable implementation details documented in the operational sources. +- Moving the skill to another repository would require preserving the dependency relationship and verification contract or superseding this decision. + +## Validation + +- Tooling can determine skill re-verification scope from registry relationships and detect disagreement between the dependency graph and skill provenance. +- Maintenance checks reject contradictory skill content, release, and provenance state. +- Pure verification refreshes leave the content version and release history unchanged. +- Manifest-shape and stable-manifest-URL breaks receive major-version treatment. +- Review trigger: replacing the shared verification workflow, removing the distinct `feeds_skills` relationship, changing metadata responsibilities, or allowing incoherent release and provenance state requires a superseding ADR. + +## Notes for AI-assisted work + +AI tools may help draft this ADR, but **must not mark it Accepted without human review**. Accepted ADRs are immutable: create a new superseding ADR rather than editing an Accepted ADR. diff --git a/adr/ADR-0015-retrospective-acceptance-and-immutability-boundary.md b/adr/ADR-0015-retrospective-acceptance-and-immutability-boundary.md new file mode 100644 index 0000000..2ba047d --- /dev/null +++ b/adr/ADR-0015-retrospective-acceptance-and-immutability-boundary.md @@ -0,0 +1,78 @@ +# ADR-0015: Retrospective acceptance and the Accepted-ADR immutability boundary + +- **Status:** Accepted +- **Acceptance:** Prospective — accepted by Jim Collinson as decision owner after review of PR 73 on 2026-07-16. +- **Date:** 2026-07-15 +- **Decision owners:** Jim Collinson +- **Reviewers:** Jim Collinson +- **Supersedes:** ADR-0001 (lifecycle entry and immutability timing only) +- **Superseded by:** none +- **Related:** PR 73; ADR-0002 through ADR-0014; `adr/README.md`; `adr/TOOLING.md`; `.adr-kit.yaml`; `scripts/adr-governance.py` + +## Context + +ADR-0001 requires every new decision to start as Proposed and makes Accepted ADRs immutable. That rule fits prospective decisions created under an established review process, but this repo also needs to record decisions made before ADR governance existed. + +Those retrospective records have no honest historical proposal or reviewer stage to reconstruct. Marking them Proposed would imply that the underlying decisions remain undecided, while inventing reviewers or an earlier acceptance event would falsify the record. At the same time, an ADR's introducing pull request must remain reviewable: corrections made before merge are part of establishing the final record, not post-acceptance rewriting of repository history. + +## Decision Drivers + +- Preserve an honest account of decisions made before ADR governance existed. +- Never invent historical reviewers, proposal stages, or acceptance ceremonies. +- Let the original decision owner confirm whether a retrospective record is faithful. +- Keep introducing pull requests reviewable until their final content is agreed. +- Preserve strict immutability once an Accepted record reaches the default branch. +- Keep prospective decisions under ADR-0001's Proposed-first human-review process. + +## Considered Options + +1. **Require retrospective records to start Proposed.** Rejected: this misstates settled historical decisions as presently undecided and creates a halfway status with no real prospective decision to make. +2. **Treat Accepted content as immutable from its first feature-branch commit.** Rejected: review corrections would require superseding ADRs before the original record had even merged. +3. **Allow owner-confirmed retrospective acceptance, with immutability beginning on the default branch.** Chosen: it keeps the historical record honest while preserving a clear, enforceable immutability boundary. + +## Decision + +Prospective decisions continue to start as Proposed and require human review before acceptance. + +A retrospective ADR may be introduced as Accepted when: + +- the decision predates the repo's ADR process; +- the original decision owner confirms that the ADR faithfully records the decision made; +- the ADR carries `Acceptance: Retrospective` metadata stating that basis; and +- any implementation gaps are tracked separately rather than hidden by rewriting the decision. + +An ADR's introducing pull request remains reviewable until merge. Accepted-ADR immutability begins when the record lands on the default branch. From that point onward, changing the decision or record requires a new superseding ADR. + +The new ADR's `Supersedes` field is the authoritative forward link. An older Accepted ADR remains byte-identical; supersession does not require adding a backlink to it. + +## Consequences + +### Positive + +- Retrospective records describe historical decisions without fictional process metadata. +- The original decision owner, rather than implementation drift, establishes whether the reconstruction is faithful. +- Pull-request review can correct a record before it becomes immutable repository history. +- The default branch provides a deterministic immutability boundary for tooling. + +### Negative / Trade-offs + +- An Accepted ADR can change across commits in its introducing pull request, so branch-local status alone does not prove immutability. +- Reviewers must distinguish corrections within an introducing PR from forbidden edits to an Accepted record already on the default branch. +- The exception depends on an identifiable original decision owner; it cannot legitimize unattributed retrospective decisions. + +### Neutral / Operational + +- `.adr-kit.yaml` continues to declare human acceptance required and forbids AI acceptance. +- Governance checks require Acceptance metadata on newly introduced Accepted ADRs and compare merged Accepted records byte-for-byte with the proposed result. +- ADR-0001's general adoption, Proposed-first prospective lifecycle, and supersession rules remain in force outside the retrospective exception and immutability timing clarified here. + +## Validation + +- A new prospective Accepted ADR without Acceptance metadata fails governance. +- A new owner-confirmed retrospective Accepted ADR remains reviewable within its introducing PR. +- Editing, deleting, renaming, or replacing an Accepted ADR already present on the comparison base fails governance. +- Review trigger: changing who may confirm retrospective acceptance, or moving the immutability boundary away from the default branch, requires a superseding ADR. + +## Notes for AI-assisted work + +AI tools helped draft this ADR. Jim Collinson accepted it after human review on 2026-07-16. Future changes require a superseding ADR. diff --git a/adr/README.md b/adr/README.md new file mode 100644 index 0000000..eccf25d --- /dev/null +++ b/adr/README.md @@ -0,0 +1,23 @@ +# Architecture Decision Records + +This directory contains Architecture Decision Records (ADRs) for this repository. + +## Rules + +1. Use `ADR-NNNN-short-title.md` names with four-digit numbers. Choose the next unused sequential number after the highest existing ADR; never reuse a number. +2. New prospective ADRs start as `Proposed`. A named decision owner may mark one `Accepted` only after human engineering review; AI tools may not accept ADRs. +3. `Accepted` ADRs are immutable. If the decision changes, create a new ADR and mark the old ADR as superseded by reference, not by editing its accepted content. +4. Architectural PRs must add or update an ADR before merge. +5. Reviews must check ADR correctness, evidence, trade-offs, and compliance — not just presence. + +Immutability begins when an Accepted ADR lands on the default branch. Its introducing PR remains reviewable until merge so the decision owner can confirm the final record. Retrospective ADRs may enter as Accepted when the original decision owner confirms that they faithfully record a decision made before ADR governance existed. + +Supersession never requires editing an Accepted record. The new ADR's `Supersedes` field is the authoritative forward link; the older record remains byte-identical. Use `Superseded by` only when that metadata can be populated before the older ADR becomes Accepted. + +## Template + +Use [`TEMPLATE.md`](./TEMPLATE.md). + +## Tooling + +See [`TOOLING.md`](./TOOLING.md) for `adrs`, `adr-kit`, and AI harness setup. diff --git a/docs/adr/TEMPLATE.md b/adr/TEMPLATE.md similarity index 83% rename from docs/adr/TEMPLATE.md rename to adr/TEMPLATE.md index 968549a..735c643 100644 --- a/docs/adr/TEMPLATE.md +++ b/adr/TEMPLATE.md @@ -1,9 +1,10 @@ # ADR-NNNN: - **Status:** Proposed +- **Acceptance:** Prospective | Retrospective — - **Date:** YYYY-MM-DD - **Decision owners:** -- **Reviewers:** +- **Reviewers:** - **Supersedes:** - **Superseded by:** - **Related:** diff --git a/docs/adr/TOOLING.md b/adr/TOOLING.md similarity index 77% rename from docs/adr/TOOLING.md rename to adr/TOOLING.md index 673aeb4..6cebeaf 100644 --- a/docs/adr/TOOLING.md +++ b/adr/TOOLING.md @@ -38,13 +38,17 @@ If the published package name differs on your machine, install from the project Add this project instruction to every AI coding harness profile (`AGENTS.md`, `CLAUDE.md`, Codex/OpenCode project rules, pi harness prompts, etc.): ```text -Before changing architecture, protocols, storage formats, crypto, network behaviour, public APIs, data models, or operational invariants, inspect docs/adr/. -If the change creates or changes an architectural decision, draft or update a Proposed ADR using docs/adr/TEMPLATE.md. +Before changing architecture, protocols, storage formats, crypto, network behaviour, public APIs, data models, or operational invariants, inspect adr/. +If the change creates or changes an architectural decision, draft or update a Proposed ADR using adr/TEMPLATE.md. Never edit an Accepted ADR. Create a superseding ADR instead. Never mark an ADR Accepted autonomously; that requires human engineering review and debate. During review, check ADR correctness, rejected alternatives, evidence, consequences, and immutable-Accepted compliance. ``` +The immutable boundary is the Accepted content on the default branch. An introducing PR remains reviewable until merge. For a retrospective ADR, the original decision owner may confirm that the proposed record faithfully captures a decision made before ADR governance existed and introduce it as Accepted. + +When a new ADR supersedes an Accepted record, put the relationship in the new ADR's `Supersedes` field. Do not edit the older record to add a backlink. + ## Review standard Do **not** "vibe code" ADRs. A useful ADR must show clear thinking: context, options, trade-offs, consequences, and validation. AI can help prepare a draft, but humans must debate and own the decision. diff --git a/docs/adr/README.md b/docs/adr/README.md deleted file mode 100644 index ec70c25..0000000 --- a/docs/adr/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Architecture Decision Records - -This directory contains Architecture Decision Records (ADRs) for this repository. - -## Rules - -1. Use `ADR-NNNN-short-title.md` names with four-digit numbers. -2. New ADRs start as `Proposed`. -3. `Accepted` ADRs are immutable. If the decision changes, create a new ADR and mark the old ADR as superseded by reference, not by editing its accepted content. -4. Architectural PRs must add or update an ADR before merge. -5. Reviews must check ADR correctness, evidence, trade-offs, and compliance — not just presence. - -## Template - -Use [`TEMPLATE.md`](./TEMPLATE.md). - -## Tooling - -See [`TOOLING.md`](./TOOLING.md) for `adrs`, `adr-kit`, and AI harness setup. diff --git a/planning/adr-implementation-conformance.md b/planning/adr-implementation-conformance.md new file mode 100644 index 0000000..eadab1a --- /dev/null +++ b/planning/adr-implementation-conformance.md @@ -0,0 +1,90 @@ +# ADR implementation conformance + +**Status:** Active planning record +**Last reviewed:** 2026-07-21 + +## Purpose + +The retrospective ADRs record decisions made before this repo adopted ADR governance. They are normative records of intended invariants, not claims that every part of the present implementation conforms. + +This file tracks known or suspected implementation gaps separately so correcting an implementation does not rewrite the historical decision. A gap is closed by bringing the implementation into conformance. A superseding ADR is required only when the decision itself changes. + +## Confirmed gaps + +| ADR | Intended invariant | Present implementation | Required follow-up | +|---|---|---|---| +| ADR-0001 / ADR-0002 | ADR format and Accepted-record immutability are enforced by CI at the repo-root `/adr` location. | The corrected script and workflow validate `/adr`, but the ADR Governance check is not a required branch-protection check and path-filtered execution cannot detect an architectural PR that omits an ADR. | Make ADR Governance a required check and define the human-review mechanism for identifying architectural changes that require a Proposed ADR. | +| ADR-0003 | Every documented surface carries complete, machine-checkable verification provenance. | The scanner validates discovered records but does not prove that every page contains a record, and its required-field validation does not cover the full declared schema. | Specify and implement repository-wide verification-presence and schema checks without moving source audit or verification judgement into the scanner. | +| ADR-0004 | Deterministic detection, efficient-model metadata audits, and frontier-model prose audits run as separate capability tiers. | Detection is deterministic, but the committed routine requires a frontier model end-to-end for both metadata and prose audits. | Add model routing after classification while preserving deterministic detection and fail-closed escalation. | +| ADR-0010 | The mechanically checkable parts of `CLAUDE.md` are enforced by CI. | `prose-guard` enforces the automated prose branch's file and release envelope, not terminology, prohibited wording, page templates, or verification-block presence. | Add a separate docs-content lint that runs after prose is written and on any PR that changes rendered docs or skill prose. The routine may invoke the same lint before opening a PR; CI remains the enforcement boundary. | +| ADR-0012 | `autonomi-developer` is a vendor-neutral portable skill distributed through multiple compatible channels. | The raw `SKILL.md` is manually consumable by compatible runtimes, but the first-class packaged and documented distribution is shaped around the Claude plugin aliases `developer`, `start`, and `/developer:start`. | Improve non-Claude installation and discoverability, evaluate skills.sh and other registries, and decide whether to realign the path or move the canonical artifact to `WithAutonomi/skills`. Preserve one canonical artifact and identity; treat any stable raw URL move as a major version migration under ADR-0013. | +| ADR-0013 | Stable canon is bundled; regenerable, fast-moving interface detail is fetched from live docs. Runtime freshness checks, pinned provenance, and stable manifest URLs provide independent staleness defences. | The skill bundles endpoint lists, constructors, command flags, method names, and installation details. Its runtime check compares release versions, so a metadata-only verification refresh with no version bump is not detected by an installed copy. Commit `2b290ef` also moved the stable raw URL without applying the required major-version migration. The promised bundled high-level post-quantum cryptography posture is absent. | Re-audit the bundle/pointer boundary, specify how runtime freshness detects verification movement without making activation noisy or brittle, and apply the major-version migration rule to the next stable raw URL move. Before adding post-quantum cryptography guidance, source-audit the authoritative components and add the required `feeds_skills` mappings and `verified_commits` entries so future changes trigger re-verification. Then add stable high-level guidance that distinguishes ML-KEM-768 and ML-DSA-65 transport security from content self-encryption, plus a pointer to the live post-quantum cryptography documentation. Do not add that skill content in PR 73. | +| ADR-0014 | The component registry is the machine-readable dependency graph for skill re-verification. | `feeds_skills` records the intended graph, but the scanner discovers skill dependencies from fixed `verified_commits` maps. Adding a registry edge alone does not add a watched skill dependency. | Make registry wiring and verification metadata agree, then have the scanner derive or validate skill dependency coverage from `feeds_skills`. | + +## Gaps requiring design confirmation + +| ADR | Question to resolve before implementation | +|---|---| +| ADR-0007 | Define the exact guarantee when issue, PR, or backlink creation fails after an earlier GitHub artifact has already been created. Decide whether compensation, reconciliation, or explicit partial-state reporting is the intended fail-closed behaviour. | +| ADR-0009 | Specify and implement the independent AI review panel against source evidence and repository policy. Define reviewer independence, agreement handling, human escalation, and measured evidence requirements. Roll out panel-gated automation to metadata updates first; add prose only after measured accuracy warrants it. Each track qualifies independently, and exact bars and measurement windows belong in the future panel specification. | + +## Proposed implementation slices + +### 1. Governance enforcement + +- Make the ADR Governance check required after the corrected workflow is green on the PR branch. +- Keep Accepted records immutable once merged to the default branch while allowing the introducing PR to remain reviewable until merge. +- Define how human review identifies architectural changes that omitted a required Proposed ADR. + +### 2. Verification completeness + +- Specify verification-presence and full-schema rules for docs pages and the developer skill. +- Add deterministic checks for missing records, missing fields, invalid modes, placeholder SHAs, and inconsistent skill metadata. +- Keep claim-level source audit and judgement outside the scanner. + +### 3. Model-tier routing + +- Keep `scripts/sweep_poll.py` deterministic and model-free. +- Route metadata-only audit work to an efficient subscription-backed model. +- Route prose audit, writing, and verification to a frontier subscription-backed model. +- Fail closed to human review when classification or routing is ambiguous. +- Keep concrete providers and model versions in operational configuration. + +### 4. Docs-content enforcement + +- Build a content lint separate from `prose-guard`. +- Run it after prose generation and on human-authored documentation PRs. +- Enforce terminology, prohibited wording, required page structure, and verification-record presence. +- Keep `prose-guard` focused on the automated branch's allowed-change envelope. + +### 5. Skill identity, freshness, and distribution + +- Re-audit bundled content against ADR-0013's stable-versus-volatile boundary. +- Preserve `autonomi-developer` as the canonical identity while treating `developer`, `start`, and `/developer:start` as Claude-channel aliases. +- Evaluate skills.sh and a multi-channel distribution workflow. +- Evaluate a move to `WithAutonomi/skills`; if chosen, draft a superseding ADR for location and apply ADR-0013's major-version treatment to stable manifest URLs. +- Improve freshness signalling so verification movement can be detected independently of release-version changes. + +### 6. Registry-driven skill maintenance + +- Define the consistency rule between `feeds_skills` and `verified_commits`. +- Make tooling detect missing or extra dependency edges. +- Use the registry to determine re-verification scope without conflating skills with Diátaxis pages. + +### 7. Fail-closed artifact reconciliation + +- Specify outcome semantics for partial GitHub writes. +- Make failure issues report durable partial state precisely. +- Add idempotent reconciliation where it reduces duplicate or stranded artifacts without hiding failures. + +### 8. Independent review-panel automation + +- Write the panel specification before changing the human merge boundary. +- Define independent provider or model lanes, source-evidence and repository-policy review, agreement handling, and human escalation for disagreement, uncertainty, or insufficient evidence. +- Put exact accuracy bars, measurement windows, and qualification evidence in that specification. +- Introduce panel-gated automation for metadata updates first while retaining human review for prose. +- Let the prose track qualify later only when its measured accuracy meets the specified bar; preserve independent qualification and rollback for each track. + +## Verification principle + +Each follow-up slice must verify implementation against the governing ADR. If a slice discovers that the intended invariant is no longer wanted, stop and propose a superseding ADR rather than changing the implementation and silently redefining the decision. diff --git a/planning/gsd-pr-73-checkpoint.md b/planning/gsd-pr-73-checkpoint.md new file mode 100644 index 0000000..1efc8b9 --- /dev/null +++ b/planning/gsd-pr-73-checkpoint.md @@ -0,0 +1,173 @@ +# GSD Checkpoint — PR 73 ADR corrections + +Date: 2026-07-21 +Project: Autonomi Developer Documentation +Slice/question: Correct PR 73's retrospective ADRs and relocated governance +Prepared by: OpenCode orchestrator +Agents/tools used: OpenCode, local code reviewer; final ADR, adversarial, Craft, and panel reviewers pending + +## Status + +Continue — approved local correction set implemented; awaiting final review and CI + +Meaningful work-unit? Yes — this changes ADR governance, immutable records, CI validation, and the durable account of prior architectural decisions. +Review cadence: latest local code review addressed; final ADR, adversarial, Craft, and panel review not yet run on the corrected implementation +Unreviewed backlog if deferred: complete corrected PR 73 work-unit + +## What happened + +- Repaired the `/adr` relocation across governance code, workflow paths, tooling guidance, and supersession metadata. +- Replaced implementation-ratification wording with owner-confirmed retrospective acceptance wording in ADR-0003 through ADR-0014. +- Corrected the Claude Code Remote routine, information-architecture authority, skill identity, vendor-neutral distribution, URL migration, release-date, and maintenance records agreed during review. +- Added `planning/adr-implementation-conformance.md` to separate intended decisions from implementation gaps. +- Drafted ADR-0015 to govern owner-confirmed retrospective acceptance and make the default branch the Accepted-record immutability boundary. Jim accepted it after review on 2026-07-16. +- On 2026-07-16, Jim decided the broad checker self-hardening was overbuilding. The governance implementation now follows David's established team-standard checker, adapted only for this repo's root-level `/adr` directory, the one-time `docs/adr` relocation, and ADR-0015 Acceptance metadata. +- Removed workflow self-inspection, action and command allowlists, symlink and execution-context threat-model machinery, and the adversarial tests dedicated to those mechanisms. +- Organization-wide validation from a trusted external checker remains a possible separate cross-repo follow-up. It is not required by PR 73. +- Addressed the latest local code review on 2026-07-20: local branch checks now compare against the merge base with `origin/main` (falling back to local `main`) instead of `HEAD^1`, while pull-request and push CI retain `GITHUB_BASE_REF` and `GITHUB_EVENT_BEFORE` handling. +- Added focused coverage proving an Accepted ADR introduced on a feature branch can be corrected across multiple commits before merge without weakening the default-branch immutability boundary. +- The checker now validates the current `.adr-kit.yaml` and fails when its `adr_directory` differs from the hard-coded `ADR_DIR`. Focused regression coverage protects the check. +- Restored executable mode on `scripts/adr-governance.py`. +- On 2026-07-20, Jim resolved the ADR-0013/ADR-0014 metadata ambiguity: `verified_date` belongs only in `skills/start/SKILL.md`, while `skills/start/version.json` is the runtime version manifest and mirrors the fields needed for runtime and external inspection, including `version`, `verification_mode`, and `verified_commits`. Avoiding a duplicate review date removes unnecessary synchronization. +- Amended ADR-0013 and ADR-0014 within their introducing PR to state that contract consistently. ADR-0015 permits these review corrections before the records reach the default-branch immutability boundary. This amendment matches the existing explicit `skills/start/MAINTAINING.md` contract and creates no implementation debt. +- Rebased the complete local correction series cleanly onto the updated remote PR head `51724df` after `main` advanced. All six correction commits remain local and unpushed. +- Aligned ordinary validation with David's team-standard changed-file scope: when a comparison base exists, format, status, required-section, and Acceptance-metadata checks apply only to current ADR files changed against that base; without a base, they apply to all current ADRs. Duplicate-number checking still covers all current ADRs, and every Accepted ADR on the base remains byte-for-byte immutable. +- Added focused push-event tests for a normal `GITHUB_EVENT_BEFORE` SHA and the all-zero initial-push fallback. Both prove that edits to an Accepted ADR on the selected base fail governance. +- Added the repo-root ADR governance instructions to `CLAUDE.md` and refreshed the implementation-conformance review date to 2026-07-20. Malformed ADR-prefix discovery remains an explicitly out-of-scope limitation of the shared standard. +- Retained workflow `permissions: contents: read` and checkout `persist-credentials: false` as ordinary least-privilege runner hygiene. They are not self-defending governance hardening; this correction adds no workflow self-inspection or action allowlists. +- Addressed the final ADR review's four findings in a separate local commit: made ADR-0014's targeted-foundational scope conditional on actual `feeds_skills` mappings; clarified ADR-0003's rendered-doc, skill-frontmatter, and runtime-manifest metadata schemas; separated default-branch merged truth from the installable/released constraint on installation and version surfaces; and recorded ADR-0013's missing bundled high-level post-quantum cryptography posture as implementation debt rather than adding skill content to PR 73. +- Addressed the follow-up code review by naming ADR-0003's model per-surface rather than per-claim and requiring the future post-quantum cryptography skill work to source-audit and wire its authoritative components through `feeds_skills` and `verified_commits` before adding bundled guidance. +- Completed the per-surface terminology correction throughout ADR-0003 after repeat review found three residual per-claim statements in its context, decision drivers, and consequences. +- The final ADR review has not rerun on those corrections. Final ADR, clean-context, adversarial, Craft, and panel review and exact-HEAD CI remain pending. No push is approved. +- On 2026-07-21, Jim confirmed the ADR artifact boundary: Accepted ADRs preserve original intent and accepted future architecture even when unimplemented, while mutable mechanics and rollout sequencing belong in planning, specifications, maintainer guidance, and tooling. +- Amended ADR-0006, ADR-0009, and ADR-0014 within their introducing PR to retain their durable invariants while moving or pointing mutable mechanics to the existing operational sources. ADR-0006 now has the outcome-focused path `adr/ADR-0006-separate-metadata-and-prose-update-tracks.md`; the exact envelopes remain in `planning/routines/upstream-sweep.md` and the existing guard workflows. ADR-0009 still commits to the unbuilt independent AI review panel, and its rollout sequence now lives in `planning/adr-implementation-conformance.md`. +- Added truthful `Reviewers: Jim Collinson` metadata to prospective ADR-0002 and ADR-0015. Retrospective ADRs remain without invented reviewer metadata. +- At the point of Jim's amendment decision, local evidence required a rerun and final ADR, clean-context, adversarial, Craft, and panel reviews remained pending. The remote PR head remains `51724df`; no push is approved. +- Aligned the routine's future auto-merge note with ADR-0009: structural guard history is not enough to replace human review without the specified and proven independent panel. Preserved ADR-0014's two skill-authoring carve-outs in `skills/start/MAINTAINING.md`, clarified numbering and human acceptance authority in `adr/README.md`, and corrected the local governance evidence count. + +## Evidence + +CI arbiter / green of record: + +- Location: PR 73 GitHub checks. +- Status: Not run for this local correction set. The remote PR head before the local commits is `51724df`; the local correction commits remain unpushed. Existing remote checks do not cover exact local HEAD. + +Local fast gate / `.gsd/gate.sh`: + +- Installed? N/A — no `.gsd/gate.sh` exists in this repo. +- Commands run: + - `python3 -I -m unittest discover -s scripts/tests -p 'test_adr_governance.py'` + - `python3 -I scripts/adr-governance.py` + - `GITHUB_BASE_REF=main python3 -I scripts/adr-governance.py` + - `GITHUB_EVENT_BEFORE="$(git rev-parse origin/main)" python3 -I scripts/adr-governance.py` + - `python3 -m py_compile scripts/adr-governance.py scripts/tests/test_adr_governance.py` + - `git diff --check` +- Result: Local checks pass after the artifact-boundary amendment: 20 focused integration tests passed; ordinary, pull-request, and push governance modes each validated 15 changed ADRs; compilation and diff checks passed. The push-event tests separately exercise Accepted-ADR immutability for both normal and all-zero `GITHUB_EVENT_BEFORE` paths. This is provisional local evidence, not final review or CI; final ADR, clean-context, adversarial, Craft, and panel reviews remain pending. + +Files changed/artifacts produced: + +- ADR-0002 through ADR-0014 corrections. +- Accepted ADR-0015. +- ADR README, template, and tooling guidance. +- ADR governance workflow, script, and integration tests. +- Remote routine policy/prompt and skill maintainer guidance. +- Skill open questions, implementation-conformance plan, work packet, and this checkpoint. +- Final bounded correction: `CLAUDE.md`, `scripts/adr-governance.py`, `scripts/tests/test_adr_governance.py`, `planning/adr-implementation-conformance.md`, and this checkpoint. +- Final ADR-review correction: ADR-0003, ADR-0014, `planning/adr-implementation-conformance.md`, and this checkpoint. The skill implementation is unchanged. +- Artifact-boundary amendment: ADR-0002, renamed ADR-0006, ADR-0009, ADR-0014, ADR-0015, `planning/adr-implementation-conformance.md`, and this checkpoint. Guard behavior and skill content are unchanged. + +## Honesty rules check + +- No-harness-modification: Pass — governance workflow/test changes were explicitly in scope and reviewed as the subject of the slice. +- Baseline-diff for evidence: Pass — no failure was dismissed as environmental, flaky, or pre-existing. +- Evidence reproducible-from-branch: Pass for the approved correction-set commit — the checks use only files included in the commit, with no helper scripts or hidden environment state. +- Local vs CI consistency: Unknown until an approved push runs CI on the exact reviewed SHA. + +## Review findings + +Local code review: + +- Reviewer/tool: latest local code review completed before the 2026-07-20 correction pass. +- Result: Findings addressed locally; final review remains pending. +- Findings: HIGH — replace `HEAD^1` fallback with the default-branch merge base and prove multi-commit introducing-PR corrections; MEDIUM — validate current `.adr-kit.yaml` against `ADR_DIR`; LOW — restore checker executable mode. +- Dispositions: All three fixes and both focused regressions are included in the correction set. The checker remains deliberately small; workflow self-inspection, action allowlists, broad symlink/execution hardening, and an external trusted validator were not reintroduced. +- Final pre-push correction status: implemented and locally verified, but no final ADR, clean-context, adversarial, Craft, or panel review has run on the bounded correction. + +Final ADR review: + +- Reviewer/tool: final ADR review completed against `c515299`; rerun on the correction commit is pending. +- Result: Four findings corrected in a separate local commit; dispositions await review confirmation. +- Findings and dispositions: ADR-0014 overstated targeted-foundational skill dependencies — restricted to components actually mapped through `feeds_skills`; ADR-0003 conflated the complete rendered-doc schema with intentionally split skill metadata — clarified each artifact contract; ADR-0003 conflated merged source truth with released truth for all pages — limited the installable/released constraint to installation, download, package, and version surfaces; ADR-0013 promised bundled high-level post-quantum cryptography guidance that the skill lacks — recorded the precise minimum follow-up in the conformance plan without changing the skill. +- Rerun status: Not run on the correction commit. This is a disposition record, not a claim of final review approval. + +Follow-up code review: + +- Reviewer/tool: code review completed against `8ff203a`; rerun on the follow-up correction commit is pending. +- Result: Two findings corrected locally; dispositions await review confirmation. +- Findings and dispositions: ADR-0003 used per-claim language for a per-surface model — corrected; the planned post-quantum cryptography guidance omitted the registry and verification-metadata work needed to keep it fresh — added source-audit, `feeds_skills`, and `verified_commits` prerequisites to the future conformance slice. +- Repeat review found residual per-claim wording at ADR-0003 lines 15, 22, and 52. All three statements now describe documented surfaces or verification records; rerun on the final correction commit is pending. + +ADR governance review: + +- Reviewer/tool: Pending. +- Result: Not run on the 2026-07-20 corrected implementation. +- Findings: Pending. + +Clean-context test: + +- Reviewer/tool: Not yet run on the simplified implementation. +- Result: Not run. +- Findings: Earlier review established the ADR-content corrections and Jim accepted ADR-0015; it does not establish readiness of the simplified checker. + +Adversarial review: + +- Reviewer/tool: fresh adversarial agents across repeated correction cycles. +- Required? Yes — governance and immutable records are meaningful shared work. +- Result: Not run on the simplified implementation. +- Findings: The earlier bypass-oriented review drove the overbuilding that Jim rejected on 2026-07-16. A focused review remains pending. + +Craft Review: + +- Reviewer/tool: fresh Craft reviewer. +- Required? Yes — shared governance and documentation changes. +- Verdict: Not run on the simplified implementation. +- CONFORMANCE findings and dispositions: Pending. +- SIMPLICITY / NIT findings carried: Pending. + +Panel review: + +- Reviewer/tool: Claude Opus panel lane. +- Result: Not run on the simplified implementation. +- Findings: Earlier panel findings remain relevant to ADR content, not checker readiness. + +## Drift / scope concerns + +- Jim determined that treating the repository-controlled workflow and checker as mutually self-defending security boundaries was overbuilding. PR 73 now preserves the established team-standard maintenance shape and only adds relocation and Acceptance-metadata behaviour required by this repo's Accepted decisions. +- A trusted external validator could address organization-wide self-modification concerns without making each repository checker self-inspecting. That is a separate design and rollout question, not PR 73 scope. +- Making ADR Governance a required branch-protection check remains out of scope and is tracked in `planning/adr-implementation-conformance.md`. +- Malformed ADR-prefix discovery is a known limitation of David's shared standard and is explicitly out of scope; no discovery logic or tests were added. +- Workflow read-only contents permission and disabled credential persistence are retained as ordinary runner hygiene, not treated as a governance security boundary. Workflow self-inspection and action allowlists remain out of scope. +- Model routing, docs-content lint, skill restructuring, registry-driven dependency discovery, and distribution publication remain future slices. +- The missing high-level post-quantum cryptography posture remains a tracked ADR-0013 conformance gap. PR 73 does not add or otherwise alter skill content. + +## Open questions / decisions for Jim + +- Resolved: Jim accepted ADR-0015 on 2026-07-16. +- Resolved: Jim directed the governance simplification on 2026-07-16 because broad self-hardening was overbuilding. +- Resolved: Jim decided on 2026-07-20 that `verified_date` lives only in `skills/start/SKILL.md`; `version.json` deliberately omits it and mirrors only the runtime/external manifest fields that need mirroring. +- Open: rerun final ADR review, then complete adversarial, Craft, and panel review plus CI evidence for the exact corrected HEAD. +- Resolved: Jim confirmed the durable-ADR versus mutable-implementation boundary and required prospective reviewer metadata on ADR-0002 and ADR-0015 only. + +PR / upstream action gate: + +- PR ready to raise? Existing PR only; the local correction set is not CI-verified. +- Jim confirmed PR may be updated? No push action is approved for this step. +- Draft PR title/description prepared: Existing PR 73 description will need updating after approval. + +## Recommended next step + +Rerun the final ADR review on the separate correction commit, then run the pending adversarial, Craft, and panel reviews. Seek explicit approval before pushing, then confirm PR 73 CI on the pushed SHA. + +## Handoff note + +Jim's 2026-07-16 simplification decision supersedes stale readiness claims for the overbuilt checker. The correction series addresses the local code- and ADR-review findings while preserving the shared checker's changed-file scope and the existing `MAINTAINING.md` contract. The series is rebased onto remote PR head `51724df`; exact local HEAD remains unpushed. Final ADR, clean-context, adversarial, Craft, and panel reviews and exact-SHA CI remain pending. The no-push gate remains in force. diff --git a/planning/gsd-pr-73-work-packet.md b/planning/gsd-pr-73-work-packet.md new file mode 100644 index 0000000..89472bc --- /dev/null +++ b/planning/gsd-pr-73-work-packet.md @@ -0,0 +1,133 @@ +# GSD Work Packet — PR 73 ADR corrections + +Date: 2026-07-15 +Prepared by: OpenCode orchestrator +Requested agent/tool: OpenCode with independent ADR, adversarial, Craft, and panel review +Role requested: Coordinator / Implementer / Verifier +Review mode: ADR governance review, Adversarial review, Craft review, and multi-model panel + +## Project / workspace + +Project: Autonomi Developer Documentation +Repo/path: `WithAutonomi/autonomi-developer-docs` +Current source of truth: PR 73 branch `adr/relocate-and-retrospective-records` plus the decisions confirmed by Jim in the review discussion + +## Owner-confirmed decisions + +- Retrospective ADRs enter as Accepted records because Jim, the original decision owner, confirms that they faithfully capture decisions made before ADR governance existed. Their introducing PR remains reviewable until merge; immutability applies to the Accepted content on the default branch. +- ADR-0004 and ADR-0010 record intended invariants even where implementation currently diverges. Those divergences belong in planning, not in replacement ADRs unless the intended decision changes. +- The scheduled sweep is a Claude Code Remote routine using subscription-backed execution rather than metered API billing. The present implementation depends on Jim's Claude account, but the decision does not foreclose future subscription-backed providers, independent models, multimodal capabilities, or shared ownership. +- ADR-0011 itself is the information-architecture authority. Navigation and planning files are implementations or historical context, not alternative sources of the decision. +- `autonomi-developer` is the canonical vendor-neutral skill identity. Claude's `developer`, `start`, and `/developer:start` names are channel-specific packaging. Compatible runtimes can consume the raw artifact manually, and future multi-channel distribution may include skills.sh or a move to `WithAutonomi/skills`. + +## Goal + +Correct PR 73 so the relocated ADR governance works, the retrospective records accurately distinguish decisions from implementation conformance, and known implementation gaps are tracked outside the immutable ADRs. + +## Read first + +- `CLAUDE.md` +- `adr/ADR-0001-adopt-architecture-decision-records.md` +- `adr/ADR-0002-adrs-live-at-repo-root-not-docs.md` +- `adr/ADR-0003-verification-block-source-of-truth.md` through `adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md` +- `adr/TEMPLATE.md` +- `adr/TOOLING.md` +- `.adr-kit.yaml` +- `scripts/adr-governance.py` +- `.github/workflows/adr-governance.yml` +- `planning/routines/upstream-sweep.md` +- `planning/routines/upstream-sweep-prompt.md` +- `skills/start/` + +## Stage + +Implementation and verification + +## Approved slice + +- Repair every stale `docs/adr` assumption in the relocated governance implementation and guidance. +- Add regression coverage proving `/adr` records are validated and Accepted records remain immutable. +- Keep ADR-0002 Accepted and make it supersede ADR-0001's location clause only. +- Replace ADR-0003 through ADR-0014's retrospective acceptance wording with the owner-confirmed wording agreed in review. +- Correct factual reconstruction where agreed: the Claude Code Remote routine, the site-wide `llms.txt`, ADR-0011 as the information-architecture authority, and the known provenance of `skills/start`. +- Make ADR-0012 unambiguous that `autonomi-developer` is the canonical vendor-neutral identity; Claude packaging is one current adapter, while compatible runtimes can consume the portable artifact manually. +- Add a planning artifact for current implementation-conformance gaps. +- Show Jim the complete local diff before any commit or push. + +## Relevant artifacts + +PR: `https://github.com/WithAutonomi/autonomi-developer-docs/pull/73` +ADR governance: ADR-0001 and `.adr-kit.yaml` +Plan/state: this packet and the conformance-gap artifact produced by the slice +Previous review: PR 73 review discussion in the active session + +## Scope + +- ADR governance script, test, workflow, and tooling guidance. +- ADR metadata and narrowly agreed factual wording. +- Planning documentation for implementation debt. + +## Out of scope + +- Implementing efficient/frontier model routing. +- Building terminology, prohibited-word, template, or verification-presence lint. +- Reworking the skill's bundled-versus-pointered content. +- Publishing to skills.sh or moving the skill to `WithAutonomi/skills`. +- Wiring `feeds_skills` into scanner dependency discovery. +- Changing repository branch-protection settings. +- Committing, pushing, merging, or publishing. + +## Constraints / forbidden actions + +- Do not change a retrospective decision merely to match a nonconforming implementation. +- Do not add fictional historical reviewers or acceptance ceremonies. +- Do not mark any new ADR Accepted autonomously. +- Do not edit `.gsd/gate.sh`, CI outside the explicitly in-scope ADR workflow, test harnesses outside the new governance regression coverage, or environment setup. +- Do not commit or push without Jim's explicit approval after diff review. + +## Unattended mode / rigor profile + +Unattended mode: No + +Rigor profile: + +- Meaningful governance and documentation work-unit. +- Run local governance and regression checks. +- Run fresh ADR, adversarial, Craft, and panel reviews before checkpoint. +- CI is the green of record, but no push is approved in this slice; report local evidence as provisional and state that PR CI has not run on the local changes. + +## Verification required + +- `python3 -m unittest discover -s scripts/tests -p 'test_adr_governance.py'` +- `python3 scripts/adr-governance.py` +- `GITHUB_BASE_REF=main python3 scripts/adr-governance.py` +- `git diff --check` +- Search for stale operational `docs/adr` references. +- Inspect the full diff against `origin/main` and the local incremental diff. +- Independent ADR governance review. +- Independent adversarial review. +- Independent Craft review. +- Independent panel review. + +## Stop conditions + +Stop and report if: + +- the governance repair requires weakening Accepted-ADR immutability; +- a proposed wording change alters a confirmed historical decision rather than clarifying its record; +- implementation requires model-routing, broad docs lint, skill redesign, distribution publication, or branch-protection changes; +- a readiness claim would depend on uncommitted helper scripts or hidden environment state; +- local evidence conflicts with PR CI after any later approved push; +- any commit, push, merge, or publication is requested implicitly rather than explicitly approved. + +## Required output + +Return: + +- sources read; +- files changed; +- exact verification evidence; +- independent review findings and dispositions; +- remaining risks and implementation gaps; +- the complete uncommitted diff summary for Jim's review; +- no commit or push. diff --git a/planning/routines/upstream-sweep-prompt.md b/planning/routines/upstream-sweep-prompt.md index 0936194..86ef1a5 100644 --- a/planning/routines/upstream-sweep-prompt.md +++ b/planning/routines/upstream-sweep-prompt.md @@ -1,6 +1,6 @@ # Upstream sweep — routine prompt -This is the prompt the Claude Desktop Remote routine executes once per day. The behaviour lives in version control so it is reviewable, diffable, and rollback-able. The Claude Desktop routine config references this file by URL or paste-in. Schedule, model selection, and any `GITHUB_TOKEN` secret value live in Claude Desktop, not in this repo. +This is the prompt the Claude Code Remote routine executes once per day. The behaviour lives in version control so it is reviewable, diffable, and rollback-able. The hosted routine carries a short bootstrap that instructs the agent to read this committed file from the fresh clone and follow it exactly. Schedule, model selection, and any `GITHUB_TOKEN` secret value live in the routine service, not in this repo. ## Goal diff --git a/planning/routines/upstream-sweep.md b/planning/routines/upstream-sweep.md index 4e4dd27..79b681e 100644 --- a/planning/routines/upstream-sweep.md +++ b/planning/routines/upstream-sweep.md @@ -8,7 +8,7 @@ The routine is the hosted-scheduled equivalent of Tier 1 + Tier 2 from `planning ## Trigger shape -- Execution venue: Claude Desktop → Routines → New routine → Remote. +- Execution venue: Claude Code Remote routine. - Schedule: daily at 09:00 UTC. Comfortably above the documented one-hour minimum interval for Remote-routine schedules. - Routine model: **Opus 4.8 or higher** end-to-end. The audit/write/verify loop in `## Opus audit/write/verify loop` requires the model to inspect upstream diffs and source at pinned SHAs, compare against docs and `SKILL.md`, write actual prose into draft PRs, and run practical verification. No subagent layer. - Prompt: the committed prompt at `planning/routines/upstream-sweep-prompt.md`. @@ -285,7 +285,7 @@ That page is held back as a `manual review needed` issue. - Deleted or renamed source repo. Same handling. - Force-push that invalidates a recorded SHA. Caught by the `sweep-sha-reachability` required check on the resulting PR. - Routine missing the 09:00 UTC slot. Recovery: next-day run picks up the drift; latency at most 24h. No GitHub artifact. -- Claude Desktop routine credential or token expiry. Routine fails closed; the failure issue captures the diagnostic; user refreshes the secret. +- Claude Code Remote routine credential or token expiry. Routine fails closed; the hosted run history captures the diagnostic. The routine opens a failure issue only when its GitHub write credentials still permit issue creation. - Daily routine-start cap exhausted on Max plan. Same recovery as missed slot. - Collision skip when a prior PR is left open for many days. The open PR is itself the signal; the next-day run resumes once the prior PR merges or closes. - SHA-fetch failure for a single record (both fetch paths). Recovery: page deferred to an `upstream-sweep-manual-review` issue; the rest of the run proceeds. @@ -311,12 +311,12 @@ Forward compatibility: when `notify-docs.yml` rolls out in upstream repos, the s If either label is missing, the routine still creates the issue when needed, falls back to creating it unlabeled, and adds an in-body note explaining the missed label and the underlying `gh` error. The labels can be created and reapplied to existing issues at any time. 2. Add `sweep-guard`, `prose-guard`, and `sweep-sha-reachability` (the exact workflow `name:` strings) as required checks on `main` in repo settings → Branch protection. 3. Confirm the routine's bot identity has push access. If branch protection requires reviewers, ensure the bot is granted bypass or has CODEOWNERS coverage on `planning/sweeps/*` and the metadata-only paths, or accept that the user reviews each PR by hand. -4. If using a `GITHUB_TOKEN` secret, provision it in the Claude Desktop routine config per the credentials section above. The secret is optional; when unset the routine derives a token from `gh auth token` and falls back to anonymous reads. +4. If using a `GITHUB_TOKEN` secret, provision it in the Claude Code Remote routine config per the credentials section above. The secret is optional; when unset the routine derives a token from `gh auth token` and falls back to anonymous reads. ## Out of scope -- Claude Desktop Remote routine config (model = Opus 4.8 or higher, schedule, any optional `GITHUB_TOKEN` secret value) — lives in Claude Desktop, not in this repo. The behaviour (the prompt) is committed at `planning/routines/upstream-sweep-prompt.md`. +- Claude Code Remote routine config (model = Opus 4.8 or higher, schedule, any optional `GITHUB_TOKEN` secret value) lives in the hosted routine service, not in this repo. The behaviour (the prompt) is committed at `planning/routines/upstream-sweep-prompt.md`. - `notify-docs.yml` installation in any upstream repo — tracked under `planning/implementation-plan.md` Section 8.2. - Per-event sweep PRs — deferred to v2 of the trigger shape. -- Auto-merge — deferred to v1.5. The label-driven `sweep-auto-merge` workflow ships once all three required checks have run cleanly for several weeks. +- Auto-merge — remains out of scope until the independent review panel in ADR-0009 is specified, implemented, and proven against the measured qualification criteria for each update track. A history of clean structural guard checks is necessary evidence, but is not sufficient on its own to replace human review. - Terminology lint, Greptile reviewer setup, and broader v2 verification-staleness checks beyond the sweep envelope. diff --git a/planning/skill-open-questions.md b/planning/skill-open-questions.md new file mode 100644 index 0000000..c71bf77 --- /dev/null +++ b/planning/skill-open-questions.md @@ -0,0 +1,30 @@ +# Skill — open questions + +Tracking notes for unresolved items on the Autonomi developer skill. Referenced from ADR-0012/0013/0014 (which record the *decisions*; this file holds the *open items* those decisions leave, so the ADRs stay durable and honest without becoming trackers). Resolving any of these that changes a recorded decision is done via a **superseding ADR**, not by editing the accepted one. + +## 1. Canonical name vs on-disk name + +- **Decision of record (ADR-0012):** the canonical name is `autonomi-developer` — vendor-neutral and self-describing. +- **Current repo state:** the skill ships as `skills/start/` (`name: start`, plugin `developer`, invocation `/developer:start`). +- **The gap:** commit `2b290ef` introduced `start` to produce the concise Claude-plugin slash form `/developer:start`; a vendor channel therefore shaped the current package identity, and `start` is opaque to non-Claude loaders. That channel-specific alias does not replace the canonical `autonomi-developer` identity recorded in ADR-0012. +- **To do:** decide whether to realign the on-disk name to `autonomi-developer` (keeping `/developer:start` only as a Claude-channel alias). Realigning the implementation with ADR-0012 needs no new ADR, but moving the stable raw URL requires a major version and migration under ADR-0013; choosing a different canonical identity does require a superseding ADR. + +## 2. Possible migration to `WithAutonomi/skills` + +- The user-facing `autonomi` skill (operate the network — nodes now; uploads/data management later) lives in `WithAutonomi/skills`. This developer skill currently lives beside the docs and is assembled by the same tooling/automation. +- **Open question:** whether this skill should migrate to `WithAutonomi/skills`, trading the "one PR updates skill + context" benefit of co-location for a dedicated skills home. +- **To do:** study the `autonomi` skill's distribution, naming, and conventions in that repo; reconcile them with ADR-0012/0013/0014 at migration time. A migration would supersede the location (and possibly naming) parts of ADR-0012 and the manifest-URL of ADR-0013. + +## 3. Draft → stable promotion + +- The skill sits at `0.1.x-draft`. The `-draft` suffix is held until a deliberate re-verification pass and a live-devnet exercise confirm the bundled commands/flows against a real network. +- **To do:** define and run that promotion gate, then cut a non-draft release. + +## 4. Distribution channels beyond the repo + +- Distribution is vendor-agnostic (ADR-0012). Compatible runtimes can consume the raw `SKILL.md` manually, while the only first-class packaged and documented distribution currently uses Claude-specific plugin conventions. `skills.sh`, an Anthropic skills directory, and other channels are anticipated. +- **To do:** decide which additional channels to publish through and in what priority. + +--- + +_Resolved since genesis (kept out of the open list): `feeds_skills:` applied to `component-registry.yml`; `MAINTAINING.md` placed skill-local; real `verified_commits` SHAs replacing the initial placeholders._ diff --git a/scripts/adr-governance.py b/scripts/adr-governance.py index 7f56bde..dbd2eab 100755 --- a/scripts/adr-governance.py +++ b/scripts/adr-governance.py @@ -1,13 +1,5 @@ #!/usr/bin/env python3 -"""Repository-local ADR governance checks. - -Enforces: -- ADR files live under docs/adr/ and use ADR-NNNN-short-title.md. -- Required sections exist. -- Status is present and from the allowed lifecycle. -- Accepted ADRs are immutable after acceptance. If a decision changes, create a - new ADR and supersede by reference rather than editing the Accepted ADR. -""" +"""Repository-local ADR governance checks.""" from __future__ import annotations import os @@ -16,106 +8,211 @@ import sys from pathlib import Path -ADR_DIR = Path("docs/adr") +ADR_DIR = Path("adr") +ADR_CONFIG = Path(".adr-kit.yaml") ALLOWED_STATUSES = {"Proposed", "Accepted", "Superseded", "Deprecated", "Rejected"} REQUIRED_SECTIONS = ["Context", "Decision", "Consequences", "Validation"] FILENAME_RE = re.compile(r"^ADR-\d{4}-[a-z0-9][a-z0-9-]*\.md$") STATUS_RE = re.compile(r"(?im)^\s*(?:[-*]\s*)?.*?Status.*?:\s*(.+?)\s*$") +ACCEPTANCE_RE = re.compile( + r"(?im)^\s*[-*]\s+\*\*Acceptance:\*\*\s*" + r"(Prospective|Retrospective)\s+—\s+(.+?)\s*$" +) def run(cmd: list[str]) -> str: - return subprocess.check_output(cmd, text=True, stderr=subprocess.DEVNULL).strip() + try: + return subprocess.check_output( + cmd, text=True, encoding="utf-8", stderr=subprocess.STDOUT + ).strip() + except subprocess.CalledProcessError as exc: + detail = exc.output.strip() or f"exit {exc.returncode}" + raise RuntimeError(f"{' '.join(cmd)} failed: {detail}") from exc def status_of(text: str) -> str | None: - m = STATUS_RE.search(text) - return m.group(1).strip().strip("*").strip() if m else None + match = STATUS_RE.search(text) + return match.group(1).strip().strip("*").strip() if match else None def base_ref() -> str | None: ref = os.environ.get("GITHUB_BASE_REF") if ref: - return f"origin/{ref}" - # On push, compare against first parent where available. - try: - return run(["git", "rev-parse", "HEAD^1"]) - except Exception: - return None + base = f"origin/{ref}" + run(["git", "rev-parse", "--verify", f"{base}^{{commit}}"]) + return base + before = os.environ.get("GITHUB_EVENT_BEFORE") + if before and before != "0" * 40: + run(["git", "rev-parse", "--verify", f"{before}^{{commit}}"]) + return before -def changed_files_against_base(base: str) -> list[str]: - try: - return run(["git", "diff", "--name-only", f"{base}...HEAD"]).splitlines() - except Exception: + for default_branch in ("origin/main", "main"): try: - return run(["git", "diff", "--name-only", f"{base}", "HEAD"]).splitlines() - except Exception: - return [] + run(["git", "rev-parse", "--verify", f"{default_branch}^{{commit}}"]) + return run(["git", "merge-base", "HEAD", default_branch]) + except RuntimeError: + continue + return None + + +def file_at(ref: str, path: Path) -> str: + return run(["git", "show", f"{ref}:{path.as_posix()}"]) -def file_at(ref: str, path: str) -> str | None: +def file_bytes_at(ref: str, path: Path) -> bytes: try: - return run(["git", "show", f"{ref}:{path}"]) - except Exception: - return None + return subprocess.check_output( + ["git", "show", f"{ref}:{path.as_posix()}"], stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError as exc: + detail = exc.output.decode(errors="replace").strip() or f"exit {exc.returncode}" + raise RuntimeError(f"git show {ref}:{path} failed: {detail}") from exc + + +def adr_directory_from_config(text: str, source: str) -> Path: + match = re.search(r"(?m)^adr_directory:\s*['\"]?([^'\"\s]+)", text) + if not match: + raise RuntimeError(f"{source}: missing adr_directory") + return Path(match.group(1)) + + +def adr_paths_at(ref: str, directory: Path) -> list[Path]: + output = run( + ["git", "ls-tree", "-r", "--name-only", ref, "--", directory.as_posix()] + ) + return [ + Path(name) + for name in output.splitlines() + if Path(name).parent == directory + and Path(name).name.startswith("ADR-") + and Path(name).suffix == ".md" + ] + + +def changed_adr_paths_against_base(base: str) -> list[Path]: + try: + output = run(["git", "diff", "--name-only", f"{base}...HEAD"]) + except RuntimeError: + output = run(["git", "diff", "--name-only", base, "HEAD"]) + return sorted( + path + for name in output.splitlines() + if (path := Path(name)).parent == ADR_DIR + and path.name.startswith("ADR-") + and path.suffix == ".md" + and path.is_file() + ) def main() -> int: errors: list[str] = [] - if not ADR_DIR.exists(): - print("No docs/adr directory; nothing to validate.") - return 0 - adr_files = sorted(p for p in ADR_DIR.glob("ADR-*.md") if p.is_file()) - base = base_ref() - changed = changed_files_against_base(base) if base else [] - changed_adr_paths = {Path(name) for name in changed if name.startswith("docs/adr/ADR-") and name.endswith(".md")} + try: + current_directory = adr_directory_from_config( + ADR_CONFIG.read_text(encoding="utf-8"), str(ADR_CONFIG) + ) + except (OSError, RuntimeError) as exc: + print(f"ADR governance failed:\n- cannot validate ADR configuration: {exc}") + return 1 + + if current_directory != ADR_DIR: + print( + "ADR governance failed:\n" + f"- {ADR_CONFIG}: adr_directory is {current_directory}, " + f"but the checker uses {ADR_DIR}" + ) + return 1 + + if not ADR_DIR.is_dir(): + print(f"ADR governance failed:\n- ADR directory does not exist: {ADR_DIR}") + return 1 + + adr_files = sorted(path for path in ADR_DIR.glob("ADR-*.md") if path.is_file()) - # Grandfather legacy ADRs when first installing governance. Enforce full - # structure on ADRs touched by this PR, while still checking duplicate - # numbers across the full directory. - files_to_validate = sorted((Path(p) for p in changed_adr_paths if Path(p).exists()), key=str) if base else adr_files + try: + base = base_ref() + base_directory = None + base_paths: list[Path] = [] + base_files: dict[str, tuple[Path, bytes]] = {} + if base: + base_config = file_at(base, ADR_CONFIG) + base_directory = adr_directory_from_config( + base_config, f"{base}:{ADR_CONFIG}" + ) + base_paths = adr_paths_at(base, base_directory) + base_files = { + path.name: (path, file_bytes_at(base, path)) for path in base_paths + } + files_to_validate = changed_adr_paths_against_base(base) + else: + files_to_validate = adr_files + except RuntimeError as exc: + print(f"ADR governance failed:\n- cannot establish comparison base: {exc}") + return 1 seen_numbers: dict[str, Path] = {} for path in adr_files: - number = path.name.split("-", 2)[1] if "-" in path.name else path.name + number = path.name.split("-", 2)[1] if number in seen_numbers: - errors.append(f"{path}: duplicate ADR number also used by {seen_numbers[number]}") + errors.append( + f"{path}: duplicate ADR number also used by {seen_numbers[number]}" + ) seen_numbers[number] = path + base_accepted_names = { + name + for name, (_, content) in base_files.items() + if status_of(content.decode("utf-8")) == "Accepted" + } for path in files_to_validate: if not FILENAME_RE.match(path.name): errors.append(f"{path}: filename must match ADR-NNNN-short-title.md") text = path.read_text(encoding="utf-8") - st = status_of(text) - if not st: + status = status_of(text) + if not status: errors.append(f"{path}: missing Status") - elif st not in ALLOWED_STATUSES: - errors.append(f"{path}: invalid Status '{st}' (allowed: {', '.join(sorted(ALLOWED_STATUSES))})") + elif status not in ALLOWED_STATUSES: + allowed = ", ".join(sorted(ALLOWED_STATUSES)) + errors.append(f"{path}: invalid Status '{status}' (allowed: {allowed})") + elif status == "Accepted" and path.name not in base_accepted_names: + acceptance = ACCEPTANCE_RE.search(text) + if ( + not acceptance + or not acceptance.group(2).strip() + or acceptance.group(2).lstrip().startswith("<") + ): + errors.append( + f"{path}: newly Accepted ADRs require Prospective or Retrospective " + "Acceptance metadata with a non-placeholder basis" + ) for section in REQUIRED_SECTIONS: if not re.search(rf"(?im)^##\s+{re.escape(section)}\b", text): errors.append(f"{path}: missing required section '## {section}'") - if base: - for name in changed: - if not (name.startswith("docs/adr/ADR-") and name.endswith(".md")): - continue - old = file_at(base, name) - if old is None: + if base and base_directory: + for old_path, old_bytes in base_files.values(): + old_text = old_bytes.decode("utf-8") + if status_of(old_text) != "Accepted": continue - old_status = status_of(old) - if old_status == "Accepted": + # During the docs/adr -> adr transition, an unchanged Accepted ADR + # maps by filename into the configured root-level directory. + current_path = ADR_DIR / old_path.name + if not current_path.is_file() or current_path.read_bytes() != old_bytes: errors.append( - f"{name}: Accepted ADRs are immutable. Create a new superseding ADR instead of editing this file." + f"{old_path}: Accepted ADRs are immutable. They may only move " + f"unchanged to {current_path}; create a superseding ADR instead." ) if errors: print("ADR governance failed:") - for e in errors: - print(f"- {e}") + for error in errors: + print(f"- {error}") return 1 - print(f"ADR governance passed ({len(adr_files)} ADR file(s) checked).") + + print(f"ADR governance passed ({len(files_to_validate)} ADR file(s) checked).") return 0 + if __name__ == "__main__": raise SystemExit(main()) diff --git a/scripts/tests/test_adr_governance.py b/scripts/tests/test_adr_governance.py new file mode 100644 index 0000000..68613f7 --- /dev/null +++ b/scripts/tests/test_adr_governance.py @@ -0,0 +1,359 @@ +from __future__ import annotations + +import os +import subprocess +import tempfile +import unittest +from pathlib import Path + + +SCRIPT = Path(__file__).resolve().parents[1] / "adr-governance.py" + +VALID_ADR = """# ADR-{number}: Test decision + +- **Status:** {status} +{acceptance} +## Context + +Context. + +## Decision + +Decision. + +## Consequences + +Consequences. + +## Validation + +Validation. +""" + + +class AdrGovernanceIntegrationTest(unittest.TestCase): + def setUp(self) -> None: + self.temp_dir = tempfile.TemporaryDirectory() + self.repo = Path(self.temp_dir.name) + self.run_command("git", "init", "-q") + self.run_command("git", "checkout", "-q", "-b", "main") + self.run_command("git", "config", "user.name", "ADR test") + self.run_command("git", "config", "user.email", "adr-test@example.com") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: adr\n", encoding="utf-8" + ) + (self.repo / "adr").mkdir() + self.write_adr("ADR-0001-existing-decision.md", "0001", "Accepted") + self.commit("add existing ADR") + self.main_sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", self.main_sha) + self.run_command("git", "checkout", "-q", "-b", "feature") + + def tearDown(self) -> None: + self.temp_dir.cleanup() + + def run_command( + self, *command: str, check: bool = True + ) -> subprocess.CompletedProcess[str]: + return subprocess.run( + command, + cwd=self.repo, + check=check, + capture_output=True, + text=True, + ) + + def write_adr( + self, + name: str, + number: str, + status: str, + *, + acceptance: bool = False, + ) -> Path: + path = self.repo / "adr" / name + metadata = ( + "- **Acceptance:** Retrospective — confirmed by the decision owner.\n" + if acceptance + else "" + ) + path.write_text( + VALID_ADR.format( + number=number, status=status, acceptance=metadata + ), + encoding="utf-8", + ) + return path + + def commit(self, message: str) -> None: + self.run_command("git", "add", ".") + self.run_command("git", "commit", "-q", "-m", message) + + def run_governance( + self, + base_ref: str | None = "main", + *, + event_before: str | None = None, + ) -> subprocess.CompletedProcess[str]: + if self.run_command("git", "status", "--porcelain").stdout: + self.commit("commit test changes") + env = os.environ.copy() + env.pop("GITHUB_BASE_REF", None) + env.pop("GITHUB_EVENT_BEFORE", None) + if base_ref: + env["GITHUB_BASE_REF"] = base_ref + if event_before is not None: + env["GITHUB_EVENT_BEFORE"] = event_before + return subprocess.run( + ["python3", "-I", str(SCRIPT)], + cwd=self.repo, + env=env, + check=False, + capture_output=True, + text=True, + ) + + def test_valid_adr_in_root_level_directory_passes(self) -> None: + self.write_adr("ADR-0002-new-decision.md", "0002", "Proposed") + + result = self.run_governance() + + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn("1 ADR file(s) checked", result.stdout) + + def test_unchanged_legacy_adr_format_is_not_revalidated(self) -> None: + legacy = self.write_adr("ADR-0002-legacy.md", "0002", "Proposed") + legacy.write_text("# ADR-0002: Legacy\n\n- **Status:** Proposed\n", encoding="utf-8") + self.commit("add legacy ADR") + sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", sha) + self.write_adr("ADR-0003-new-decision.md", "0003", "Proposed") + + result = self.run_governance() + + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn("1 ADR file(s) checked", result.stdout) + + def test_without_comparison_base_all_adrs_are_validated(self) -> None: + path = self.repo / "adr" / "ADR-0001-existing-decision.md" + path.write_text("# ADR-0001: Legacy\n\n- **Status:** Accepted\n", encoding="utf-8") + self.commit("make existing ADR malformed") + self.run_command("git", "update-ref", "-d", "refs/remotes/origin/main") + self.run_command("git", "branch", "-D", "main") + + result = self.run_governance(base_ref=None) + + self.assertEqual(result.returncode, 1) + self.assertIn("missing required section", result.stdout) + + def test_missing_required_section_fails(self) -> None: + path = self.write_adr("ADR-0002-broken.md", "0002", "Proposed") + path.write_text("# ADR-0002: Broken\n\n- **Status:** Proposed\n", encoding="utf-8") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("missing required section", result.stdout) + + def test_invalid_status_fails(self) -> None: + self.write_adr("ADR-0002-new-decision.md", "0002", "Draft") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("invalid Status 'Draft'", result.stdout) + + def test_malformed_filename_fails(self) -> None: + self.write_adr("ADR-0002-bad_title.md", "0002", "Proposed") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("filename must match", result.stdout) + + def test_duplicate_adr_numbers_fail(self) -> None: + self.write_adr("ADR-0002-first.md", "0002", "Proposed") + self.write_adr("ADR-0002-second.md", "0002", "Proposed") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("duplicate ADR number", result.stdout) + + def test_editing_accepted_adr_fails(self) -> None: + path = self.repo / "adr" / "ADR-0001-existing-decision.md" + path.write_text(path.read_text(encoding="utf-8") + "\nChanged.\n", encoding="utf-8") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("Accepted ADRs are immutable", result.stdout) + + def test_renaming_accepted_adr_fails(self) -> None: + old_path = self.repo / "adr" / "ADR-0001-existing-decision.md" + old_path.rename(self.repo / "adr" / "ADR-0001-renamed-decision.md") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("Accepted ADRs are immutable", result.stdout) + + def test_push_before_sha_enforces_accepted_immutability(self) -> None: + path = self.repo / "adr" / "ADR-0001-existing-decision.md" + path.write_text(path.read_text(encoding="utf-8") + "\nChanged.\n", encoding="utf-8") + self.commit("edit accepted ADR") + pushed_sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", pushed_sha) + + result = self.run_governance( + base_ref=None, + event_before=self.main_sha, + ) + + self.assertEqual(result.returncode, 1) + self.assertIn("Accepted ADRs are immutable", result.stdout) + + def test_initial_push_zero_before_falls_back_to_default_branch(self) -> None: + path = self.repo / "adr" / "ADR-0001-existing-decision.md" + path.write_text(path.read_text(encoding="utf-8") + "\nChanged.\n", encoding="utf-8") + self.commit("edit accepted ADR") + + result = self.run_governance( + base_ref=None, + event_before="0" * 40, + ) + + self.assertEqual(result.returncode, 1) + self.assertIn("Accepted ADRs are immutable", result.stdout) + + def test_new_accepted_adr_requires_acceptance_metadata(self) -> None: + self.write_adr("ADR-0002-no-acceptance.md", "0002", "Accepted") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("newly Accepted ADRs require", result.stdout) + + def test_new_accepted_adr_with_acceptance_metadata_passes(self) -> None: + self.write_adr( + "ADR-0002-retrospective.md", + "0002", + "Accepted", + acceptance=True, + ) + + result = self.run_governance() + + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + + def test_accepted_adr_can_be_corrected_across_introducing_branch_commits(self) -> None: + path = self.write_adr( + "ADR-0002-retrospective.md", + "0002", + "Accepted", + acceptance=True, + ) + self.commit("introduce retrospective ADR") + path.write_text( + path.read_text(encoding="utf-8").replace("Decision.", "Corrected decision."), + encoding="utf-8", + ) + self.commit("correct retrospective ADR") + + result = self.run_governance(base_ref=None) + + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + + def test_newly_accepted_existing_adr_requires_acceptance_metadata(self) -> None: + path = self.write_adr("ADR-0002-proposed.md", "0002", "Proposed") + self.commit("add proposed ADR") + sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", sha) + path.write_text( + VALID_ADR.format(number="0002", status="Accepted", acceptance=""), + encoding="utf-8", + ) + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("newly Accepted ADRs require", result.stdout) + + def test_missing_comparison_base_fails_closed(self) -> None: + result = self.run_governance(base_ref="missing") + + self.assertEqual(result.returncode, 1) + self.assertIn("cannot establish comparison base", result.stdout) + + def test_current_config_directory_must_match_checker_directory(self) -> None: + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: docs/adr\n", encoding="utf-8" + ) + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("adr_directory is docs/adr, but the checker uses adr", result.stdout) + + def test_missing_base_config_fails_closed(self) -> None: + self.run_command("git", "checkout", "-q", "main") + (self.repo / ".adr-kit.yaml").unlink() + self.commit("remove base config") + sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", sha) + self.run_command("git", "checkout", "-q", "-B", "feature") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: adr\n", encoding="utf-8" + ) + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("cannot establish comparison base", result.stdout) + + def test_relocating_unchanged_accepted_adr_passes(self) -> None: + self.run_command("git", "checkout", "-q", "main") + (self.repo / "docs").mkdir() + self.run_command("git", "mv", "adr", "docs/adr") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: docs/adr\n", encoding="utf-8" + ) + self.commit("put ADRs in legacy directory") + sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", sha) + self.run_command("git", "checkout", "-q", "-B", "relocation") + self.run_command("git", "mv", "docs/adr", "adr") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: adr\n", encoding="utf-8" + ) + + result = self.run_governance() + + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + + def test_relocating_modified_accepted_adr_fails(self) -> None: + self.run_command("git", "checkout", "-q", "main") + (self.repo / "docs").mkdir() + self.run_command("git", "mv", "adr", "docs/adr") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: docs/adr\n", encoding="utf-8" + ) + self.commit("put ADRs in legacy directory") + sha = self.run_command("git", "rev-parse", "HEAD").stdout.strip() + self.run_command("git", "update-ref", "refs/remotes/origin/main", sha) + self.run_command("git", "checkout", "-q", "-B", "relocation") + self.run_command("git", "mv", "docs/adr", "adr") + (self.repo / ".adr-kit.yaml").write_text( + "adr_directory: adr\n", encoding="utf-8" + ) + path = self.repo / "adr" / "ADR-0001-existing-decision.md" + path.write_text(path.read_text(encoding="utf-8") + "\nChanged.\n", encoding="utf-8") + + result = self.run_governance() + + self.assertEqual(result.returncode, 1) + self.assertIn("Accepted ADRs are immutable", result.stdout) + + +if __name__ == "__main__": + unittest.main() diff --git a/skills/start/MAINTAINING.md b/skills/start/MAINTAINING.md index 28bdff7..70aa75a 100644 --- a/skills/start/MAINTAINING.md +++ b/skills/start/MAINTAINING.md @@ -17,7 +17,7 @@ A **stamp refresh** (no version bump, pure verification heartbeat) is a differen Stamp refreshes typically come in via the daily upstream-sweep routine. See `planning/routines/upstream-sweep.md`. -A skill patch release driven by an upstream-sweep prose PR uses the same envelope as a manual patch release. The prose PR contains the full release set in a single coherent change: `SKILL.md` body and frontmatter, `version.json` (`version` and `published_date`), `CHANGELOG.md` (one new entry whose header matches the new `version`), and the matching `verified_commits` and `verified_date:` refreshes. The `prose-guard` required check enforces this both ways — if the body changes, all release fields must move; if the body is unchanged, none of them may. +A skill patch release driven by an upstream-sweep prose PR uses the same envelope as a manual patch release. The prose PR contains the full release set in a single coherent change: `SKILL.md` body and frontmatter, `version.json` (`version` and `published_date`), `CHANGELOG.md` (one new entry whose header matches the new `version`), and the matching `verified_commits` and `verified_date:` refreshes. The `prose-guard` required check enforces this both ways: if the body changes, all release fields must move; if the body is unchanged, release fields stay fixed while `verified_commits` and `verified_date` may move as a pure stamp refresh. ## Current scope @@ -137,6 +137,15 @@ Check these against the relevant docs pages and source repos: - local devnet workflow - protocol-adjacent operational details such as devnet-manifest handoff and prepare/finalize variants +## Style contract + +Apply `CLAUDE.md` to the skill as you would to rendered documentation, with two narrow carve-outs: + +- The skill may enumerate prohibited terms when terminology guidance is the subject being taught. +- The skill may name its own repository when recording the skill's provenance. + +These exceptions do not permit provenance narration or prohibited wording in ordinary user-facing guidance. + ## Update order 1. Update `SKILL.md`. @@ -147,7 +156,7 @@ Check these against the relevant docs pages and source repos: Use Semantic Versioning with these rules: -- major: breaking changes to skill loading or manifest shape +- major: breaking changes to skill loading or manifest shape, including a move of the stable manifest URL - minor: new paths, new verified examples, or new operational capabilities - patch: wording fixes, pointer fixes, or substantive re-verification — for example a SHA refresh that reflects an upstream change to a public surface the skill describes (a new flow, a renamed command, a removed step). Pure stamp refreshes that do not touch any described surface are not patches; they are stamp refreshes (see `## Files that move together`). @@ -156,7 +165,7 @@ Keep the `-draft` suffix until the skill has gone through at least one deliberat ## Pre-merge checklist - [ ] `SKILL.md`, `version.json`, and `CHANGELOG.md` agree on the version. -- [ ] `verified_date` and `published_date` match. +- [ ] For a linked release, `verified_date` and `published_date` match; for a pure stamp refresh, only `verified_commits` and `verified_date` move. - [ ] every `verified_commits` entry is a real SHA. - [ ] every live-docs URL in `SKILL.md` resolves. - [ ] no hard-coded detail was added without a matching verification source.