From bc79a09e5c6c699e30d31843d87415752d375ea5 Mon Sep 17 00:00:00 2001 From: Jim Collinson <13061030+JimCollinson@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:34:09 +0100 Subject: [PATCH] adr: relocate ADRs to /adr; add ADR-0002 (location) + retrospective ADR-0003-0014 --- .adr-kit.yaml | 2 +- .github/workflows/adr-governance.yml | 4 +- ...001-adopt-architecture-decision-records.md | 0 ...DR-0002-adrs-live-at-repo-root-not-docs.md | 67 +++++++++++++ ...0003-verification-block-source-of-truth.md | 72 ++++++++++++++ ...-deterministic-scanner-model-audit-loop.md | 76 +++++++++++++++ ...eduled-routine-over-repository-dispatch.md | 76 +++++++++++++++ ...06-two-track-pr-envelope-sweep-vs-prose.md | 70 ++++++++++++++ ...ail-closed-and-github-artifact-topology.md | 74 +++++++++++++++ ...book-github-dual-audience-docs-platform.md | 77 +++++++++++++++ ...man-review-gate-path-to-full-automation.md | 69 ++++++++++++++ ...laude-md-authoring-constraints-contract.md | 74 +++++++++++++++ ...rmation-architecture-and-content-design.md | 83 ++++++++++++++++ ...oper-skill-colocated-versioned-artifact.md | 95 +++++++++++++++++++ ...freshness-mechanism-and-content-tiering.md | 89 +++++++++++++++++ ...aintenance-workflow-and-registry-wiring.md | 80 ++++++++++++++++ {docs/adr => adr}/README.md | 0 {docs/adr => adr}/TEMPLATE.md | 0 {docs/adr => adr}/TOOLING.md | 0 planning/skill-open-questions.md | 30 ++++++ scripts/adr-governance.py | 2 +- 21 files changed, 1036 insertions(+), 4 deletions(-) rename {docs/adr => adr}/ADR-0001-adopt-architecture-decision-records.md (100%) create mode 100644 adr/ADR-0002-adrs-live-at-repo-root-not-docs.md create mode 100644 adr/ADR-0003-verification-block-source-of-truth.md create mode 100644 adr/ADR-0004-deterministic-scanner-model-audit-loop.md create mode 100644 adr/ADR-0005-hosted-scheduled-routine-over-repository-dispatch.md create mode 100644 adr/ADR-0006-two-track-pr-envelope-sweep-vs-prose.md create mode 100644 adr/ADR-0007-fail-closed-and-github-artifact-topology.md create mode 100644 adr/ADR-0008-gitbook-github-dual-audience-docs-platform.md create mode 100644 adr/ADR-0009-human-review-gate-path-to-full-automation.md create mode 100644 adr/ADR-0010-claude-md-authoring-constraints-contract.md create mode 100644 adr/ADR-0011-information-architecture-and-content-design.md create mode 100644 adr/ADR-0012-developer-skill-colocated-versioned-artifact.md create mode 100644 adr/ADR-0013-skill-freshness-mechanism-and-content-tiering.md create mode 100644 adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md rename {docs/adr => adr}/README.md (100%) rename {docs/adr => adr}/TEMPLATE.md (100%) rename {docs/adr => adr}/TOOLING.md (100%) create mode 100644 planning/skill-open-questions.md mode change 100755 => 100644 scripts/adr-governance.py 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..07d01c2 100644 --- a/.github/workflows/adr-governance.yml +++ b/.github/workflows/adr-governance.yml @@ -3,7 +3,7 @@ name: ADR Governance on: pull_request: paths: - - 'docs/adr/**' + - 'adr/**' - '.adr-kit.yaml' - 'scripts/adr-governance.py' - '.github/workflows/adr-governance.yml' @@ -12,7 +12,7 @@ on: - main - master paths: - - 'docs/adr/**' + - 'adr/**' - '.adr-kit.yaml' - 'scripts/adr-governance.py' - '.github/workflows/adr-governance.yml' 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..3a91dbd --- /dev/null +++ b/adr/ADR-0002-adrs-live-at-repo-root-not-docs.md @@ -0,0 +1,67 @@ +# ADR-0002: Architecture Decision Records live at repo-root `/adr`, not `docs/adr` + +- **Status:** Accepted +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0001 (adopt ADRs — sets `docs/adr` as the team default this repo re-points), 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; a mismatch silently skips validation. + +### 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`) validates the `/adr` set on every PR; `docs/adr/` no longer exists. +- 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..a731f5a --- /dev/null +++ b/adr/ADR-0003-verification-block-source-of-truth.md @@ -0,0 +1,72 @@ +# ADR-0003: Verification-block source-of-truth model for developer docs + +- **Status:** Accepted +- **Acceptance:** Retrospective — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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 technical claim on every page, *exactly which upstream commit that claim was verified against*, so drift is detectable mechanically rather than by re-reading everything by eye. + +A second tension: docs must describe what a developer can do **now** (merged, released code), not the expected future product shape. Without an explicit rule, pages tend to drift toward aspirational descriptions of unmerged branches. But a launch-hardening pass needs the opposite — the ability to pin a set of pages to a fixed release target and *stop* following moving branches. + +## Decision Drivers + +- Drift must be **detectable by a machine**, not only by human review. +- Every rendered claim must be **traceable to an exact upstream commit SHA**. +- Docs must default to **current, merged, released** truth, never future state. +- 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-claim verification blocks pinning `(source_repo, source_ref, source_commit, verified_date, verification_mode)`, with two explicit modes.** Chosen. + +## Decision + +We will embed **verification blocks** in the docs and skill sources as the single machine-readable record of what each page was verified against. + +- Every documented surface carries one or more `` blocks in `docs/**/*.md`, and the skill carries equivalent `verified_commits` records in `skills/start/version.json` and `skills/start/SKILL.md` frontmatter. Each block records `source_repo`, `source_ref`, `source_commit` (an exact SHA), `verified_date`, and `verification_mode`. +- 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 claim is auditable to an exact SHA, so a reviewer can reproduce the evidence. +- The default keeps docs honest about shipped behaviour; 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 block schema (`source_repo`, `source_ref`, `source_commit`, `verified_date`, `verification_mode`) becomes a stable contract that every downstream tool depends on; changing it 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..1f12125 --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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. Implemented 2026-05-04 and hardened through 2026-05-11. + +## 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..81baa0e --- /dev/null +++ b/adr/ADR-0005-hosted-scheduled-routine-over-repository-dispatch.md @@ -0,0 +1,76 @@ +# ADR-0005: Subscription-billed daily scheduled routine as the interim trigger, over repository_dispatch webhooks and API-billed CI + +- **Status:** Accepted +- **Acceptance:** Retrospective — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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; deployment-venue notes in vault `Inbox/Untitled 43.md`; 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 a **pre-existing team subscription via OAuth**, 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 OAuth session. 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, billed to a team member's subscription via OAuth** 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 routine currently runs as a **Claude Code scheduled routine from Jim's desktop**, under **Jim's Claude subscription (OAuth)** — so it depends on that account and setup. GitHub access rides the connected **Claude GitHub App** (no `GITHUB_TOKEN`/PAT required; PRs attributed to the user's GitHub identity), and `ANTHROPIC_API_KEY` is deliberately left **unset** so runs never silently fall through to API billing. (A Claude Code *web* routine at `claude.ai/code/routines` is an equivalent surface for the same design, explored in `Inbox/Untitled 43.md`; the billing and auth model are the same.) +- **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 long-lived PAT to store or rotate; auth rides the GitHub App. + +### 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 routine runs under Jim's subscription and desktop setup, so it is tied to that account being active and in good standing. Moving it to a shared or service identity is unresolved and is the main fragility of this arrangement. +- 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 current path relies on the GitHub App and needs none of it. + +## Validation + +- First-run check: within ~10 minutes of the trigger, expect either a silent no-drift exit, up to two PRs, and/or manual-review issues; nothing appearing means the routine did not fire (GitHub App scope or schedule misconfig), per `Inbox/Untitled 43.md`. +- 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, moving off subscription/OAuth billing, or moving the routine off a personal account to a shared identity 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-0006-two-track-pr-envelope-sweep-vs-prose.md b/adr/ADR-0006-two-track-pr-envelope-sweep-vs-prose.md new file mode 100644 index 0000000..1f97f83 --- /dev/null +++ b/adr/ADR-0006-two-track-pr-envelope-sweep-vs-prose.md @@ -0,0 +1,70 @@ +# ADR-0006: Two-track PR envelope — metadata "sweeps" vs prose "pro sweeps" — enforced by branch-scoped CI guards + +- **Status:** Accepted +- **Acceptance:** Retrospective — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification blocks), ADR-0004 (execution tiers — the two tracks map onto the efficient/frontier model tiers), ADR-0010 (CLAUDE.md authoring contract the prose track must satisfy), ADR-0014 (the skill release lifecycle the linked-release rule governs); `.github/workflows/sweep-guard.yml`, `prose-guard.yml`, `sweep-sha-reachability.yml`; `planning/routines/upstream-sweep.md` §"Sweep/Prose PR envelope", §"Page batching rule"; commits `e5869c5` (2026-05-05, prose-guard + v1.1), `7b23c92` (2026-05-08), `ddeb45a` (2026-05-09) + +> Retrospective ADR. The metadata sweep track shipped 2026-05-04; the prose ("pro sweep") track and `prose-guard` were added 2026-05-05 and hardened through 2026-05-09. + +## Context + +Drift comes in two very different shapes. Sometimes an upstream commit changes nothing a developer sees — only the pinned SHA needs re-stamping (a **metadata-only** refresh). Sometimes it changes real behaviour — an endpoint, a flag, a type — and the rendered prose must be rewritten (a **prose-impacting** change). These carry very different review risk: a SHA re-stamp is near-mechanical and safe to skim; a prose rewrite must be read word-for-word by a human before it ships. + +If both kinds land in one undifferentiated PR, a reviewer cannot trust *any* PR without a full read, and a prose change can hide inside a "just re-stamping" PR (or vice versa). We needed the two kinds to be **structurally separable and independently trustworthy**, and we needed that separation **machine-enforced**, because the author is an automated routine. + +## Decision Drivers + +- A metadata re-stamp must be reviewable in seconds; a prose change must be flagged for a full read. +- The routine is automated, so the envelope must be **enforced by CI**, not by author discipline. +- A prose change to the skill must move its **release metadata in lockstep** (or the skill's version lies). +- Verification-block integrity must be protected: a "metadata" PR must not smuggle prose, and a prose PR must not silently edit a deferred/ambiguous verification block. + +## Considered Options + +1. **One PR type for all drift.** Rejected: no PR is trustworthy without a full read; hides prose in metadata noise. +2. **Human labels each PR by type after the fact.** Rejected: the author is a routine; labels are best-effort and can be missing; not enforceable pre-merge. +3. **Two branch-namespaced PR tracks with per-track CI guards** that green-skip on the other track. Chosen. + +## Decision + +We will split routine output into **two mutually exclusive PR tracks**, distinguished by branch prefix and each policed by a required CI check: + +- **Metadata sweep — `claude/sweep-*`, policed by `sweep-guard`.** Allowed: verification-block `source_commit`/`verified_date` lines in `docs/**`, the `verified_commits` maps in `version.json` and `SKILL.md` frontmatter (key sets **locked** — values may refresh, keys may not add/remove), and exactly one new `planning/sweeps/.md`. Forbidden: any prose, any `SKILL.md` body change, `version`/`published_date`/`CHANGELOG` changes, and any change to `scripts/**`, `.github/**`, `repo-registry.yml`, `component-registry.yml`. +- **Prose sweep ("pro sweep") — `claude/prose-*` (opened as draft), policed by `prose-guard`.** Allowed: `docs/**` prose, `SKILL.md` body/frontmatter, and — only as part of a **linked release** — `version`, `published_date`, and `CHANGELOG`. Draft status forces a human to promote to ready only after reading the prose. +- **Linked-release rule (both directions):** if the `SKILL.md` body changes, the same PR must bump `version` (patch), update `published_date`, match frontmatter `version:`, update `verified_date:`, and add one `CHANGELOG` entry. If the body is byte-identical, **none** of those may change. `prose-guard` enforces both directions. +- **The two tracks never touch the same page.** A page's classification (via the five-case page-batching rule in `upstream-sweep.md`) sends it to exactly one track; a prose PR that touches a page with a deferred ambiguous record must leave that record's verification block **byte-identical** to base (routine-side deferred-record self-check, since the guards cannot know which records were deferred). +- Guards run **only** on their own branch prefix and green-skip elsewhere, using a real gate step plus an `if:` on every later step (exit-0 from an early step does not skip later steps in Actions). `sweep-sha-reachability` runs on **both** tracks and validates every changed SHA against upstream. + +## Consequences + +### Positive + +- A `claude/sweep-*` PR is trustworthy at a glance: CI guarantees it changed nothing but pinned SHAs and a run summary. +- A `claude/prose-*` PR is always a draft and always flags a human read; prose can never hide in a "metadata" PR. +- The skill's published version can never silently diverge from its body. +- Enforcement is mechanical, so it holds regardless of author (routine or human). + +### Negative / Trade-offs + +- Two guard workflows plus a reachability check to maintain; their envelopes are intricate and were the source of most early defects (`7b23c92`, `ddeb45a`). +- The page-batching rule that keeps the tracks disjoint is non-trivial and lives in the routine prompt, not in CI — CI enforces the envelope, not the classification. +- A single upstream change touching one page's prose and another page's metadata produces two PRs. + +### Neutral / Operational + +- Branch prefixes (`claude/sweep-*`, `claude/prose-*`) and the guard `name:` strings become required-check contracts in branch protection. +- `planning/sweeps/.md` summary files accumulate as a per-run audit trail. + +## Validation + +- `sweep-guard`, `prose-guard`, and `sweep-sha-reachability` are required checks on `main`; a PR that violates its envelope fails CI and cannot merge. +- Synthetic verification PRs (branch slugs) exercise individual envelope rules; the 2026-05-09 pass (`ddeb45a`, `8c541eb`) added shape/type and rename-diff coverage after five defects were found this way. +- Review trigger: any change to what a track may touch, or to the linked-release rule, 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-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..f514a85 --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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: unresolved conditions must **deduplicate**, not re-open daily. +- 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..cf87b11 --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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 + +- The published site at `docs.autonomi.com/developers` serves both the human pages and the machine surfaces (`llms.txt`, per-page Markdown). +- 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..beae130 --- /dev/null +++ b/adr/ADR-0009-human-review-gate-path-to-full-automation.md @@ -0,0 +1,69 @@ +# ADR-0009: Human review gate now, with a path to full automation via AI-panel review and human escalation + +- **Status:** Accepted +- **Acceptance:** Retrospective — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0005 (scheduled routine), ADR-0006 (PR tracks), ADR-0007 (fail-closed topology); `planning/routines/upstream-sweep.md` §"Out of scope" (auto-merge deferred to v1.5); `planning/implementation-plan.md` §8 + +> Retrospective ADR capturing both the current gate and the intended end-state. The current-state parts are settled; the end-state (AI-panel review, auto-merge) is **intent**, not yet built — called out as such below. + +## Context + +The **ultimate intention** is a fully automated pipeline: any change in an upstream repository is reflected in the documentation **without human intervention**, while maintaining very high, demonstrable, tested accuracy. That is the goal, and we have not reached it yet. + +Two things stand between the current system and that goal. First, trust: the daily sweeps are reliable and the PRs are largely good, but prose PRs still occasionally contain inaccuracies, so a human must currently read them before merge. Second, mechanism: there is no automated reviewer that could stand in for that human with enough confidence to merge unattended. This ADR records how we gate today and the shape of the path to full automation, so the intent is not lost and the interim gate is not mistaken for the destination. + +## Decision Drivers + +- Accuracy is non-negotiable: wrong docs are worse than stale docs, so nothing merges unverified. +- The end goal is **no routine human intervention**, with accuracy maintained by construction and testing, not by a person eyeballing every PR. +- The routine already produces structurally-constrained, self-checked PRs (ADR-0006/0007), so the missing piece is trustworthy *review*, not trustworthy *generation*. +- Automation should be able to **escalate to a human** for the cases it cannot clear, rather than choosing between "merge blindly" and "block everything." + +## Considered Options + +1. **Auto-merge now.** Rejected: prose PRs still carry occasional inaccuracies; unattended merge would publish them. +2. **Permanent human review.** Rejected as the *end-state*: it caps throughput on human availability and contradicts the fully-automated goal, even though it is the correct *interim* gate. +3. **Human gate now; evolve to an AI review panel that can merge clean PRs and escalate uncertain ones to humans.** Chosen as the trajectory. + +## Decision + +We will keep a **human review gate as the current merge control**, while treating full automation as the explicit destination reached by strengthening *review*, not by loosening the gate. + +- **Now (settled):** every routine PR is opened for human review; prose PRs are opened as drafts (ADR-0006) and a person promotes and merges them after reading. Ambiguity and failures are surfaced as issues, never merged (ADR-0007). Auto-merge is deferred (`upstream-sweep.md` §"Out of scope" marks it v1.5). +- **Assessment (current):** the metadata sweeps appear reliable enough to be candidates for full automation first; the prose track is close but not yet trustworthy for unattended merge because of residual inaccuracies. The sweep track and the prose track can therefore cross the automation threshold **independently**. +- **Intended end-state (not yet built):** replace the routine human read with an **AI review panel** — multiple independent agents reviewing each PR against the source-of-truth evidence and `CLAUDE.md` (ADR-0010) — that can approve and merge a clean PR and **escalate to human team members** when reviewers disagree or confidence is low. Provider diversity is desirable: reviews by both Anthropic and OpenAI frontier models, each run under its own subscription/OAuth (consistent with ADR-0005's no-API-budget constraint), so the reviewers are genuinely independent rather than one model checking itself. +- **Sequencing:** metadata sweeps are the first candidate for panel-gated auto-merge; the prose track follows once its accuracy is demonstrably high enough. Each step is a config/tooling change, not a change to the source-of-truth model. + +## Consequences + +### Positive + +- Nothing inaccurate is published while the gate is human: accuracy is protected today. +- The end-state is written down, so the interim gate is understood as a stage, not the design's ceiling. +- Splitting the automation threshold by track lets the reliable sweep path advance without waiting on the prose path. +- Provider-diverse AI review reduces correlated blind spots and keeps escalation to humans as a safety valve. + +### Negative / Trade-offs + +- Human review is a throughput bottleneck and a standing time cost until the panel exists. +- An AI review panel is non-trivial to build and to trust; a panel that merges wrongly is worse than a slow human gate, so the bar for switching is high. +- Provider-diverse review adds operational surface (multiple subscriptions/OAuth sessions, multiple harnesses). + +### Neutral / Operational + +- The merge gate (human → AI-panel-with-escalation) is operational configuration; it does not alter ADR-0003's source-of-truth model or ADR-0006's envelopes. +- "Demonstrable, tested accuracy" implies a measurable accuracy signal (e.g. human-agreement rate on PRs) as the trigger for advancing a track — see Validation. + +## Validation + +- Current gate: no routine PR merges without human promotion; ambiguous/failed runs appear as issues, not merges (ADR-0007). +- Advancement criterion: a track moves to panel-gated auto-merge only when it clears an explicit, measured accuracy bar (e.g. sustained high reviewer/human agreement with no published inaccuracies over a defined window). The bar and window are set when the panel is specified. +- Review trigger: enabling any auto-merge, or standing up the AI review panel, is an architectural change that supersedes or extends 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-0010-claude-md-authoring-constraints-contract.md b/adr/ADR-0010-claude-md-authoring-constraints-contract.md new file mode 100644 index 0000000..6db7ceb --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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..96a73e4 --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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` (v4 — detailed IA, per-page briefs, design principles); `docs/SUMMARY.md`; the route-first IA 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 records the durable design of record; `planning/information-architecture.md` holds the page-by-page detail and remains the working reference for specifics. + +## 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). + +`planning/information-architecture.md` holds the concrete expression of this design (directory tree, `SUMMARY.md`, per-page briefs, launch priorities). That file is the working reference; this ADR is the decision and its invariants. + +## 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 + +- `docs/SUMMARY.md` and the `docs/{sdk,mcp,cli,rust,guides,reference}/` tree reflect the route-first-with-Diátaxis layout. +- 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 taxonomy (away from Diátaxis), the navigation model (away from route-first), the interface set, the primary-interface stance, or the canonical-REST-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..020c941 --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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. Channels include the Claude plugin/marketplace (currently realised via `.claude-plugin/` → marketplace `withautonomi`, plugin `developer`, giving `/developer:start`), **skills.sh**, **manual install** (fetch `SKILL.md` and drop it in), and future channels — with or without human intervention. The Claude marketplace is **one channel, not the definition of the artifact**; whether "plugin" is even the right framing is deliberately open. +- **Name (intended): `autonomi-developer`.** The canonical, vendor-neutral, self-describing name is `autonomi-developer` — accurate across all four paths (not just SDK), and distinct from a user-facing skill. This is what the ADR records as intended. + +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`) — a form introduced to suit the Claude-plugin slash command rather than the vendor-neutral intent. This divergence, the unclear provenance of `start`, and the open question of a future migration to `WithAutonomi/skills` are tracked in `planning/skill-open-questions.md` and will be closed by a superseding ADR when resolved (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. +- Vendor-agnostic distribution keeps reach broad (Claude marketplace, skills.sh, manual, future) and allows agent self-install. + +### Negative / Trade-offs + +- **Naming debt:** the on-disk name (`start`) reflects a vendor channel, not the canonical `autonomi-developer` intent — tracked in `planning/skill-open-questions.md`, to be closed by a superseding ADR. +- 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 possibly the naming parts of this ADR. +- `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: resolving the `start` vs `autonomi-developer` question, or migrating the skill to `WithAutonomi/skills`, supersedes the relevant parts of this ADR. +- 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..947ec8e --- /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 — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **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 a `version` and a per-repo `verified_commits` SHA map (plus `verified_date`, `verification_mode`) in the `SKILL.md` frontmatter, mirrored in `version.json`. Even with no network, a maintainer can see exactly what was verified, 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). +- **Two copies of `version`/SHAs** (frontmatter + `version.json`) must be kept in sync — a maintenance-discipline item (ADR-0014 checklist). +- 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..59431ee --- /dev/null +++ b/adr/ADR-0014-skill-maintenance-workflow-and-registry-wiring.md @@ -0,0 +1,80 @@ +# ADR-0014: Skill maintenance — reuse the docs verification workflow, wired via `feeds_skills` + +- **Status:** Accepted +- **Acceptance:** Retrospective — predates the ADR process; ratified by the implementation built on it and by this review pass, not by prospective pre-implementation review. +- **Date:** 2026-07-14 +- **Decision owners:** Jim Collinson +- **Supersedes:** none +- **Superseded by:** none +- **Related:** ADR-0003 (verification model), ADR-0005 (the routine now runs skill stamp refreshes), ADR-0006 (linked-release rule / guards enforce the skill envelope), ADR-0009 (path to automation), ADR-0010 (CLAUDE.md style contract), ADR-0012 (the skill), ADR-0013 (freshness the workflow keeps current); origin decision log Phases 8, 9, 10 + verification-metadata & style cross-cuts; `repo-registry.yml`, `component-registry.yml` (`feeds_skills`), `planning/verification-workflow.md`, `skills/start/MAINTAINING.md` + +> Retrospective ADR reconstructed from the origin design sessions. At genesis this workflow was manual/agent-driven with automation deferred; the repo has since moved along exactly that trajectory (the daily routine now carries stamp refreshes) — noted where relevant. + +## Context + +The skill has to stay accurate as upstream code moves. The docs repo already owns the machinery for that: `repo-registry.yml` classifies each upstream repo by change impact (`broad-source`, `targeted-foundational`, `specialist`, `watchlist`, `excluded`, `tangential`) with an `on_change` behaviour; `component-registry.yml` maps each component to the pages it `feeds_pages:`; and `planning/verification-workflow.md` defines the `source audit → draft → verify` pass. The question is whether the skill gets its own maintenance machinery or reuses this. + +## Decision Drivers + +- **No fragmentation:** a docs verification pass and a skill verification pass should not be two disconnected chores that drift apart. +- **No premature automation:** upstream changes too fast for auto-open-PR-on-every-change; follow the docs project's own deliberate manual-then-automate trajectory (ADR-0009). +- **Machine-consumable dependency graph:** "when X changes, what re-verifies?" must be answerable from the registry alone, not by cross-reading artifacts. +- **Verification parity with pages** (real SHA per source of truth) without the pages' HTML-comment mechanics. + +## Considered Options + +**Maintenance workflow (Phase 8).** +1. *A new parallel workflow just for the skill.* Rejected: fragments maintenance; a docs pass and a skill pass fall out of sync. +2. *Full CI automation now* (watch upstreams, auto-open skill-bump PRs). Rejected as the v1 approach: too noisy given upstream velocity; the docs project itself introduces automation deliberately, and the skill should follow the same path. +3. *Reuse the existing docs verification workflow*, treating the skill as another artifact driven by the registries. Chosen. + +**Registry wiring (Phase 9).** +1. *Option A — a new `feeds_skills:` field* on each component, parallel to `feeds_pages:`. Chosen. +2. *Option B — reuse `feeds_pages:`*, adding the skill path to the same list. Rejected: mixes two artifact types; any tool that opens each `feeds_pages:` entry as a Diátaxis-templated page breaks on the skill entry. +3. *Option C — the skill declares its own `depends_on:`* in frontmatter. Rejected: inverts the dependency graph — the registry should be the single source of "what updates when X changes." + +**Verification metadata shape (cross-cut).** +1. *One block, first repo only.* Rejected: silent under-verification. +2. *Multiple HTML comment blocks* (as pages use). Rejected: awkward in a YAML frontmatter world. +3. *A frontmatter `verified_commits:` dict* (per-repo SHA) + single `verified_date`/`verification_mode`, mirrored in `version.json`. Chosen. + +## Decision + +- **Reuse the docs verification workflow.** The skill is a first-class artifact consuming both registries and following `source audit → draft → verify`. Re-verification scope keys off `repo-registry.yml`: a `broad-source` change (`ant-sdk`, `ant-client`, `ant-node`) triggers broad re-verification of every skill section touching those components; a `targeted-foundational` change (`saorsa-pqc`, `self_encryption`, `evmlib`, `ant-merkle`, `saorsa-transport`) triggers targeted re-verification of the named concept; `excluded`/`watchlist` changes only re-confirm the skill still says the repo is out of scope. +- **Wire the skill in via `feeds_skills:` (Option A)** in `component-registry.yml` — same YAML shape as `feeds_pages:`, new field name, on the components the skill actually covers (`antd`, `openapi`, `proto`, `ant-core`, `ant-cli`, `ant-node`, the documented language bindings, `self_encryption`, `evmlib`, `ant-protocol`, and the protocol-adjacent components), and *not* on components the skill doesn't teach. This keeps the registry the single machine-readable answer to "what re-verifies when X changes." +- **Verification metadata:** a frontmatter `verified_commits:` dict (real SHA per source repo) plus one `verified_date` and `verification_mode`, mirrored in `version.json` for external inspection. No published release may carry a placeholder SHA. +- **Releases move as a set, SemVer'd.** A verification pass produces a matched change to `SKILL.md`, `version.json`, and `CHANGELOG.md` in one commit. SemVer: **major** = skill-loading / manifest-shape breaks (including a manifest-URL move, per ADR-0013); **minor** = new paths, examples, or capabilities; **patch** = wording/pointer fixes or a re-verification that reflects an upstream change to a described surface. A pure `verified_commits` stamp refresh that touches no described surface is *not* a version bump — it moves only the SHA map (+ `verified_date`), which is the daily routine's normal gesture. +- **Style compliance:** the skill follows `CLAUDE.md`'s style contract (ADR-0010) as if it were a page, with two carve-outs — it may enumerate prohibited words (that's its terminology-mirror topic), and it may name its own repo (provenance about the skill itself is not provenance about docs content). +- **Maintainer doc placement:** resolved to **skill-local `MAINTAINING.md`** (`skills/start/MAINTAINING.md`). Alternatives considered (fold into `CLAUDE.md`; a repo-wide `skills/MAINTAINING.md`) remain open only if more skills co-locate here; the content is portable. + +## Consequences + +### Positive + +- Zero new infrastructure and one workflow to learn; a docs pass and a skill pass are the same muscle. +- `feeds_skills:` keeps the "what re-verifies when X changes" answer in the registry, machine-consumable, without conflating pages and skills. +- The frontmatter `verified_commits` dict gives page-equivalent verification (real SHA per repo) that both the runtime freshness check (ADR-0013) and human/CI review consume. +- The trajectory matched the docs project's: the daily routine (ADR-0005) now performs the skill's stamp refreshes, and the linked-release rule is CI-enforced (ADR-0006) — the "automate later" the genesis session anticipated has largely arrived for the metadata half. + +### Negative / Trade-offs + +- Depends on `on_change` triggers actually being followed; if no one runs a pass after a `broad-source` change, the skill drifts silently and the runtime warning (ADR-0013) is the only user-side safety net — a human-paced fix rate. +- Skill and docs maintenance are coupled: a large docs PR can pull a skill re-verification along. Fine while docs volume is bounded; decouples if the skill migrates to `WithAutonomi/skills` (ADR-0012). +- **Two copies of the SHA map** (frontmatter + `version.json`) to keep in sync — a `MAINTAINING.md` checklist item. +- `feeds_skills:` adds a one-time teach-the-tooling cost (link validators / verification tooling must iterate the new field). + +### Neutral / Operational + +- `feeds_skills:` was drafted-then-deferred at genesis and has since been **applied** to `component-registry.yml`; the covered-component set tracks the skill's actual scope and evolves with it (e.g. `saorsa-pqc` out, `ant-protocol` in relative to the genesis draft). +- The manual/agent-driven maintenance path and the automated daily routine coexist: the routine handles metadata stamps and prose drafts; deliberate releases (version promotion out of `-draft`) remain a human gesture (ADR-0009). + +## Validation + +- `sweep-guard` / `prose-guard` (ADR-0006) enforce the skill's metadata-only vs linked-release envelopes; a mismatched release fails CI. +- `MAINTAINING.md` pre-merge checklist: `SKILL.md`/`version.json`/`CHANGELOG.md` agree on version; `verified_date`==`published_date`; every `verified_commits` entry is a real SHA; every live-docs URL resolves; no guessed SDK/CLI/MCP surface. +- Registry check: every component the skill covers carries `feeds_skills:`; components it doesn't teach do not. +- Review trigger: replacing the reuse-the-docs-workflow model, changing the registry wiring, or moving to full unattended auto-release supersedes this ADR (and interacts with ADR-0009). + +## 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/docs/adr/README.md b/adr/README.md similarity index 100% rename from docs/adr/README.md rename to adr/README.md diff --git a/docs/adr/TEMPLATE.md b/adr/TEMPLATE.md similarity index 100% rename from docs/adr/TEMPLATE.md rename to adr/TEMPLATE.md diff --git a/docs/adr/TOOLING.md b/adr/TOOLING.md similarity index 100% rename from docs/adr/TOOLING.md rename to adr/TOOLING.md diff --git a/planning/skill-open-questions.md b/planning/skill-open-questions.md new file mode 100644 index 0000000..dd6a388 --- /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:** `start` was introduced to produce a clean Claude-plugin slash form, i.e. a vendor channel shaped the canonical identity; `start` is opaque to any non-Claude loader. The provenance of `start` is unclear. +- **To do:** decide whether to realign the on-disk name to `autonomi-developer` (keeping `/developer:start` only as a Claude-channel alias), and record the resolution in a superseding ADR to 0012. + +## 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). The Claude marketplace channel is realised; `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 old mode 100755 new mode 100644 index 7f56bde..c6fb743 --- a/scripts/adr-governance.py +++ b/scripts/adr-governance.py @@ -16,7 +16,7 @@ import sys from pathlib import Path -ADR_DIR = Path("docs/adr") +ADR_DIR = Path("adr") 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$")