From 60f3072a658b91033a6ecb1744caee3445aad327 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 09:49:35 -0400 Subject: [PATCH 01/21] fix(release): align target-aware command contract Keep the public release route, governing skill, version surfaces, and changelog-footer policy consistent across all four plugin targets. CHANGELOG: Correct the release command and skill contracts for target selection and required changelog footers. --- CHANGELOG.md | 12 ++++ README.md | 2 +- plugins/ca/.claude-plugin/plugin.json | 2 +- plugins/ca/commands/release.md | 89 ++++++++++++++++----------- plugins/ca/skills/release/SKILL.md | 4 +- 5 files changed, 70 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0d3696..3c0bdce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,18 @@ predate the plugin rewrite and are grouped by date. ## [Unreleased] +## [2.10.6] — 2026-07-30 + +### Fixed + +- **The release command now matches the target-aware release skill.** Its + operator contract names `ca`, `ca-codex`, `ca-sandbox`, and `ca-pi`, selects + the correct tag series and manifest for each target, and points maintainers + to the matching changelog. The command and skill now also agree that + `CHANGELOG:` footers are mandatory for `feat` and `fix`, rolled for `perf` + when present, and never synthesized for `refactor`. The generated reference + no longer publishes the obsolete `ca`-only or all-bumping-footer contracts. + ## [2.10.5] — 2026-07-30 ### Changed diff --git a/README.md b/README.md index f56a4c9e..dd7f4286 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Every intent routes through a gated skill or reviewer agent. Nothing commits unt Claude Code plugin Codex plugin Pi Feature Forge preview -version 2.10.5 +version 2.10.6 commands skills agents diff --git a/plugins/ca/.claude-plugin/plugin.json b/plugins/ca/.claude-plugin/plugin.json index b3d77dc5..79db2219 100644 --- a/plugins/ca/.claude-plugin/plugin.json +++ b/plugins/ca/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "ca", "displayName": "codeArbiter", "description": "Orchestration layer for Claude Code. Routes every intent through gated skills and reviewer agents, drives spec-driven TDD, mechanically enforces the commit and audit-trail gates, decides via SMARTS, and keeps an append-only audit trail. Requires Python 3 on PATH. Dormant until you opt a repo in; run /ca:init to activate.", - "version": "2.10.5", + "version": "2.10.6", "author": { "name": "arbiterForge" }, "license": "AGPL-3.0-only", "homepage": "https://github.com/arbiterForge/codeArbiter", diff --git a/plugins/ca/commands/release.md b/plugins/ca/commands/release.md index ad3379ef..f2fdac0e 100644 --- a/plugins/ca/commands/release.md +++ b/plugins/ca/commands/release.md @@ -1,52 +1,71 @@ --- -description: Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. -argument-hint: [""] | --auto | --dry-run +description: Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. +argument-hint: "[ca | ca-codex | ca-sandbox | ca-pi]" --- -# /ca:release — tagged release +# /ca:release — target-aware tagged release -The only permitted path to a version tag. A release is a deployment-readiness assertion: the codebase at this SHA satisfies the bar for shipping. `/ca:release` aggregates existing compliance — it does not duplicate it. +The only permitted path to a version tag. A release is a deployment-readiness assertion for one +specific plugin payload at one commit. `/ca:release` routes to the `release` skill; it does not +duplicate the release gates here. -## Flow +## Target + +The optional argument selects one independently-versioned plugin. A bare command defaults to `ca`. -Routes to the `release` skill. `/ca:release` targets the **`ca`** plugin only (ADR-0007): `LAST_TAG` -is the newest `ca` SemVer tag (`v*`, never the sibling `ca-sandbox-v*`), and the release window is the -`plugins/ca/`-scoped commit set — a `ca-sandbox` tag or commit never bumps `ca` or lands in its -changelog. +| Target | Tag series | Manifest | Changelog | +|---|---|---|---| +| `ca` | `vX.Y.Z` | `plugins/ca/.claude-plugin/plugin.json` | `CHANGELOG.md` | +| `ca-codex` | `ca-codex-vX.Y.Z` | `plugins/ca-codex/.codex-plugin/plugin.json` | `plugins/ca-codex/CHANGELOG.md` | +| `ca-sandbox` | `ca-sandbox-vX.Y.Z` | `plugins/ca-sandbox/.claude-plugin/plugin.json` | `plugins/ca-sandbox/CHANGELOG.md` | +| `ca-pi` | `ca-pi-vX.Y.Z` | `plugins/ca-pi/package.json` plus generated root `package.json` | `plugins/ca-pi/CHANGELOG.md` | -1. **Pre-flight** — working tree clean, on the configured release branch, suite green, no blocking - `[CONFIRM-NN]` open, HEAD not produced via `/ca:override`. Identify `LAST_TAG` and the release window. -2. **Version bump (SemVer)** — classify every commit in `LAST_TAG..HEAD` by Conventional Commits type - and apply the highest-precedence bump (major beats minor beats patch). An explicit version that - disagrees with the classification BLOCKS — the bump is never silently up- or downgraded. -3. **Changelog** — roll up the `CHANGELOG:` footers from `feat`, `fix`, and `perf` commits into a new - section. BLOCK if any `feat`/`fix` commit lacks the footer; never auto-fill it. -4. **Tag** — compose the annotated tag. Never push it to a remote without explicit user - authorization — publication is a separate decision. +An unknown target STOPs. It is never guessed from the current branch or changed files. + +## Flow -`--dry-run` runs every gate and surfaces the readiness report, then STOPs before composing the tag. +1. **Resolve the target row.** The shared release helper supplies the tag prefix. The last tag and + commit window are scoped to that tag series and payload only. +2. **Prove readiness.** The tree is clean, HEAD is not the default branch, the target has a + bump-earning commit window, its manifests agree, and every shipped bundle rebuilds cleanly. +3. **Derive version and changelog.** Conventional Commits determine the bump. Every `feat` and + `fix` requires a `CHANGELOG:` footer; `perf` footers are rolled when present, while `refactor` + earns a patch without inventing a user-facing entry. +4. **Sync release surfaces.** The selected manifest and target-specific generated or README + surfaces move together through the normal commit gate. +5. **Compose and report.** An annotated tag is created locally and the target, classification, + changelog section, and tag SHA are reported. +6. **Publish on authorization.** The tag and GitHub Release publish together only after the user + explicitly approves publication. The release is read back and the tag provenance is recorded. -## Arguments +Only `ca` may hold GitHub's repo-wide Latest badge. Every sibling release explicitly declines it. -- **``** (e.g. `"1.2.3"`) — explicit version; Phase 2 still classifies the window and BLOCKs - on disagreement. -- **`--auto`** — derive the version from the commit log (default when no version is given). -- **`--dry-run`** — run all gates, compose nothing. Combines with `--auto` or an explicit version. - An explicit version never combines with `--auto` — they are mutually exclusive. +## Usage -## Routes to +```text +/ca:release +/ca:release ca +/ca:release ca-codex +/ca:release ca-sandbox +/ca:release ca-pi +``` -`release` (`${CLAUDE_PLUGIN_ROOT}/skills/release/SKILL.md`). +Codex uses `$ca-release `; Pi uses `/ca-release `. -## When NOT to use +## When not to use it -- Tagging an in-progress branch → land work first via `/ca:feature` / `/ca:fix`. -- Pushing an already-composed tag → that is a separate user-authorized step. -- A changelog only → it is a phase output, not a standalone deliverable. +- Work is still in progress: land it through the owning feature, fix, or chore lane. +- The tree is dirty or HEAD is the default branch: prepare a release branch first. +- You only want readiness information: inspect the target's release workflow and commit window + without invoking the mutating release lane. +- A published release is wrong: fix forward and publish a new version. Never move or delete the + published tag. ## Hard gate -MUST NOT compose a tag on a red suite. MUST NOT silently up- or downgrade the SemVer classification — -an explicit version that disagrees with the commit log BLOCKS. MUST NOT auto-fill a missing -`CHANGELOG:` footer. MUST NOT write to the default branch or force-push. MUST NOT push the tag without -explicit user authorization. Any BLOCK may be bypassed only via `/ca:override`. +MUST resolve exactly one supported target before release work begins. MUST scope the tag series, +commit window, manifest, changelog, payload, and built artifacts to that target. MUST derive SemVer +from the target's Conventional-Commit history and MUST NOT tag a non-bumping window. MUST NOT +auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release without explicit +authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, retarget, or +delete a published tag; correction is a new version. diff --git a/plugins/ca/skills/release/SKILL.md b/plugins/ca/skills/release/SKILL.md index ead9cae7..be4fbf25 100644 --- a/plugins/ca/skills/release/SKILL.md +++ b/plugins/ca/skills/release/SKILL.md @@ -63,7 +63,7 @@ Derive the bump mechanically from the commit log; do not guess it. A badge, prose-count, README-table, catalog, or root-manifest drift is a **BLOCK** — reconcile it before tagging. CI's badge-consistency guard (`.github/scripts/check_badge_consistency.py`) and the per-plugin version-bump gates are the mechanical backstop; if either is red, this step is not done. 6. If the changelog edit or the surface sync needs to land as a commit before tagging, route it through `commit-gate`. Do not reimplement the commit path here. -Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a bumping commit's `CHANGELOG:` footer is missing, or any surface drifts. +Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a `feat`/`fix` commit's required `CHANGELOG:` footer is missing, or any surface drifts. ## Phase 2 — Tag & report · gate: BLOCK @@ -121,7 +121,7 @@ The one case that is **not** a correction: a tag pushed by mistake with **no** G - MUST NOT assert `--latest` for any target except `ca`, and not even for `ca` unless the tag is the newest release across all four series. - MUST use the Phase-1 changelog section verbatim as the GitHub Release notes — never re-derive or hand-write them. - MUST NOT guess the version — derive it from the commit log. A `feat` in the window cannot ship as a `patch`. -- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past one — a missing footer on a bumping commit is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. +- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past a missing required footer — a `feat`/`fix` commit without one is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. A `perf` footer is rolled when present; `refactor` earns a patch but does not require or synthesize a user-facing entry. - MUST NOT tag a non-bumping window — `test`/`docs`/`chore`/`ci`-only sets do not release. - **MUST NOT move, retarget, delete, or re-point a published tag**, in any namespace (`v*`, `ca-codex-v*`, `ca-sandbox-v*`, `ca-pi-v*`), for any reason — no `git push --force` on a tag, no `git push --delete`, no `gh release delete`. A bad release is corrected by publishing a NEW version; see "Recovering from a bad release". There is no break-glass path (issue #386). - MUST record every newly published tag in `.github/published-tags.json`, and MUST NOT edit an existing entry to silence a red `[CHECK] | [REPO] | Published tag immutability` run — a red run means a ref moved, and the manifest is the evidence of where it belonged. From 900caa5a1aa91ef4c2909c644dd94d7c7075c975 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 09:54:00 -0400 Subject: [PATCH 02/21] docs(site): build first-class learning experience Turn the documentation into a source-aware curriculum and product-quality reference so a new operator can progress from installation to governed power-user workflows with verifiable evidence. Replace simulated splash content with a rendered replay of a direct shipped-hook invocation, add a custom visual system, and make host availability and generated references derive from shipped source. --- .codearbiter/overrides.log | 3 + site/DESIGN-SYSTEM.md | 111 ++ site/README.md | 197 ++-- site/astro.config.mjs | 55 +- site/package.json | 3 +- site/scripts/capture-hook-proof.ts | 296 ++++++ site/scripts/gen.ts | 5 + .../generator/configuration-reference.ts | 461 +++++++++ site/scripts/generator/generate.ts | 40 +- .../scripts/generator/host-command-catalog.ts | 66 ++ site/scripts/generator/render-agent-page.ts | 9 +- site/scripts/generator/render-command-page.ts | 9 +- .../generator/render-hooks-reference.ts | 10 +- .../generator/render-reference-lead.ts | 90 ++ site/scripts/generator/render-skill-page.ts | 9 +- site/scripts/generator/render-source-embed.ts | 7 +- site/scripts/generator/source-ref.ts | 13 + .../scripts/generator/truncate-description.ts | 28 +- site/scripts/generator/types.ts | 3 + .../assets/fonts/LICENSE-JetBrains-Mono.txt | 93 ++ site/src/assets/fonts/LICENSE-Manrope.txt | 93 ++ .../jetbrains-mono-latin-wght-normal.woff2 | Bin 0 -> 40404 bytes .../fonts/manrope-latin-wght-normal.woff2 | Bin 0 -> 24836 bytes site/src/assets/hero-gates.webp | Bin 0 -> 69580 bytes site/src/assets/proof/hook-proof-poster.webp | Bin 0 -> 23862 bytes site/src/assets/proof/hook-proof.json | 25 + site/src/assets/proof/hook-proof.mp4 | Bin 0 -> 115063 bytes site/src/assets/proof/hook-proof.webm | Bin 0 -> 116387 bytes site/src/components/ArbiterIcon.astro | 81 ++ site/src/components/GateCatchTerminal.astro | 84 -- site/src/components/HookProof.astro | 117 +++ site/src/components/InstallTerminal.astro | 96 -- site/src/components/PageContext.astro | 47 + site/src/components/PageTitle.astro | 25 +- site/src/components/Sidebar.astro | 15 + site/src/components/SidebarSublist.astro | 199 ++++ site/src/content.config.ts | 16 +- .../src/content/docs/codearbiter-directory.md | 77 +- site/src/content/docs/concepts.md | 66 +- site/src/content/docs/concepts/adrs.md | 19 + .../src/content/docs/concepts/auditability.md | 47 + site/src/content/docs/concepts/checkpoints.md | 25 + site/src/content/docs/concepts/gated-lanes.md | 34 + .../docs/concepts/hardening-history.md | 17 +- .../docs/concepts/jit-context-injection.md | 41 + .../docs/concepts/persona-and-context.md | 29 + .../content/docs/concepts/provenance-drift.md | 31 + site/src/content/docs/concepts/smarts.md | 7 + site/src/content/docs/enforcement.md | 14 +- site/src/content/docs/faq.md | 71 +- .../content/docs/feature-forge/overview.md | 21 + .../feature-forge/using-preview-features.md | 22 + .../docs/feature-forge/whats-in-the-forge.mdx | 7 + .../docs/getting-started/choose-your-host.md | 60 ++ .../getting-started/claude-code-and-codex.md | 11 +- .../docs/getting-started/compatibility.md | 13 +- .../content/docs/getting-started/install.md | 14 +- site/src/content/docs/getting-started/pi.md | 18 + .../docs/getting-started/quickstart.md | 159 ++- site/src/content/docs/glossary.md | 6 + .../docs/guides/adding-a-dependency.md | 45 +- .../content/docs/guides/autonomous-sprints.md | 140 ++- site/src/content/docs/guides/ca-sandbox.md | 77 +- site/src/content/docs/guides/feature-lane.md | 56 + site/src/content/docs/guides/opt-in-a-repo.md | 49 +- .../content/docs/guides/overriding-a-gate.md | 25 +- .../src/content/docs/guides/recording-adrs.md | 111 +- .../docs/guides/releasing-a-version.md | 185 ++-- .../src/content/docs/guides/the-statusline.md | 15 +- .../content/docs/guides/troubleshooting.md | 19 +- site/src/content/docs/guides/uninstalling.md | 41 +- site/src/content/docs/hooks.md | 13 +- site/src/content/docs/index.mdx | 392 +++---- site/src/content/docs/learn/index.mdx | 144 +++ site/src/content/docs/overview.md | 6 + site/src/curated/commands/cleanup.md | 50 + site/src/curated/commands/release.md | 69 +- site/src/curated/skills/post-merge-cleanup.md | 38 + site/src/curated/skills/release.md | 52 +- site/src/pages/404.astro | 53 + site/src/styles/design-system.css | 783 ++++++++++++++ site/src/styles/landing.css | 972 +++++++++--------- site/src/styles/theme.css | 149 +-- site/test/content/learning-contract.test.ts | 50 + .../generator/configuration-reference.test.ts | 91 ++ site/test/generator/curated-coverage.test.ts | 47 + site/test/generator/generate.test.ts | 4 +- .../generator/host-command-catalog.test.ts | 47 + site/test/generator/render-agent-page.test.ts | 2 +- .../generator/render-command-page.test.ts | 29 +- .../generator/render-hooks-reference.test.ts | 7 +- site/test/generator/render-skill-page.test.ts | 2 +- .../generator/render-source-embed.test.ts | 4 +- site/test/generator/source-ref.test.ts | 15 + .../generator/truncate-description.test.ts | 12 + site/test/landing/landing-page.test.ts | 498 +++------ site/test/search/search-contract.test.ts | 46 + 97 files changed, 5614 insertions(+), 1839 deletions(-) create mode 100644 site/DESIGN-SYSTEM.md create mode 100644 site/scripts/capture-hook-proof.ts create mode 100644 site/scripts/generator/configuration-reference.ts create mode 100644 site/scripts/generator/host-command-catalog.ts create mode 100644 site/scripts/generator/render-reference-lead.ts create mode 100644 site/scripts/generator/source-ref.ts create mode 100644 site/src/assets/fonts/LICENSE-JetBrains-Mono.txt create mode 100644 site/src/assets/fonts/LICENSE-Manrope.txt create mode 100644 site/src/assets/fonts/jetbrains-mono-latin-wght-normal.woff2 create mode 100644 site/src/assets/fonts/manrope-latin-wght-normal.woff2 create mode 100644 site/src/assets/hero-gates.webp create mode 100644 site/src/assets/proof/hook-proof-poster.webp create mode 100644 site/src/assets/proof/hook-proof.json create mode 100644 site/src/assets/proof/hook-proof.mp4 create mode 100644 site/src/assets/proof/hook-proof.webm create mode 100644 site/src/components/ArbiterIcon.astro delete mode 100644 site/src/components/GateCatchTerminal.astro create mode 100644 site/src/components/HookProof.astro delete mode 100644 site/src/components/InstallTerminal.astro create mode 100644 site/src/components/PageContext.astro create mode 100644 site/src/components/Sidebar.astro create mode 100644 site/src/components/SidebarSublist.astro create mode 100644 site/src/content/docs/getting-started/choose-your-host.md create mode 100644 site/src/content/docs/learn/index.mdx create mode 100644 site/src/curated/commands/cleanup.md create mode 100644 site/src/curated/skills/post-merge-cleanup.md create mode 100644 site/src/pages/404.astro create mode 100644 site/src/styles/design-system.css create mode 100644 site/test/content/learning-contract.test.ts create mode 100644 site/test/generator/configuration-reference.test.ts create mode 100644 site/test/generator/curated-coverage.test.ts create mode 100644 site/test/generator/host-command-catalog.test.ts create mode 100644 site/test/generator/source-ref.test.ts create mode 100644 site/test/search/search-contract.test.ts diff --git a/.codearbiter/overrides.log b/.codearbiter/overrides.log index 71fcf7c6..b25b0948 100644 --- a/.codearbiter/overrides.log +++ b/.codearbiter/overrides.log @@ -23,3 +23,6 @@ 2026-07-19T21:45:48.4804940-04:00 | BY: SUaDtL@users.noreply.github.com | GATE: append-only sprint-log byte rewrite | REASON: Content-preserving CRLF-to-LF normalization of the already appended sprint records is required for the mandatory git diff --check gate; exact text and order remain unchanged; user explicitly approved this immediate action. 2026-07-30T07:22:18.5387323-04:00 | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: — [2026-07-30T11:48:22Z] | BY: session-cleanup | HOST: codex | DEV: exit | NOTE: cleared by SessionStart (prior session ended mid-dev without $ca-arbiter) +2026-07-30T07:24:09.5507998-04:00 | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: isolated docs-site worktree +[2026-07-30T09:25:46.2296648-04:00] | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: resume docs-site work after upstream replay removed local marker +[2026-07-30T09:43:51.6342839-04:00] | BY: SUaDtL@users.noreply.github.com | DEV: exit diff --git a/site/DESIGN-SYSTEM.md b/site/DESIGN-SYSTEM.md new file mode 100644 index 00000000..19c04e41 --- /dev/null +++ b/site/DESIGN-SYSTEM.md @@ -0,0 +1,111 @@ +# Documentation design system + +This is the contract for codeArbiter's public documentation. It exists so the landing page, +hand-authored guidance, and generated reference feel like one product without turning every page +into a custom layout. + +## Product principles + +1. **Proof before promise.** A claim about enforcement is paired with a mechanism, transcript, + dated verification record, or source link. +2. **One page, one reader outcome.** A reader should know why the page matters, how to act on it, + what success looks like, and where to go next. +3. **Source-visible reference.** Generated pages add reader orientation but retain the exact shipped + source in a collapsed block. Curated prose never replaces the implementation contract. +4. **Local-first delivery.** Fonts and production assets ship with the static build. The site makes + no runtime request to a font or analytics service. +5. **Quiet confidence.** Gold identifies decisions, gates, and primary actions. It is not a general + highlight color. Motion explains sequence and is removed under `prefers-reduced-motion`. + +## SMARTS decisions + +### Landing shell + +| Option | Scalable | Maintainable | Available | Reliable | Testable | Securable | +|---|---|---|---|---|---|---| +| Keep the home page inside the documentation sidebar | Adequate | Strong | Strong | Strong | Strong | Strong | +| Use a full-width Starlight splash and keep the docs shell for inner pages | Strong | Strong | Strong | Strong | Strong | Strong | +| Build a separate marketing application | Strong | Weak | Adequate | Weak | Weak | Adequate | + +**Decision:** use Starlight's splash shell. It creates a first-class product entrance while keeping +one build, one navigation system, and one link/search index. + +### Typography + +| Option | Scalable | Maintainable | Available | Reliable | Testable | Securable | +|---|---|---|---|---|---|---| +| System fonts only | Adequate | Strong | Strong | Strong | Strong | Strong | +| Vendor reviewed variable-font subsets with their licenses | Strong | Strong | Strong | Strong | Strong | Strong | +| Runtime Google Fonts request | Adequate | Adequate | Weak | Weak | Adequate | Weak | + +**Decision:** bundle the Latin subsets of Manrope Variable and JetBrains Mono Variable as static +assets, with their complete OFL-1.1 license texts beside them. They add no package or production +network dependency and give product and technical surfaces distinct voices. + +### Reference orientation + +| Option | Scalable | Maintainable | Available | Reliable | Testable | Securable | +|---|---|---|---|---|---|---| +| Hand-edit generated pages | Weak | Weak | Strong | Weak | Weak | Adequate | +| Keep source embeds only | Adequate | Strong | Strong | Strong | Strong | Strong | +| Add collection-specific orientation in the generator and keep source embeds | Strong | Strong | Strong | Strong | Strong | Strong | + +**Decision:** commands state how to invoke them in Claude Code and Codex; skills state that the +orchestrator routes to them; agents state that an owning skill dispatches them. The generator owns +that distinction across the full collection. + +## Tokens and primitives + +Shared tokens and primitives live in `src/styles/design-system.css`. + +- **Typography:** `--ca-font-sans`, `--ca-font-mono`, and the `--ca-text-*` scale. +- **Spacing:** `--ca-space-1` through `--ca-space-9`. +- **Surfaces:** `--ca-bg`, `--ca-bg-raised`, `--ca-bg-panel`, `--ca-line`. +- **Meaning:** `--ca-brand` for gates/actions, `--ca-positive` for verified state, + `--ca-danger` for blocks, and `--ca-preview` for Feature Forge status. +- **Components:** `.ca-button`, `.ca-panel`, `.ca-pill`, `.ca-eyebrow`, and + `.ca-reference-lead`. + +Page-specific selectors belong in `src/styles/landing.css` or a named component. Do not add +one-off colors, font stacks, or spacing values to content files. + +## Page completeness contract + +### Getting Started + +Answer: what must be installed, what command to run, what the reader should observe, how to verify +enforcement, and where host behavior differs. + +### Guides + +State the outcome and prerequisites, give an ordered procedure, show observable success, cover the +likely block or recovery path, and link the exact command reference. + +### Concepts + +Explain the problem, the mental model, one concrete example, how the concept changes real use, its +limits, and the next practical page. A definition alone is incomplete. + +### Generated reference + +- **Command:** invocation, purpose, example, gates, alternatives, exact source. +- **Skill:** routing context, phases or behavior, exits, gates, related commands, exact source. +- **Agent:** dispatch context, tool/model boundary, output or findings contract, related routes, + exact source. +- **Hook gate:** tag, condition, effect, exact emitted message, source location. + +### Trust and lifecycle + +Compatibility, enforcement, privacy/network behavior, uninstall, troubleshooting, changelog, and +license must be findable without reading the landing page top to bottom. + +## Required verification + +Before documentation changes ship: + +1. Generate the reference and build the static site. +2. Run unit tests, typecheck, and the post-build link audit. +3. Crawl every sitemap route for HTTP success, one H1, a description, and image alt text. +4. Inspect the landing, one page from each hand-authored category, and one page from each generated + collection at desktop and mobile widths. +5. Test keyboard focus, search, reduced motion, and horizontal overflow. diff --git a/site/README.md b/site/README.md index dd94a4f2..3caea595 100644 --- a/site/README.md +++ b/site/README.md @@ -1,99 +1,116 @@ # codeArbiter documentation site -An [Astro Starlight](https://starlight.astro.build/) site for codeArbiter. The -**Overview** and **Concepts** pages are hand-written; the entire **Reference** -section (commands, skills, agents) is **auto-generated from the plugin's own -frontmatter**, so it can never drift from the source. +The public documentation for codeArbiter, built with +[Astro Starlight](https://starlight.astro.build/). It combines: -Everything is JavaScript/TypeScript — the generator is TypeScript, tests run on -[Vitest](https://vitest.dev/), and the package manager is npm (Node LTS). +- a purpose-built product splash page; +- hand-authored onboarding, guide, concept, and operations pages; +- generated command, skill, agent, hook-gate, configuration, and changelog reference; and +- curated explanations layered over the exact plugin source. -## Quick start +The visual and content contract lives in [`DESIGN-SYSTEM.md`](./DESIGN-SYSTEM.md). Read it before +adding a component or documentation page. + +## Local development + +Use Node LTS and npm: ```bash cd site -npm install -npm run dev # generates the reference, then serves at http://localhost:4321 +npm ci +npm run dev ``` -| Command | What it does | -| --- | --- | -| `npm run dev` | Run the generator (`predev`), then start the Astro dev server. | -| `npm run build` | Run the generator (`prebuild`), then build the static site to `dist/`. | -| `npm run preview` | Serve the built `dist/` locally. | -| `npm run gen` | Regenerate the reference pages + sidebar only. | -| `npm test` | Run the Vitest suite for the generator. | -| `npm run typecheck` | Type-check the generator with `tsc --noEmit`. | - -`npm run gen` is wired into `predev` and `prebuild`, so the reference is always -fresh before the site is served or built. - -## How the generator works - -The generator lives in `scripts/generator/` and is built as many small, -single-responsibility, independently-tested modules. The pipeline: - -1. **`collect-sources`** reads `plugins/ca/commands/*.md`, - `plugins/ca/skills/*/SKILL.md`, and `plugins/ca/agents/*.md`. -2. **`split-frontmatter` + `parse-fields` → `parse-doc`** parse each file's - frontmatter (`name`, `description`, `tools`, `model`) into a record. The - parser is dep-free and tolerant: missing fields are simply absent, extra - fields are preserved, malformed lines are skipped (never thrown). -3. **`derive-name`** picks a display name (the `name` field, or the filename for - commands); **`slugify` + `assign-slugs`** produce stable, collision-free slugs. -4. **`render-agent-page` / `render-command-page` / `render-skill-page`** emit one - Markdown page per source. Agent pages render the model tier (`model-tier`) and - tools list (`format-tools-list`); commands and skills render name + description. -5. **`build-index`** groups the pages by type for the sidebar and index. -6. **`generate`** orchestrates the above, writes one page per source to - `src/content/docs/reference/{commands,skills,agents}/.md`, writes - `src/content/docs/reference/index.md`, and writes the sidebar data to - `src/generated/sidebar.json` (consumed by `astro.config.mjs`). - -`generate` is **idempotent** — running it twice over the same sources produces -byte-identical output. `INDEX.md` catalog files in the plugin are skipped (they -are routing tables, not documentable entities). Source files are normalized -(CRLF→LF, BOM stripped) at the read boundary so a Windows checkout parses cleanly. - -### Adding to the reference - -Don't edit `src/content/docs/reference/**` by hand — it's regenerated. To change -the reference, change the frontmatter in `plugins/ca/**` and re-run `npm run gen`. - -## Tests - -The generator is built test-first: each module has its own Vitest file under -`test/generator/`, with fixtures under `test/fixtures/`. Run `npm test`. - -## Deploying to GitHub Pages - -The site is wired for GitHub Pages at `https://arbiterforge.github.io/codeArbiter/`. -The pieces are already in place: - -- **Base path** — `astro.config.mjs` sets `site` + `base: '/codeArbiter'`. Note this - also applies in local dev (the dev server serves at `http://localhost:4321/codeArbiter/`). -- **Base-aware links** — Starlight prepends `base` to its own navigation (sidebar, - next/prev), but **not** to author-written links. So the hand-authored links are - base-safe explicitly: the home page (`index.mdx`) bakes the base into its links - (`/codeArbiter/overview/`); the plain-markdown pages use relative links - (`../concepts/`); the generated reference index links are relative too. (Astro does - **not** auto-rewrite root-relative links in content, and `import.meta.env.BASE_URL` - carries no trailing slash — that's why these are explicit. If you change `base`, - update the hardcoded home links to match.) -- **Workflow** — `.github/workflows/docs.yml` builds with `withastro/action` - (`path: site`) and deploys via `actions/deploy-pages`. It triggers on push to - `main` touching `site/**` or `plugins/ca/**`, and can be run manually - (`workflow_dispatch`). The reference is regenerated in CI, so committing the - generated output isn't needed. - -**To go live:** enable Pages once (Settings → Pages → Source: **GitHub Actions**), -then merge to `main` (or run the workflow manually). Pages and Actions are free for -this public repo. - -### Versioning against releases - -The reference reflects whatever `plugins/ca/**` is on `main`, and the workflow's -`paths` filter rebuilds the docs whenever the plugin payload changes — so a release -that bumps the payload republishes the reference automatically. Publishing *versioned* -docs (one set per release tag) is a later step: add Starlight multi-version support or -build one site per tag into a versioned subpath, once single-version docs are validated. +The development URL is `http://localhost:4321/codeArbiter/` because the production site is a +GitHub Pages project site. + +| Command | Purpose | +|---|---| +| `npm run dev` | Regenerate reference content, then start Astro's development server. | +| `npm run gen` | Regenerate command, skill, agent, hook, configuration, changelog, and sidebar artifacts. | +| `npm test` | Run generator, content-contract, and landing-page tests. | +| `npm run typecheck` | Type-check the generator and site support code. | +| `npm run build` | Regenerate content, build every route, and create the Pagefind index. | +| `npm run preview` | Serve the current production build locally. | + +Generated files are deterministic. If two runs against the same source produce a diff, the +generator has a defect. + +## Content ownership + +Choose the owner before editing: + +| Content | Owner | Where to change it | +|---|---|---| +| Splash, onboarding, guides, concepts, operations | Human-authored page | `src/content/docs/` | +| Command behavior | Plugin source + curated explanation | `../plugins/ca/commands/` and `src/curated/commands/` | +| Skill behavior | Plugin source + curated explanation | `../plugins/ca/skills/` and `src/curated/skills/` | +| Agent behavior | Plugin source + curated explanation | `../plugins/ca/agents/` and `src/curated/agents/` | +| Hook gate messages and source links | Hook call sites | `../plugins/ca/hooks/*.py` | +| Configuration variables | Typed configuration catalog | `scripts/generator/configuration-reference.ts` | +| Release history | Repository changelog | `../CHANGELOG.md` | +| Navigation | Hand-authored IA + generated reference groups | `astro.config.mjs` and `src/generated/sidebar.json` | +| Visual tokens and component rules | Design system | `DESIGN-SYSTEM.md` and `src/styles/design-system.css` | + +Do not edit generated pages under `src/content/docs/reference/{commands,skills,agents}/` or the +generated `reference/hooks-gates.md`, `reference/configuration.md`, and `changelog.md`. Run +`npm run gen` after changing their source. + +### Generated reference pipeline + +`scripts/gen.ts` coordinates small modules under `scripts/generator/`: + +1. collect the plugin source files; +2. parse and normalize their frontmatter and source; +3. assign stable, collision-free slugs; +4. combine a host-aware orientation lead, curated explanation, and exact source disclosure; +5. emit pages and reference sidebar data; +6. extract hook gates from literal `block()` and `remind()` call sites; +7. emit the typed, maintainer-reviewed configuration catalog; and +8. transform the repository changelog for Starlight. + +Every command, skill, and agent must have a curated explanation. The coverage test fails when a +new source entity ships without one. + +## Writing a usable page + +A page is complete when a reader can tell: + +1. what outcome it provides; +2. what must be true before they start; +3. which host-native syntax to use; +4. what inputs and defaults apply; +5. what to do and what successful output looks like; +6. how to verify the result independently; +7. where the workflow stops and how to recover; +8. whether the action is reversible; and +9. what to read or invoke next. + +Concept pages adapt the same contract: explain the mental model, show a concrete example, state +where the model matters in practice, and link to the workflow and exact reference. + +Use root-absolute content links such as `/guides/feature-lane/`. The local +`rehype-base-links` processor prefixes the configured project-site base. In Astro component props, +use `import.meta.env.BASE_URL`. Never hard-code `/codeArbiter/` in a link. + +## Verification + +Before opening a documentation pull request: + +```bash +npm test +npm run typecheck +npm run build +``` + +Then crawl the built sitemap and inspect the splash, one page from every hand-authored section, +and representative command, skill, agent, hook, configuration, and changelog pages at desktop and +mobile widths. Check response status, one visible H1, descriptions, image alt text, keyboard +navigation, focus visibility, horizontal overflow, contrast, reduced motion, and useful Pagefind +results. + +## Deployment + +`.github/workflows/docs.yml` builds the site from `site/` and deploys GitHub Pages when relevant +changes land on `main`, or when started manually. The site and deployment use only repository and +GitHub Pages resources; no paid service is required. diff --git a/site/astro.config.mjs b/site/astro.config.mjs index fd550e1f..7f3b1b4c 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -91,6 +91,9 @@ export default defineConfig({ starlight({ title: "codeArbiter", description: "Shared enforcement and project-context parity across Claude Code, Codex, and Pi.", + // A custom StarlightPage-backed src/pages/404.astro owns the production + // fallback without forcing a phantom docs collection lookup at build time. + disable404Route: true, logo: { src: "./src/assets/logo.svg", replacesTitle: true, @@ -113,8 +116,11 @@ export default defineConfig({ // inline search (live as you type, results drop below the box) // replacing Starlight's stock modal-on-click search Search: "./src/components/Search.astro", + // authored navigation groups with the codeArbiter icon language + Sidebar: "./src/components/Sidebar.astro", }, customCss: [ + "./src/styles/design-system.css", "./src/styles/theme.css", "./src/styles/callouts.css", "./src/styles/landing.css", @@ -129,59 +135,58 @@ export default defineConfig({ ], sidebar: [ { - label: "Getting Started", - collapsed: true, + label: "Start", + collapsed: false, items: [ { label: "What Is codeArbiter", slug: "overview" }, + { label: "Learning Path", slug: "learn" }, + { label: "Choose Your Host", slug: "getting-started/choose-your-host" }, { label: "Install", slug: "getting-started/install" }, + { label: "Protect Your First Repository", slug: "getting-started/quickstart" }, { label: "Claude Code + Codex", slug: "getting-started/claude-code-and-codex" }, { label: "Pi", slug: "getting-started/pi" }, - { label: "Quickstart", slug: "getting-started/quickstart" }, { label: "Compatibility", slug: "getting-started/compatibility" }, { label: "FAQ", slug: "faq" }, ], }, { - label: "Guides", + label: "Workflows", collapsed: true, items: [ { label: "Opt a Repository In", slug: "guides/opt-in-a-repo" }, - { label: "Record an Architecture Decision", slug: "guides/recording-adrs" }, - { label: "Add a Dependency Safely", slug: "guides/adding-a-dependency" }, - { label: "Set Up the Statusline", slug: "guides/the-statusline" }, { label: "Build a Feature End to End", slug: "guides/feature-lane" }, { label: "Run an Autonomous Sprint", slug: "guides/autonomous-sprints" }, - { label: "Override a Gate Safely", slug: "guides/overriding-a-gate" }, + { label: "Record an Architecture Decision", slug: "guides/recording-adrs" }, + { label: "Add a Dependency Safely", slug: "guides/adding-a-dependency" }, { label: "Cut a Release", slug: "guides/releasing-a-version" }, - { label: "Troubleshooting", slug: "guides/troubleshooting" }, - { label: "Uninstall & Disable", slug: "guides/uninstalling" }, - { label: "ca-sandbox: Explore Untrusted Code", slug: "guides/ca-sandbox" }, ], }, { - label: "Preview Features", + label: "Operate", collapsed: true, items: [ - { label: "What Is the Feature Forge", slug: "feature-forge/overview" }, - { label: "What's in the Forge", slug: "feature-forge/whats-in-the-forge" }, - { label: "Using Features Still in the Forge", slug: "feature-forge/using-preview-features" }, + { label: "Set Up the Statusline", slug: "guides/the-statusline" }, + { label: "Override a Gate Safely", slug: "guides/overriding-a-gate" }, + { label: "Troubleshooting", slug: "guides/troubleshooting" }, + { label: "Uninstall & Disable", slug: "guides/uninstalling" }, + { label: "Explore Untrusted Code", slug: "guides/ca-sandbox" }, ], }, { - label: "Concepts", + label: "Understand", collapsed: true, items: [ - { label: "Overview", slug: "concepts" }, + { label: "Concept Map", slug: "concepts" }, + { label: "The Gated-Lane Model", slug: "concepts/gated-lanes" }, { label: "SMARTS", slug: "concepts/smarts" }, + { label: "Enforcement & Security", slug: "enforcement" }, { label: "Provenance and Context Drift", slug: "concepts/provenance-drift" }, { label: "ADRs and the Decision Log", slug: "concepts/adrs" }, { label: "Just-in-Time Context Injection", slug: "concepts/jit-context-injection" }, - { label: "The Gated-Lane Model", slug: "concepts/gated-lanes" }, - { label: "Enforcement & Security", slug: "enforcement" }, - { label: "Hardening History", slug: "concepts/hardening-history" }, { label: "Checkpoints", slug: "concepts/checkpoints" }, { label: "The Persona-Register Split", slug: "concepts/persona-and-context" }, { label: "Auditability", slug: "concepts/auditability" }, + { label: "Selected Hardening Notes", slug: "concepts/hardening-history" }, ], }, { @@ -189,6 +194,7 @@ export default defineConfig({ collapsed: true, items: [ { label: "The .codearbiter/ Directory", slug: "codearbiter-directory" }, + { label: "Configuration", slug: "reference/configuration" }, { label: "Glossary", slug: "glossary" }, { label: "All Reference", slug: "reference" }, { label: "Hooks Reference", slug: "hooks" }, @@ -197,6 +203,15 @@ export default defineConfig({ ...referenceGroups, ], }, + { + label: "Labs", + collapsed: true, + items: [ + { label: "What Is the Feature Forge", slug: "feature-forge/overview" }, + { label: "What's in the Forge", slug: "feature-forge/whats-in-the-forge" }, + { label: "Use a Preview Feature", slug: "feature-forge/using-preview-features" }, + ], + }, ], }), ], diff --git a/site/package.json b/site/package.json index 3cc39d9a..24d73485 100644 --- a/site/package.json +++ b/site/package.json @@ -3,9 +3,10 @@ "version": "0.1.0", "private": true, "type": "module", - "description": "codeArbiter documentation site (MVP) — Astro Starlight + auto-generated reference", + "description": "Professional codeArbiter product documentation with source-backed generated reference.", "scripts": { "gen": "tsx scripts/gen.ts", + "capture:proof": "tsx scripts/capture-hook-proof.ts", "predev": "npm run gen", "dev": "astro dev", "prebuild": "npm run gen", diff --git a/site/scripts/capture-hook-proof.ts b/site/scripts/capture-hook-proof.ts new file mode 100644 index 00000000..74eb4fd8 --- /dev/null +++ b/site/scripts/capture-hook-proof.ts @@ -0,0 +1,296 @@ +import { createHash } from "node:crypto"; +import { + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join, relative, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; +import sharp from "sharp"; + +const repoRoot = resolve(import.meta.dirname, "../.."); +const siteRoot = resolve(import.meta.dirname, ".."); +const hookPath = join(repoRoot, "plugins", "ca", "hooks", "pre-bash.py"); +const outputDir = join(siteRoot, "src", "assets", "proof"); +const fixtureRoot = mkdtempSync(join(tmpdir(), "codearbiter-hook-proof-")); +const frameRoot = mkdtempSync(join(tmpdir(), "codearbiter-proof-frames-")); +const python = process.env.PYTHON || "python"; +const attemptedCommand = ["git", "add", "-A"].join(" "); + +function run( + command: string, + args: string[], + cwd: string, + options: { input?: string; env?: NodeJS.ProcessEnv } = {}, +) { + return spawnSync(command, args, { + cwd, + input: options.input, + env: options.env ?? process.env, + encoding: "utf8", + windowsHide: true, + }); +} + +function mustRun(command: string, args: string[], cwd: string) { + const result = run(command, args, cwd); + if (result.status !== 0) { + throw new Error( + `${command} ${args.join(" ")} failed (${result.status}):\n${result.stderr || result.stdout}`, + ); + } + return result; +} + +function escapeXml(value: string): string { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """); +} + +function wrapLine(value: string, width = 78): string[] { + const words = value.trim().split(/\s+/); + const lines: string[] = []; + let line = ""; + for (const word of words) { + if (!line) { + line = word; + } else if (`${line} ${word}`.length <= width) { + line += ` ${word}`; + } else { + lines.push(line); + line = word; + } + } + if (line) lines.push(line); + return lines.length ? lines : [""]; +} + +type TranscriptLine = { + text: string; + tone?: "normal" | "muted" | "command" | "blocked" | "success"; +}; + +function renderFrame(lines: TranscriptLine[], label: string): string { + const colors = { + normal: "#c7d0da", + muted: "#7f8da0", + command: "#ffd568", + blocked: "#ff7b72", + success: "#58d68d", + }; + const rows = lines.map((line, index) => { + const y = 146 + index * 31; + return `${escapeXml(line.text)}`; + }).join("\n"); + + return ` + + + + + + + + + + + + + + + + + + DIRECT HOOK REPLAY · ${escapeXml(label)} + + ${rows} + direct invocation · disposable repository · exact captured stderr +`; +} + +async function makeVideo(frames: Array<{ lines: TranscriptLine[]; duration: number }>, label: string) { + const concatLines: string[] = []; + for (const [index, frame] of frames.entries()) { + const framePath = join(frameRoot, `frame-${index.toString().padStart(2, "0")}.png`); + await sharp(Buffer.from(renderFrame(frame.lines, label))).png().toFile(framePath); + concatLines.push(`file '${framePath.replaceAll("\\", "/").replaceAll("'", "'\\''")}'`); + concatLines.push(`duration ${frame.duration}`); + } + const lastFrame = join(frameRoot, `frame-${(frames.length - 1).toString().padStart(2, "0")}.png`); + concatLines.push(`file '${lastFrame.replaceAll("\\", "/").replaceAll("'", "'\\''")}'`); + const concatPath = join(frameRoot, "frames.txt"); + writeFileSync(concatPath, `${concatLines.join("\n")}\n`, "utf8"); + + const shared = ["-loglevel", "error", "-f", "concat", "-safe", "0", "-i", concatPath, "-vf", "fps=30,format=yuv420p"]; + const mp4 = run( + "ffmpeg", + [...shared, "-c:v", "libx264", "-crf", "24", "-preset", "slow", "-movflags", "+faststart", "-y", join(outputDir, "hook-proof.mp4")], + siteRoot, + ); + if (mp4.status !== 0) { + throw new Error(`ffmpeg MP4 render failed (${mp4.status}):\n${mp4.stderr}`); + } + const webm = run( + "ffmpeg", + [...shared, "-c:v", "libvpx-vp9", "-b:v", "0", "-crf", "35", "-an", "-y", join(outputDir, "hook-proof.webm")], + siteRoot, + ); + if (webm.status !== 0) { + throw new Error(`ffmpeg WebM render failed (${webm.status}):\n${webm.stderr}`); + } + await sharp(lastFrame).webp({ quality: 88 }).toFile(join(outputDir, "hook-proof-poster.webp")); +} + +async function main() { + mkdirSync(outputDir, { recursive: true }); + mkdirSync(join(fixtureRoot, ".codearbiter"), { recursive: true }); + writeFileSync( + join(fixtureRoot, ".codearbiter", "CONTEXT.md"), + "---\narbiter: enabled\nstage: 2\n---\n\nDisposable documentation proof fixture.\n", + "utf8", + ); + writeFileSync(join(fixtureRoot, ".codearbiter", "gate-events.log"), "", "utf8"); + mustRun("git", ["init", "-q", "-b", "feat/proof"], fixtureRoot); + mustRun("git", ["config", "user.name", "codeArbiter proof"], fixtureRoot); + mustRun("git", ["config", "user.email", "proof@codearbiter.invalid"], fixtureRoot); + mustRun("git", ["add", ".codearbiter/CONTEXT.md", ".codearbiter/gate-events.log"], fixtureRoot); + mustRun("git", ["commit", "-q", "-m", "chore: initialize proof fixture"], fixtureRoot); + writeFileSync(join(fixtureRoot, "note.txt"), "This file must remain unstaged.\n", "utf8"); + + const statusBefore = mustRun("git", ["status", "--short"], fixtureRoot).stdout.trimEnd(); + const stagedBefore = mustRun("git", ["diff", "--cached", "--name-only"], fixtureRoot).stdout.trimEnd(); + const payload = JSON.stringify({ + hook_event_name: "PreToolUse", + tool_name: "Bash", + cwd: fixtureRoot, + tool_input: { command: attemptedCommand }, + }); + const hook = run(python, [hookPath], fixtureRoot, { + input: payload, + env: { ...process.env, CLAUDE_PROJECT_DIR: fixtureRoot, CODEARBITER_HOST: "claude" }, + }); + const statusAfter = mustRun("git", ["status", "--short"], fixtureRoot).stdout.trimEnd(); + const stagedAfter = mustRun("git", ["diff", "--cached", "--name-only"], fixtureRoot).stdout.trimEnd(); + const stderr = hook.stderr.trim().replace(/\r\n/g, "\n"); + const gateEvent = readFileSync(join(fixtureRoot, ".codearbiter", "gate-events.log"), "utf8").trim(); + + if (hook.status !== 2 || !stderr.includes("BLOCKED [H-03]")) { + throw new Error(`Expected a real H-03 exit-2 block; got exit=${hook.status} stderr=${stderr}`); + } + if (stagedAfter !== stagedBefore || !statusAfter.includes("?? note.txt")) { + throw new Error( + `The blocked call staged content or changed the intended file state:\n` + + `staged before=${stagedBefore}\nstaged after=${stagedAfter}\nstatus=${statusAfter}`, + ); + } + if (!gateEvent.includes("BLOCK [H-03]")) { + throw new Error(`Expected H-03 to append a real gate event; got: ${gateEvent}`); + } + + const capturedAt = new Date().toISOString(); + const sourceSha256 = createHash("sha256").update(readFileSync(hookPath)).digest("hex"); + const record = { + schema: 1, + evidenceKind: "direct-hook-invocation-rendered-replay", + hostDiscoveryProven: false, + capturedAt, + source: relative(repoRoot, hookPath).replaceAll("\\", "/"), + sourceSha256, + fixture: { + branch: "feat/proof", + activation: "arbiter: enabled", + untrackedBefore: statusBefore, + statusAfter, + stagedBefore, + stagedAfter, + gateEvent, + }, + invocation: { + attemptedCommand, + hookEvent: "PreToolUse", + toolName: "Bash", + exitCode: hook.status, + stderr, + commandExecuted: false, + }, + }; + writeFileSync(join(outputDir, "hook-proof.json"), `${JSON.stringify(record, null, 2)}\n`, "utf8"); + + const blockLines = stderr.split("\n").flatMap((line) => wrapLine(line, 77)); + const timeline: TranscriptLine[] = [ + { text: "codeArbiter hook verification", tone: "muted" }, + { text: "repo feat/proof · arbiter: enabled", tone: "normal" }, + ]; + const frames: Array<{ lines: TranscriptLine[]; duration: number }> = [ + { lines: [...timeline], duration: 1.2 }, + { + lines: [...timeline, { text: "$ git status --short", tone: "command" }, { text: statusBefore, tone: "normal" }], + duration: 1.8, + }, + { + lines: [ + ...timeline, + { text: "$ git status --short", tone: "command" }, + { text: statusBefore, tone: "normal" }, + { text: `$ ${attemptedCommand}`, tone: "command" }, + { text: "→ PreToolUse · plugins/ca/hooks/pre-bash.py", tone: "muted" }, + ], + duration: 1.6, + }, + { + lines: [ + ...timeline, + { text: `$ ${attemptedCommand}`, tone: "command" }, + { text: "→ PreToolUse · plugins/ca/hooks/pre-bash.py", tone: "muted" }, + ...blockLines.map((text) => ({ text, tone: "blocked" as const })), + ], + duration: 3.2, + }, + { + lines: [ + ...timeline, + { text: `$ ${attemptedCommand}`, tone: "command" }, + ...blockLines.map((text) => ({ text, tone: "blocked" as const })), + { text: "exit 2 · tool call denied · command not executed", tone: "blocked" }, + ], + duration: 2.2, + }, + { + lines: [ + ...timeline, + { text: "$ git diff --cached --name-only", tone: "command" }, + { text: stagedAfter || "(no staged files)", tone: "normal" }, + { text: "$ git status --short", tone: "command" }, + ...statusAfter.split("\n").map((text) => ({ text, tone: "normal" as const })), + { text: "✓ note.txt remained unstaged", tone: "success" }, + { text: "✓ H-03 block appended to gate-events.log", tone: "success" }, + ], + duration: 3, + }, + ]; + await makeVideo(timeline.length ? frames : [], capturedAt.slice(0, 10)); + process.stdout.write( + `Captured direct H-03 hook invocation from ${record.source} at ${capturedAt}\n` + + `Source SHA-256: ${sourceSha256}\n` + + `Artifacts: ${relative(siteRoot, outputDir)}\n`, + ); +} + +try { + await main(); +} finally { + const safeTempRoot = resolve(tmpdir()).toLowerCase(); + for (const target of [fixtureRoot, frameRoot]) { + const resolved = resolve(target).toLowerCase(); + if (!resolved.startsWith(`${safeTempRoot}\\`) && !resolved.startsWith(`${safeTempRoot}/`)) { + throw new Error(`Refusing to remove non-temporary capture path: ${target}`); + } + rmSync(target, { recursive: true, force: true }); + } +} diff --git a/site/scripts/gen.ts b/site/scripts/gen.ts index 9fb8e481..03247887 100644 --- a/site/scripts/gen.ts +++ b/site/scripts/gen.ts @@ -7,6 +7,7 @@ import { generate } from "./generator/generate"; import { extractHookGates } from "./generator/extract-hook-gates"; import { renderHooksReference, buildEventMap, type HooksJson } from "./generator/render-hooks-reference"; import { renderChangelog } from "./generator/render-changelog"; +import { renderConfigurationReference } from "./generator/configuration-reference"; const here = dirname(fileURLToPath(import.meta.url)); // site/scripts const repoRoot = resolve(here, "..", ".."); // -> repo root @@ -57,6 +58,10 @@ if (skipped.length > 0) { } } +const configurationOutPath = join(outDir, "configuration.md"); +writeFileSync(configurationOutPath, renderConfigurationReference()); +console.log(`Generated configuration reference -> ${configurationOutPath}`); + // On-site changelog (docs-site-overhaul spec, decision f): the repo-root // CHANGELOG.md is the single source of truth for release history — this emits // a Starlight-ready copy rather than duplicating it by hand. Gitignored output, diff --git a/site/scripts/generator/configuration-reference.ts b/site/scripts/generator/configuration-reference.ts new file mode 100644 index 00000000..61daf9cd --- /dev/null +++ b/site/scripts/generator/configuration-reference.ts @@ -0,0 +1,461 @@ +export type ConfigurationGroup = + | "Core and safety" + | "Statusline" + | "PR watch" + | "Transcript pruning" + | "Farm backend"; + +export interface ConfigurationEntry { + readonly name: string; + readonly group: ConfigurationGroup; + readonly defaultValue: string; + readonly accepted: string; + readonly purpose: string; + readonly caution?: string; +} + +/** + * Operator-facing environment variables only. Test seams, derived internal + * state paths, and host-private transport variables are intentionally omitted. + */ +export const CONFIGURATION_ENTRIES: readonly ConfigurationEntry[] = [ + { + name: "CODEARBITER_DEV", + group: "Core and safety", + defaultValue: "off", + accepted: "`1` to arm", + purpose: "Allows the logged maintainer dev-mode command to suspend orchestration.", + caution: "This does nothing until the dev command is invoked; every entry and exit is audited.", + }, + { + name: "NO_COLOR", + group: "Core and safety", + defaultValue: "unset", + accepted: "any present value", + purpose: "Removes ANSI color from terminal output while preserving content and layout.", + }, + { + name: "CODEARBITER_BASE_BRANCH", + group: "Core and safety", + defaultValue: "`main`", + accepted: "local branch name", + purpose: "Sets the branch used as the SessionStart comparison base when the repository uses a different default.", + caution: "This changes startup reporting only; it does not retarget a pull request or authorize a merge.", + }, + { + name: "CODEARBITER_THEME", + group: "Statusline", + defaultValue: "`violet`", + accepted: "`violet`, `blue`, `green`, `amber`, `mono`, or `custom`", + purpose: "Selects the Claude Code statusline palette.", + }, + { + name: "CODEARBITER_THEME_FILE", + group: "Statusline", + defaultValue: "`~/.codearbiter/statusline-theme.json`", + accepted: "local JSON file path", + purpose: "Overrides the custom-theme file used when `CODEARBITER_THEME=custom`.", + }, + { + name: "CODEARBITER_STATUSLINE", + group: "Statusline", + defaultValue: "on", + accepted: "`off` disables", + purpose: "Disables the Claude Code statusline without uninstalling its settings entry.", + }, + { + name: "CODEARBITER_WIDTH", + group: "Statusline", + defaultValue: "terminal width", + accepted: "integer, clamped to 70–160", + purpose: "Overrides the rendered statusline width; otherwise `COLUMNS` and terminal probes apply.", + }, + { + name: "CODEARBITER_COMPACT", + group: "Statusline", + defaultValue: "off", + accepted: "`1`, `true`, `on`, or `yes`", + purpose: "Uses the lean statusline layout and drops subagent rows.", + }, + { + name: "CODEARBITER_COMPACT_AT", + group: "Statusline", + defaultValue: "`92`", + accepted: "context percentage", + purpose: "Sets the context-usage threshold presented as compaction pressure.", + }, + { + name: "CODEARBITER_BABYSIT", + group: "PR watch", + defaultValue: "`off`", + accepted: "`on`, `true`, or `1`", + purpose: "Auto-attaches the server-side PR watcher after the PR command opens a pull request.", + caution: "It applies only in opted-in repositories and never enables itself or auto-merges.", + }, + { + name: "CODEARBITER_BABYSIT_ONRED", + group: "PR watch", + defaultValue: "`propose`", + accepted: "`propose` or `branch`", + purpose: "Chooses whether a red watcher only diagnoses and proposes, or also creates an unmergeable spike branch.", + caution: "Unknown values fail closed to `propose`; the `branch` result can never be merged or opened as a PR.", + }, + { + name: "CODEARBITER_PRUNE", + group: "Transcript pruning", + defaultValue: "`off`", + accepted: "`off`, `dry`, or `on`", + purpose: "Controls the preview transcript-pruning service; `dry` records candidates without pruning.", + caution: "Enable it yourself only after reviewing dry-run output; codeArbiter never opts you in.", + }, + { + name: "CODEARBITER_PRUNE_TIER", + group: "Transcript pruning", + defaultValue: "`gentle`", + accepted: "documented pruning tier", + purpose: "Selects the pruning strategy preset.", + }, + { + name: "CODEARBITER_PRUNE_STRATEGIES", + group: "Transcript pruning", + defaultValue: "tier defaults", + accepted: "comma-separated strategy names", + purpose: "Overrides the strategy set selected by the pruning tier.", + }, + { + name: "CODEARBITER_PRUNE_KEEP_RECENT", + group: "Transcript pruning", + defaultValue: "`10`", + accepted: "non-negative integer", + purpose: "Keeps this many recent eligible items untouched.", + }, + { + name: "CODEARBITER_PRUNE_MAXBYTES", + group: "Transcript pruning", + defaultValue: "`8192`", + accepted: "positive byte count", + purpose: "Caps replacement sidecar content retained for one pruned item.", + }, + { + name: "CODEARBITER_PRUNE_MIN_SIZE", + group: "Transcript pruning", + defaultValue: "`1048576`", + accepted: "positive byte count", + purpose: "Requires this transcript size before the service considers pruning.", + }, + { + name: "CODEARBITER_PRUNE_MIN_GROWTH", + group: "Transcript pruning", + defaultValue: "`262144`", + accepted: "positive byte count", + purpose: "Requires this much growth since the previous pass before reconsidering the transcript.", + }, + { + name: "CODEARBITER_PRUNE_BACKUPS", + group: "Transcript pruning", + defaultValue: "`3`", + accepted: "non-negative integer", + purpose: "Retains this many rotating transcript backups before older backups are removed.", + }, + { + name: "CODEARBITER_PRUNE_LIVE_SECS", + group: "Transcript pruning", + defaultValue: "`90`", + accepted: "non-negative seconds", + purpose: "Treats a transcript as live for this long after its latest update and avoids compacting it.", + }, + { + name: "CODEARBITER_PRUNE_METRICS", + group: "Transcript pruning", + defaultValue: "`~/.codearbiter/metrics/prune-dry.jsonl`", + accepted: "local JSONL file path", + purpose: "Overrides the append-only destination for dry-run pruning measurements.", + }, + { + name: "CODEARBITER_PRUNE_NUDGE", + group: "Transcript pruning", + defaultValue: "`off`", + accepted: "`on` to arm", + purpose: "Enables the preview cold-miss nudge when pruning is also on.", + }, + { + name: "CODEARBITER_PRUNE_NUDGE_IDLE_SECS", + group: "Transcript pruning", + defaultValue: "`240`", + accepted: "positive seconds", + purpose: "Sets the minimum idle period before a cold-miss nudge can appear.", + }, + { + name: "CODEARBITER_PRUNE_NUDGE_MIN_TOKENS", + group: "Transcript pruning", + defaultValue: "`80000`", + accepted: "positive estimated token count", + purpose: "Sets the minimum model-context size before a cold-miss nudge can appear.", + }, + { + name: "FARM_API_KEY", + group: "Farm backend", + defaultValue: "required for `--farm`", + accepted: "provider API key", + purpose: "Authenticates the preview OpenAI-compatible farm backend.", + caution: "Keep it in the shell environment or the documented local tools `.env`; never commit it.", + }, + { + name: "FARM_API_BASE_URL", + group: "Farm backend", + defaultValue: "`https://opencode.ai/zen/v1`", + accepted: "HTTPS OpenAI-compatible endpoint", + purpose: "Selects the farm provider endpoint.", + }, + { + name: "FARM_MODEL", + group: "Farm backend", + defaultValue: "automatic selection", + accepted: "provider model id", + purpose: "Skips canary and cached model selection and pins one model.", + }, + { + name: "FARM_CANDIDATE_MODELS", + group: "Farm backend", + defaultValue: "unset", + accepted: "comma-separated provider model ids", + purpose: "Supplies the model set for a canary comparison.", + }, + { + name: "FARM_CONCURRENCY", + group: "Farm backend", + defaultValue: "`4`", + accepted: "integer of at least 1", + purpose: "Caps total in-flight worker calls, including best-of-N samples.", + }, + { + name: "FARM_BASE_BRANCH", + group: "Farm backend", + defaultValue: "`main`", + accepted: "local branch name", + purpose: "Sets the branch from which the farm integration branch is created.", + }, + { + name: "FARM_INTEGRATION_BRANCH", + group: "Farm backend", + defaultValue: "`farm/integration`", + accepted: "new or existing local branch name", + purpose: "Names the farm's integration branch.", + caution: "Concurrent farm runs need distinct integration branches.", + }, + { + name: "FARM_SAMPLES", + group: "Farm backend", + defaultValue: "`1`", + accepted: "integer of at least 1", + purpose: "Draws this many isolated candidates per task attempt; the first gate-passing candidate wins.", + caution: "Values above 1 can multiply provider token use even though concurrency remains capped.", + }, + { + name: "FARM_TEMPERATURE", + group: "Farm backend", + defaultValue: "`0`", + accepted: "provider-supported number", + purpose: "Controls worker sampling; when samples exceed 1 and this is unset, the dispatcher uses 0.7.", + }, + { + name: "FARM_MAX_TOKENS", + group: "Farm backend", + defaultValue: "provider default", + accepted: "non-negative integer", + purpose: "Caps completion tokens per worker call; `0` leaves the provider default.", + }, + { + name: "FARM_MAX_RETRIES", + group: "Farm backend", + defaultValue: "`2`", + accepted: "non-negative integer", + purpose: "Limits gate retries for one task before escalation.", + }, + { + name: "FARM_REQUEST_TIMEOUT_MS", + group: "Farm backend", + defaultValue: "`120000`", + accepted: "positive milliseconds", + purpose: "Bounds one provider request, including its response body.", + }, + { + name: "FARM_API_MAX_RETRIES", + group: "Farm backend", + defaultValue: "`3`", + accepted: "non-negative integer", + purpose: "Limits transport retries for rate limits and server errors; provider `Retry-After` is honored.", + }, + { + name: "FARM_ENTITLEMENT_PROBE_TIMEOUT_MS", + group: "Farm backend", + defaultValue: "`35000`", + accepted: "positive milliseconds", + purpose: "Bounds each model-entitlement probe during canary selection.", + }, + { + name: "FARM_GATE_TIMEOUT_MS", + group: "Farm backend", + defaultValue: "`300000`", + accepted: "milliseconds of at least 1000", + purpose: "Bounds each non-git setup, gate, and mutation child command.", + }, + { + name: "FARM_ABORT_ESCALATION_RATE", + group: "Farm backend", + defaultValue: "`0.5`", + accepted: "number from 0 to 1", + purpose: "Trips the dispatch circuit breaker when the settled-task escalation rate exceeds this fraction.", + }, + { + name: "FARM_ABORT_MIN_TASKS", + group: "Farm backend", + defaultValue: "`3`", + accepted: "positive integer", + purpose: "Requires this many settled tasks before the escalation-rate circuit breaker can abort a run.", + }, + { + name: "FARM_ENRICH_MAX_BYTES", + group: "Farm backend", + defaultValue: "`131072`", + accepted: "positive byte count", + purpose: "Caps test and in-scope source context injected into a worker prompt.", + }, + { + name: "FARM_WORKTREE_ROOT", + group: "Farm backend", + defaultValue: "`.farm/worktrees`", + accepted: "repository-contained directory", + purpose: "Sets the scratch worktree root used by farm tasks and samples.", + caution: "An external root is refused unless `FARM_ALLOW_EXTERNAL_WORKTREE_ROOT=1` is set explicitly.", + }, + { + name: "FARM_ALLOW_EXTERNAL_WORKTREE_ROOT", + group: "Farm backend", + defaultValue: "off", + accepted: "`1` to arm", + purpose: "Allows `FARM_WORKTREE_ROOT` to resolve outside the repository.", + caution: "This is a destructive-scope override: farm cleanup recursively removes task worktrees under that root.", + }, + { + name: "FARM_REPORT_DIR", + group: "Farm backend", + defaultValue: "`.farm`", + accepted: "directory path", + purpose: "Sets the root for run-scoped farm receipts and reports.", + }, + { + name: "FARM_RUN_ID", + group: "Farm backend", + defaultValue: "fresh random id", + accepted: "1–64 characters from `A-Z`, `a-z`, `0-9`, `.`, `_`, or `-`", + purpose: "Pins the run receipt directory name for reproducibility.", + caution: "Reusing an id publishes over that run's existing receipt directory; use a fresh id per run.", + }, + { + name: "FARM_MUTATION", + group: "Farm backend", + defaultValue: "`on`", + accepted: "`off` disables", + purpose: "Disables the farm mutation guard for a run.", + }, + { + name: "FARM_MUTATION_SAMPLE", + group: "Farm backend", + defaultValue: "`15`", + accepted: "positive integer", + purpose: "Caps the number of built-in mutation candidates evaluated per task.", + }, + { + name: "FARM_MUTATION_BUDGET_MS", + group: "Farm backend", + defaultValue: "`30000`", + accepted: "non-negative milliseconds", + purpose: "Time-boxes mutation testing for one task.", + }, + { + name: "FARM_MUTATION_WARN_BELOW", + group: "Farm backend", + defaultValue: "`0.5`", + accepted: "number from 0 to 1", + purpose: "Attaches a review warning when the mutation score falls below this value.", + }, + { + name: "FARM_MUTATION_ESCALATE_BELOW", + group: "Farm backend", + defaultValue: "`0.1`", + accepted: "number from 0 to 1", + purpose: "Hard-escalates a sufficiently sampled task at or below this mutation score.", + }, + { + name: "FARM_MUTATION_CMD", + group: "Farm backend", + defaultValue: "built-in text mutator", + accepted: "local executable command", + purpose: "Replaces the built-in mutator with a language-specific mutation framework.", + caution: "The command runs inside the task worktree; verify it independently before trusting its score.", + }, +]; + +const GROUPS: readonly ConfigurationGroup[] = [ + "Core and safety", + "Statusline", + "PR watch", + "Transcript pruning", + "Farm backend", +]; + +function renderEntry(entry: ConfigurationEntry): string { + const caution = entry.caution ? ` ${entry.caution}` : ""; + return `| \`${entry.name}\` | ${entry.defaultValue} | ${entry.accepted} | ${entry.purpose}${caution} |`; +} + +export function renderConfigurationReference( + entries: readonly ConfigurationEntry[] = CONFIGURATION_ENTRIES, +): string { + const sections = GROUPS.map((group) => { + const rows = entries.filter((entry) => entry.group === group).map(renderEntry).join("\n"); + return `## ${group}\n\n| Variable | Default | Accepted values | Effect |\n|---|---|---|---|\n${rows}`; + }).join("\n\n"); + + return `--- +title: Configuration Reference +description: "Operator-facing environment variables for codeArbiter, with defaults, accepted values, effects, and safety boundaries." +--- + +Use environment variables only when the documented default does not fit. Host-native commands +remain the primary interface; these settings tune a feature or arm an explicitly opt-in surface. + +Set a variable in the environment that launches your host, then start a fresh session. On +PowerShell, use \`$env:NAME = "value"\` for the current process. On POSIX shells, use +\`export NAME=value\`. Persist values with your operating system or shell profile only after a +temporary session behaves as expected. + +Do not commit secrets or machine-specific paths. Unknown values generally fall back or fail soft, +but safety-sensitive settings can refuse a run. The tables below cover supported operator-facing +settings; test seams and internal state-path overrides are intentionally excluded. + +This catalog is typed and deterministic, but its defaults and explanations are maintainer-reviewed +rather than extracted from executable code. A contract test requires every documented variable name +to exist in shipped implementation code; follow the linked feature guide and current source when a +default is release-critical. + +${sections} + +## Verify and undo + +After changing a statusline setting, run the statusline status command and trigger a new render. +After changing pruning, run the prune status command and begin with \`dry\`. Farm settings are +recorded in the run report; inspect that receipt before trusting the result. + +To undo a setting, remove it from the launch environment and start a fresh host session. Removing +\`FARM_API_KEY\` makes \`--farm\` stop at preflight; it does not affect the premium path. + +## Related + +- [Set Up the Statusline](/guides/the-statusline/) +- [Transcript pruning command](/reference/commands/prune/) +- [Run an Autonomous Sprint](/guides/autonomous-sprints/) +- [Feature Forge](/feature-forge/overview/) +`; +} diff --git a/site/scripts/generator/generate.ts b/site/scripts/generator/generate.ts index 9ec17a1b..388c19fb 100644 --- a/site/scripts/generator/generate.ts +++ b/site/scripts/generator/generate.ts @@ -10,6 +10,10 @@ import { renderSkillPage } from "./render-skill-page"; import { buildIndex } from "./build-index"; import { getCommandForgeStatus } from "./forge-status"; import { loadCurated } from "./load-curated"; +import { + commandHostAvailability, + loadHostCommandCatalogs, +} from "./host-command-catalog"; import type { GenerateResult, PageInput, @@ -144,6 +148,12 @@ export function generate( : new Map(); const pluginVersion = readPluginVersion(srcDir); + const hostCatalogs = loadHostCommandCatalogs( + srcDir, + parsed + .filter(({ source }) => source.type === "command") + .map(({ source }) => deriveName(source.path, {})), + ); const pages: RenderedPage[] = parsed.map(({ source, doc }, i) => { const name = names[i]; @@ -184,6 +194,10 @@ export function generate( forgeStatus, curated, relatedLinks, + commandHosts: + source.type === "command" + ? commandHostAvailability(slugs[i], hostCatalogs) + : undefined, sourceRaw: source.raw, sourceRelPath: `plugins/ca/${source.path}`, pluginVersion, @@ -227,18 +241,13 @@ export function generate( skill: "| Skill | Description |\n|---|---|", agent: "| Agent | Model tier | Description |\n|---|---|---|", }; - // Host-scope note (docs-codex findings 01/02): every page below is generated - // from the `ca` (Claude Code) plugin payload — its command syntax and its - // 27-agent `Task`-dispatch catalog describe Claude Code specifically. Codex - // uses `ca-codex` (same command names, `$ca-` instead of `/ca:`) - // and runs the same reviewer/author roles inline in the current thread - // rather than dispatching them as isolated subagents. Stated once here, - // generator-side, so it can't drift out of sync across 65+ per-page edits; - // see docs/parity.md's "Role charters" row for the canonical wording. + // The entities come from the Claude Code payload, while each command page + // derives its host availability from all three shipped COMMANDS.md catalogs. + // Agent dispatch remains a Claude-specific catalog boundary. const hostNote = "Commands, skills, and agents below are generated from the `ca` (Claude Code) plugin " + - "payload. Codex CLI (`ca-codex`) uses the same names with `$ca-` in place of " + - "`/ca:`; Pi (`ca-pi`) uses `/ca-`. The Agents catalog below describes Claude " + + "payload. Each command page checks the shipped Claude Code, Codex, and Pi catalogs and marks " + + "adapters where that command is not shipped. The Agents catalog below describes Claude " + "Code's isolated `Task`-tool dispatch — Codex executes the same reviewer and author roles " + "inline in the current thread instead, until packaged agent dispatch lands for that host. " + "See [Compatibility → Host Differences](/getting-started/compatibility/#host-differences) " + @@ -258,7 +267,16 @@ export function generate( return `${heading}\n\n${TABLE_HEADER[group.type]}\n${rows.join("\n")}`; }) .join("\n\n"); - const indexContent = `---\ntitle: Reference\ndescription: Auto-generated reference for codeArbiter commands, skills, and agents.\n---\n\nThis section is generated from the plugin's own frontmatter and regenerates on every build, so it can never drift from the source. See how the three catalogs cooperate in [How a Request Flows](/overview/#how-a-request-flows): a command routes to an owning skill, which may dispatch specialist agents.\n\n${hostNote}\n\n${indexBody}\n`; + const catalogCards = sidebar.map((group) => { + const plural = `${group.type.charAt(0).toUpperCase()}${group.type.slice(1)}s`; + const purpose = group.type === "command" + ? "Public entry points you invoke for an outcome." + : group.type === "skill" + ? "Gated workflows the orchestrator routes into." + : "Focused author and reviewer roles a skill may dispatch."; + return `${group.items.length}${plural}${purpose}`; + }).join("\n"); + const indexContent = `---\ntitle: Reference\ndescription: Source-backed command, skill, and agent catalogs with host syntax, operating context, gates, relationships, and exact shipped source.\n---\n\nThis section is generated from the plugin's own frontmatter and regenerates on every build, so it can never drift from the source. See how the three catalogs cooperate in [How a Request Flows](/overview/#how-a-request-flows): a command routes to an owning skill, which may dispatch specialist agents.\n\n
\n${catalogCards}\n
\n\n
\nUse the catalog from left to right.\n
    \n
  1. Choose the public command that matches the outcome you need.
  2. \n
  3. Follow its owning skill to understand phases, stops, and durable artifacts.
  4. \n
  5. Open an agent only to inspect a dispatched role's tools and constraints; agents are not a second command surface.
  6. \n
\n

Every entity page begins with host-native syntax or dispatch context, then curated operating guidance, gates, related routes, and the exact source used to generate it.

\n
\n\n${hostNote}\n\n${indexBody}\n`; mkdirSync(outDir, { recursive: true }); mkdirSync(dirname(resolvedSidebarPath), { recursive: true }); diff --git a/site/scripts/generator/host-command-catalog.ts b/site/scripts/generator/host-command-catalog.ts new file mode 100644 index 00000000..69ec5181 --- /dev/null +++ b/site/scripts/generator/host-command-catalog.ts @@ -0,0 +1,66 @@ +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; + +export type CommandHost = "claude" | "codex" | "pi"; + +export type CommandHostAvailability = Readonly>; + +const HOST_CATALOGS: Readonly> = { + claude: "ca/COMMANDS.md", + codex: "ca-codex/COMMANDS.md", + pi: "ca-pi/COMMANDS.md", +}; + +const COMMAND_PATTERNS: Readonly> = { + claude: /`\/ca:([a-z0-9-]+)`/g, + codex: /`\$ca-([a-z0-9-]+)`/g, + pi: /`\/ca-([a-z0-9-]+)`/g, +}; + +export function parseHostCommandCatalog( + host: CommandHost, + markdown: string, +): ReadonlySet { + return new Set( + Array.from(markdown.matchAll(COMMAND_PATTERNS[host]), (match) => match[1]), + ); +} + +/** + * Load the three shipped command catalogs adjacent to `plugins/ca`. Synthetic + * generator fixtures do not carry sibling adapters, so they fall back to the + * collected Claude command names and mark the other hosts unavailable. + */ +export function loadHostCommandCatalogs( + claudePluginDir: string, + fallbackClaudeCommands: readonly string[] = [], +): Readonly>> { + const pluginsDir = dirname(claudePluginDir); + const fallback = { + claude: new Set(fallbackClaudeCommands), + codex: new Set(), + pi: new Set(), + }; + + return Object.fromEntries( + (Object.keys(HOST_CATALOGS) as CommandHost[]).map((host) => { + const path = join(pluginsDir, HOST_CATALOGS[host]); + if (!existsSync(path)) return [host, fallback[host]]; + return [ + host, + parseHostCommandCatalog(host, readFileSync(path, "utf8")), + ]; + }), + ) as Record>; +} + +export function commandHostAvailability( + command: string, + catalogs: Readonly>>, +): CommandHostAvailability { + return { + claude: catalogs.claude.has(command), + codex: catalogs.codex.has(command), + pi: catalogs.pi.has(command), + }; +} diff --git a/site/scripts/generator/render-agent-page.ts b/site/scripts/generator/render-agent-page.ts index c491a293..83192333 100644 --- a/site/scripts/generator/render-agent-page.ts +++ b/site/scripts/generator/render-agent-page.ts @@ -5,6 +5,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; +import { renderReferenceLead } from "./render-reference-lead"; /** * Render an agent reference page as Starlight-compatible markdown. @@ -23,13 +24,15 @@ export function renderAgentPage(input: PageInput): string { const description = input.description ?? ""; const descriptionLine = yamlDescriptionLine(description); const frontMatterFields = descriptionLine - ? `title: ${input.name}\n${descriptionLine}` - : `title: ${input.name}`; + ? `title: ${input.name} agent\n${descriptionLine}` + : `title: ${input.name} agent`; const frontMatter = `---\n${frontMatterFields}\n---`; const modelLine = `- **Model tier:** ${modelTier(input.model)}`; const toolsLine = `- **Tools:** ${formatToolsList(input.tools)}`; - const sections: string[] = [`${description}\n\n${modelLine}\n${toolsLine}`]; + const sections: string[] = [ + `${renderReferenceLead("agent", input.name, description)}\n\n${modelLine}\n${toolsLine}`, + ]; if (curated?.body) { sections.push(curated.body.trim()); diff --git a/site/scripts/generator/render-command-page.ts b/site/scripts/generator/render-command-page.ts index a1bc331c..b4101f74 100644 --- a/site/scripts/generator/render-command-page.ts +++ b/site/scripts/generator/render-command-page.ts @@ -4,6 +4,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; +import { renderReferenceLead } from "./render-reference-lead"; /** * Render a command reference page as Starlight-compatible markdown. @@ -37,10 +38,12 @@ export function renderCommandPage(input: PageInput): string { const descriptionLine = yamlDescriptionLine(desc); const frontMatter = descriptionLine - ? `title: ${name}\n${descriptionLine}` - : `title: ${name}`; + ? `title: ${name} command\n${descriptionLine}` + : `title: ${name} command`; - const sections: string[] = [`${decoration}${desc}`.trimEnd()]; + const sections: string[] = [ + `${decoration}${renderReferenceLead("command", name, desc, input.commandHosts)}`.trimEnd(), + ]; if (curated?.body) { sections.push(curated.body.trim()); diff --git a/site/scripts/generator/render-hooks-reference.ts b/site/scripts/generator/render-hooks-reference.ts index d7a761d1..4c58a2c1 100644 --- a/site/scripts/generator/render-hooks-reference.ts +++ b/site/scripts/generator/render-hooks-reference.ts @@ -7,6 +7,7 @@ * permalinks, never pasted Python. */ import type { HookCallSite } from "./extract-hook-gates"; +import { repositorySourceRef } from "./source-ref"; /** Parsed shape of `plugins/ca/hooks/hooks.json` (only the fields this module reads). */ export interface HooksJson { @@ -23,6 +24,9 @@ export interface HooksJson { * directly, not registered in `hooks.json`, so it gets a hand-mapped label. */ const GIT_BACKSTOP_FILE = "git-enforce.py"; const GIT_BACKSTOP_LABEL = "git backstop"; +const DELEGATED_EVENT_OWNER = new Map([ + ["_bashguardlib.py", "pre-bash.py"], +]); /** * Build a script-basename -> event-label list map from a parsed `hooks.json`. @@ -56,7 +60,8 @@ export function buildEventMap(hooksJson: HooksJson): Map { /** Events label list for one hook source file — hand-mapped for the git backstop. */ export function eventsFor(file: string, eventMap: Map): string[] { if (file === GIT_BACKSTOP_FILE) return [GIT_BACKSTOP_LABEL]; - return eventMap.get(file) ?? []; + const owner = DELEGATED_EVENT_OWNER.get(file) ?? file; + return eventMap.get(owner) ?? []; } /** Parse a gate tag into (numeric part, letter suffix) for natural sort: @@ -102,6 +107,7 @@ export function renderHooksReference( pluginVersion: string, ): string { const tag = `v${pluginVersion}`; + const sourceRef = repositorySourceRef(); const byTag = new Map(); for (const site of callSites) { const list = byTag.get(site.tag) ?? []; @@ -125,7 +131,7 @@ export function renderHooksReference( const entries = sites .map((site) => { - const permalink = `https://github.com/arbiterForge/codeArbiter/blob/${tag}/plugins/ca/hooks/${site.file}#L${site.line}`; + const permalink = `https://github.com/arbiterForge/codeArbiter/blob/${sourceRef}/plugins/ca/hooks/${site.file}#L${site.line}`; return `${toBlockquote(site.message)}\n>\n> — [\`${site.file}:${site.line}\`](${permalink})`; }) .join("\n\n"); diff --git a/site/scripts/generator/render-reference-lead.ts b/site/scripts/generator/render-reference-lead.ts new file mode 100644 index 00000000..d524697e --- /dev/null +++ b/site/scripts/generator/render-reference-lead.ts @@ -0,0 +1,90 @@ +import type { CommandHost, CommandHostAvailability } from "./host-command-catalog"; + +export type ReferenceKind = "command" | "skill" | "agent"; + +function escapeHtml(value: string): string { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """); +} + +const HOST_LABEL: Readonly> = { + claude: "Claude Code", + codex: "Codex", + pi: "Pi", +}; + +function syntaxFor(host: CommandHost, name: string): string { + if (host === "claude") return `/ca:${name}`; + if (host === "codex") return `$ca-${name}`; + return `/ca-${name}`; +} + +function usageFor( + kind: ReferenceKind, + name: string, + commandHosts?: CommandHostAvailability, +): string { + + if (kind === "command") { + const availability = commandHosts ?? { + claude: true, + codex: false, + pi: false, + }; + const rows = (Object.keys(HOST_LABEL) as CommandHost[]).map((host) => { + const value = availability[host] + ? `${escapeHtml(syntaxFor(host, name))}` + : 'Not shipped'; + return `
${HOST_LABEL[host]}${value}
`; + }); + return [ + 'Invoke it', + '
', + ...rows, + "
", + "

Availability is read from each shipped host catalog. Preview-only commands are marked on the page.

", + ].join("\n"); + } + + if (kind === "skill") { + return [ + 'How it is used', + "

The orchestrator routes to this skill from an owning command. It is not a direct user entry point.

", + ].join("\n"); + } + + return [ + 'How it is used', + "

An owning skill dispatches this specialist. You invoke the command that owns the lane, not the agent directly.

", + ].join("\n"); +} + +/** + * Render the collection-specific orientation shared by all generated reference + * pages. It makes the command/skill/agent distinction visible before the + * curated detail while keeping the exact source embed further down the page. + */ +export function renderReferenceLead( + kind: ReferenceKind, + name: string, + description: string, + commandHosts?: CommandHostAvailability, +): string { + const label = kind === "command" ? "Command" : kind === "skill" ? "Routed skill" : "Specialist agent"; + const summary = escapeHtml(description); + + return [ + `
`, + '
', + `${label}`, + `

${summary}

`, + "
", + '
', + usageFor(kind, name, commandHosts), + "
", + "
", + ].join("\n"); +} diff --git a/site/scripts/generator/render-skill-page.ts b/site/scripts/generator/render-skill-page.ts index 7f73fe2f..c4736e8c 100644 --- a/site/scripts/generator/render-skill-page.ts +++ b/site/scripts/generator/render-skill-page.ts @@ -3,6 +3,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; +import { renderReferenceLead } from "./render-reference-lead"; /** * Render a skill reference page as Starlight-compatible markdown. @@ -20,10 +21,12 @@ export function renderSkillPage(input: PageInput): string { const description = input.description ?? ""; const descriptionLine = yamlDescriptionLine(description); const frontMatterFields = descriptionLine - ? `title: ${input.name}\n${descriptionLine}` - : `title: ${input.name}`; + ? `title: ${input.name} skill\n${descriptionLine}` + : `title: ${input.name} skill`; - const sections: string[] = [description]; + const sections: string[] = [ + renderReferenceLead("skill", input.name, description), + ]; if (curated?.body) { sections.push(curated.body.trim()); diff --git a/site/scripts/generator/render-source-embed.ts b/site/scripts/generator/render-source-embed.ts index 8a1ee92b..85c81179 100644 --- a/site/scripts/generator/render-source-embed.ts +++ b/site/scripts/generator/render-source-embed.ts @@ -1,3 +1,5 @@ +import { repositorySourceRef } from "./source-ref"; + /** render-source-embed.ts — codeArbiter's verbatim source-embed renderer. * * The "source-visible" half of every reference page: a collapsible `
` @@ -39,9 +41,10 @@ export function renderSourceEmbed( ): string { const fence = chooseFence(sourceRaw); const tag = `v${pluginVersion}`; - const repoUrl = `https://github.com/arbiterForge/codeArbiter/blob/${tag}/${sourceRelPath}`; + const sourceRef = repositorySourceRef(); + const repoUrl = `https://github.com/arbiterForge/codeArbiter/blob/${sourceRef}/${sourceRelPath}`; - return `
+ return `
Source — ${sourceRelPath} (${tag}) ${fence}md diff --git a/site/scripts/generator/source-ref.ts b/site/scripts/generator/source-ref.ts new file mode 100644 index 00000000..f84ca971 --- /dev/null +++ b/site/scripts/generator/source-ref.ts @@ -0,0 +1,13 @@ +/** + * Resolve a GitHub blob ref that exists when the documentation is published. + * + * GitHub Actions provides the exact commit SHA for production builds. Local + * builds use `main`, which remains navigable while a release version is staged + * but its tag has not been cut yet. + */ +export function repositorySourceRef( + env: Record = process.env, +): string { + const sha = env.GITHUB_SHA?.trim(); + return sha && /^[0-9a-f]{40}$/i.test(sha) ? sha : "main"; +} diff --git a/site/scripts/generator/truncate-description.ts b/site/scripts/generator/truncate-description.ts index 4954bbdc..9f33c6f2 100644 --- a/site/scripts/generator/truncate-description.ts +++ b/site/scripts/generator/truncate-description.ts @@ -1,13 +1,27 @@ /** * Truncate a description to its first sentence for a compact table cell. * - * Cuts at the first ". " (period + space) and keeps the leading period, so a - * multi-sentence frontmatter `description` collapses to one line in the - * roster tables. A description with no ". " (already one sentence, or ending - * in a bare period with nothing after it) is returned unchanged. + * Cuts at the first sentence-ending period while skipping common abbreviations + * such as "vs.", "e.g.", and "i.e.". This avoids visibly broken roster text + * like "persona vs." while keeping the index compact. */ export function truncateDescription(text: string): string { - const idx = text.indexOf(". "); - if (idx === -1) return text; - return text.slice(0, idx + 1); + const abbreviations = new Set([ + "vs.", + "e.g.", + "i.e.", + "etc.", + "mr.", + "mrs.", + "ms.", + "dr.", + ]); + + for (let idx = text.indexOf(". "); idx !== -1; idx = text.indexOf(". ", idx + 2)) { + const prefix = text.slice(0, idx + 1); + const token = prefix.match(/(?:^|\s)(\S+)$/)?.[1]?.toLowerCase() ?? ""; + if (!abbreviations.has(token)) return prefix; + } + + return text; } diff --git a/site/scripts/generator/types.ts b/site/scripts/generator/types.ts index 0d216176..8c380683 100644 --- a/site/scripts/generator/types.ts +++ b/site/scripts/generator/types.ts @@ -1,6 +1,7 @@ // Shared type contract for the codeArbiter reference generator. // Authored as the harness API; individual modules implement against it. import type { ForgeStatus } from "./forge-status"; +import type { CommandHostAvailability } from "./host-command-catalog"; /** The three kinds of plugin source documents the generator reads. */ export type SourceType = "command" | "skill" | "agent"; @@ -67,6 +68,8 @@ export interface PageInput { curated?: CuratedDoc; /** Resolved `related:` links, or undefined when there are none. */ relatedLinks?: RelatedLink[]; + /** Command availability derived from each shipped host adapter's catalog. */ + commandHosts?: CommandHostAvailability; /** Verbatim raw contents of the plugin source file, for the source embed. */ sourceRaw: string; /** Repo-relative path to the plugin source file, e.g. `plugins/ca/commands/sprint.md`. */ diff --git a/site/src/assets/fonts/LICENSE-JetBrains-Mono.txt b/site/src/assets/fonts/LICENSE-JetBrains-Mono.txt new file mode 100644 index 00000000..8f7ed671 --- /dev/null +++ b/site/src/assets/fonts/LICENSE-JetBrains-Mono.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) JetBrainsMono-Italic[wght].ttf: Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/site/src/assets/fonts/LICENSE-Manrope.txt b/site/src/assets/fonts/LICENSE-Manrope.txt new file mode 100644 index 00000000..462280f3 --- /dev/null +++ b/site/src/assets/fonts/LICENSE-Manrope.txt @@ -0,0 +1,93 @@ +Copyright 2019 The Manrope Project Authors (https://github.com/sharanda/manrope) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/site/src/assets/fonts/jetbrains-mono-latin-wght-normal.woff2 b/site/src/assets/fonts/jetbrains-mono-latin-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cd5102a44c763f1cfe9f2b199fd5d7257491c0dd GIT binary patch literal 40404 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 literal 0 HcmV?d00001 diff --git a/site/src/assets/hero-gates.webp b/site/src/assets/hero-gates.webp new file mode 100644 index 0000000000000000000000000000000000000000..3c5d3cba36503d62fcd405eae0c746875e8e3b00 GIT binary patch literal 69580 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%0Dd0M~jkBtcMCK_O%l8*_BT>4wg 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 literal 0 HcmV?d00001 diff --git a/site/src/assets/proof/hook-proof.json b/site/src/assets/proof/hook-proof.json new file mode 100644 index 00000000..87a8b692 --- /dev/null +++ b/site/src/assets/proof/hook-proof.json @@ -0,0 +1,25 @@ +{ + "schema": 1, + "evidenceKind": "direct-hook-invocation-rendered-replay", + "hostDiscoveryProven": false, + "capturedAt": "2026-07-30T13:34:06.903Z", + "source": "plugins/ca/hooks/pre-bash.py", + "sourceSha256": "b2cf1a875a6ec0aa4c36e59bfdc2c9f64a81a70dcc901a5bbc46465b087e4873", + "fixture": { + "branch": "feat/proof", + "activation": "arbiter: enabled", + "untrackedBefore": "?? note.txt", + "statusAfter": " M .codearbiter/gate-events.log\n?? note.txt", + "stagedBefore": "", + "stagedAfter": "", + "gateEvent": "[2026-07-30T13:34:06Z] BLOCK [H-03] host=claude hook=pre-bash.py | 'git add -A' / 'git add .' / 'git add --all' / 'git add -u' are prohibited. Stage files explicitly (commit-gate skill)." + }, + "invocation": { + "attemptedCommand": "git add -A", + "hookEvent": "PreToolUse", + "toolName": "Bash", + "exitCode": 2, + "stderr": "BLOCKED [H-03]: 'git add -A' / 'git add .' / 'git add --all' / 'git add -u' are prohibited. Stage files explicitly (commit-gate skill).", + "commandExecuted": false + } +} diff --git a/site/src/assets/proof/hook-proof.mp4 b/site/src/assets/proof/hook-proof.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ef0d985e9b8e1364de958e6078f2b6928cac1bce GIT binary patch literal 115063 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[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 literal 0 HcmV?d00001 diff --git a/site/src/assets/proof/hook-proof.webm b/site/src/assets/proof/hook-proof.webm new file mode 100644 index 0000000000000000000000000000000000000000..ca82b995420f1f243ce7a0ab93f98140973b3aeb GIT binary patch literal 116387 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

6Bw{|z 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 = { + start: + '', + workflow: + '', + operate: + '', + understand: + '', + reference: + '', + forge: + '', + gate: + '', + repository: + '', + audit: + '', + feature: + '', + fix: + '', + sprint: + '', + dependency: + '', + decision: + '', +}; +const a11yAttrs = label ? { role: "img", "aria-label": label } : { "aria-hidden": "true" }; +--- + + + + diff --git a/site/src/components/GateCatchTerminal.astro b/site/src/components/GateCatchTerminal.astro deleted file mode 100644 index a53b6c45..00000000 --- a/site/src/components/GateCatchTerminal.astro +++ /dev/null @@ -1,84 +0,0 @@ ---- -/** - * GateCatchTerminal.astro — codeArbiter's conversion centerpiece. - * - * Renders an animated gate-catch demo: a commit blocked by a CRITICAL finding, - * then fixed, then green. Animation is CSS-only (no JS player). The transcript - * is real DOM text for screen readers. Under prefers-reduced-motion the - * animation collapses to static and the full transcript is immediately visible. - * - * Design-system contract (from theme.css): - * .ca-terminal, .ca-terminal__titlebar, .ca-terminal__dot, .ca-terminal__title, - * .ca-terminal__body, .ca-terminal__line, .ca-terminal__line--error, - * .ca-terminal__line--pass, .ca-terminal__line--dim, .ca-terminal__prompt - */ ---- - -

diff --git a/site/src/components/HookProof.astro b/site/src/components/HookProof.astro new file mode 100644 index 00000000..7ea6b03c --- /dev/null +++ b/site/src/components/HookProof.astro @@ -0,0 +1,117 @@ +--- +import proofMp4 from "../assets/proof/hook-proof.mp4"; +import proofWebm from "../assets/proof/hook-proof.webm"; +import proofPoster from "../assets/proof/hook-proof-poster.webp"; +import proof from "../assets/proof/hook-proof.json"; + +const capturedDate = new Intl.DateTimeFormat("en", { + year: "numeric", + month: "short", + day: "numeric", + timeZone: "UTC", +}).format(new Date(proof.capturedAt)); +const shortDigest = proof.sourceSha256.slice(0, 12); +--- + +
+ +
+ Verified direct hook invocation · {capturedDate} + {shortDigest} source digest +
+
+ Read the exact invocation result +
+

Attempted tool call {proof.invocation.attemptedCommand}

+
{proof.invocation.stderr}
+

Exit {proof.invocation.exitCode} · command not executed · no staged files

+

Audit side effect The block appended an H-03 event to .codearbiter/gate-events.log.

+

Capture source {proof.source} at SHA-256 {proof.sourceSha256}.

+

Evidence boundary This directly invoked the shipped hook with the host payload shape. It does not prove a host discovered or registered that hook.

+
+
+
+ + diff --git a/site/src/components/InstallTerminal.astro b/site/src/components/InstallTerminal.astro deleted file mode 100644 index 01ec0b54..00000000 --- a/site/src/components/InstallTerminal.astro +++ /dev/null @@ -1,96 +0,0 @@ ---- -/** - * InstallTerminal.astro — the "get it running" companion to GateCatchTerminal. - * - * Renders an animated transcript of the commands a new user runs in Claude Code: - * the two install commands, then /ca:statusline to wire the bar. The rendered - * statusline itself is shown below the terminal (in index.mdx), using the - * canonical statusline.png single-source asset. - * - * Same design-system contract and animation grammar as GateCatchTerminal: - * CSS-only fade-in per line (--ca-term-delay), real DOM text for screen readers, - * and a static, fully-visible transcript under prefers-reduced-motion. - * - * Design-system contract (from theme.css): - * .ca-terminal, .ca-terminal__titlebar, .ca-terminal__dot, .ca-terminal__title, - * .ca-terminal__body, .ca-terminal__line, .ca-terminal__line--pass, - * .ca-terminal__line--dim, .ca-terminal__prompt - */ ---- - -
- - -
    -
  1. - Claude Code -
  2. - -
  3. - - /plugin marketplace add arbiterForge/codeArbiter -
  4. -
  5. - Marketplace added: codearbiter. -
  6. - - -
  7. - - /plugin install ca@codearbiter -
  8. -
  9. - Installing ca@codearbiter ... -
  10. -
  11. - Plugin installed. Hooks, commands, and agents loaded. -
  12. -
  13. - All commands resolve under the /ca: namespace. -
  14. - - -
  15. - - /ca:statusline -
  16. -
  17. - Wiring the statusline into ~/.claude/settings.json ... -
  18. -
  19. - Statusline wired. It renders on every session. -
  20. - -
  21. - Codex (public marketplace) -
  22. -
  23. - - codex plugin marketplace add arbiterForge/codeArbiter -
  24. -
  25. - - codex plugin add ca-codex@codearbiter -
  26. -
  27. - Review the trusted hook set in /hooks, then start a fresh thread. -
  28. -
  29. - - $ca-doctor -
  30. -
-
diff --git a/site/src/components/PageContext.astro b/site/src/components/PageContext.astro new file mode 100644 index 00000000..cfc75d4a --- /dev/null +++ b/site/src/components/PageContext.astro @@ -0,0 +1,47 @@ +--- +import ArbiterIcon, { type ArbiterIconName } from "./ArbiterIcon.astro"; + +interface Props { + level: "Foundation" | "Practitioner" | "Power user" | "Reference" | "Labs"; + time: string; + outcome: string; + prerequisites?: string[]; + proof?: string; +} + +const { level, time, outcome, prerequisites = [], proof } = Astro.props; +const iconByLevel: Record = { + Foundation: "start", + Practitioner: "workflow", + "Power user": "understand", + Reference: "reference", + Labs: "forge", +}; +--- + + diff --git a/site/src/components/PageTitle.astro b/site/src/components/PageTitle.astro index 4084be32..82fe034a 100644 --- a/site/src/components/PageTitle.astro +++ b/site/src/components/PageTitle.astro @@ -6,9 +6,30 @@

, so Starlight's default page-title

would be a duplicate. Every other page falls through to the stock PageTitle unchanged. */ import Default from '@astrojs/starlight/components/PageTitle.astro'; +import PageContext from './PageContext.astro'; -const { id } = Astro.locals.starlightRoute; +const { id, entry } = Astro.locals.starlightRoute; const isHome = id === '' || id === 'index'; +const explicitJourney = entry.data.journey; +const inferred = id.startsWith('reference/') || id === 'reference' || id === 'hooks' || id === 'glossary' + ? { level: 'Reference' as const, time: 'Lookup', outcome: entry.data.description ?? `the source-backed details for ${entry.data.title}` } + : id.startsWith('getting-started/') + ? { level: 'Foundation' as const, time: '10–15 min', outcome: entry.data.description ?? `a working understanding of ${entry.data.title}` } + : id.startsWith('guides/') + ? { level: 'Practitioner' as const, time: '10–20 min', outcome: entry.data.description ?? `a repeatable ${entry.data.title} workflow` } + : id.startsWith('concepts/') || id === 'concepts' || id === 'enforcement' + ? { level: 'Power user' as const, time: '8–12 min', outcome: entry.data.description ?? `the operating model behind ${entry.data.title}` } + : id.startsWith('feature-forge/') + ? { level: 'Labs' as const, time: '5–10 min', outcome: entry.data.description ?? `a safe way to evaluate ${entry.data.title}` } + : { level: 'Foundation' as const, time: '5–10 min', outcome: entry.data.description ?? `a clear understanding of ${entry.data.title}` }; +const journey = explicitJourney ?? inferred; --- -{!isHome && } +{ + !isHome && ( + <> + + + + ) +} diff --git a/site/src/components/Sidebar.astro b/site/src/components/Sidebar.astro new file mode 100644 index 00000000..bb8e851b --- /dev/null +++ b/site/src/components/Sidebar.astro @@ -0,0 +1,15 @@ +--- +import MobileMenuFooter from "virtual:starlight/components/MobileMenuFooter"; +import SidebarPersister from "@astrojs/starlight/components/SidebarPersister.astro"; +import SidebarSublist from "./SidebarSublist.astro"; + +const { sidebar } = Astro.locals.starlightRoute; +--- + + + + + +
+ +
diff --git a/site/src/components/SidebarSublist.astro b/site/src/components/SidebarSublist.astro new file mode 100644 index 00000000..fc549daa --- /dev/null +++ b/site/src/components/SidebarSublist.astro @@ -0,0 +1,199 @@ +--- +import { Badge, Icon } from "@astrojs/starlight/components"; +import SidebarRestorePoint from "@astrojs/starlight/components/SidebarRestorePoint.astro"; +import ArbiterIcon, { type ArbiterIconName } from "./ArbiterIcon.astro"; + +type SidebarBadge = { + variant?: "note" | "tip" | "caution" | "danger" | "success" | "default"; + class?: string; + text: string; +}; +type SidebarLink = { + type: "link"; + label: string; + href: string; + isCurrent: boolean; + badge?: SidebarBadge; + attrs: Record; +}; +type SidebarGroup = { + type: "group"; + label: string; + entries: SidebarEntry[]; + collapsed: boolean; + badge?: SidebarBadge; +}; +type SidebarEntry = SidebarLink | SidebarGroup; + +interface Props { + sublist: SidebarEntry[]; + nested?: boolean; +} + +const { sublist, nested } = Astro.props; +const groupIcons: Record = { + Start: "start", + Workflows: "workflow", + Operate: "operate", + Understand: "understand", + Reference: "reference", + Labs: "forge", +}; +const hasCurrentLink = (entries: SidebarEntry[]): boolean => + entries.some((entry) => entry.type === "link" ? entry.isCurrent : hasCurrentLink(entry.entries)); +--- + +
+ + diff --git a/site/src/content.config.ts b/site/src/content.config.ts index 7fbcf2c3..f64ea7ae 100644 --- a/site/src/content.config.ts +++ b/site/src/content.config.ts @@ -1,7 +1,21 @@ import { defineCollection } from "astro:content"; import { docsLoader } from "@astrojs/starlight/loaders"; import { docsSchema } from "@astrojs/starlight/schema"; +import { z } from "astro/zod"; export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), + docs: defineCollection({ + loader: docsLoader(), + schema: docsSchema({ + extend: z.object({ + journey: z.object({ + level: z.enum(["Foundation", "Practitioner", "Power user", "Reference", "Labs"]), + time: z.string(), + outcome: z.string(), + prerequisites: z.array(z.string()).optional(), + proof: z.string().optional(), + }).optional(), + }), + }), + }), }; diff --git a/site/src/content/docs/codearbiter-directory.md b/site/src/content/docs/codearbiter-directory.md index 979a25e9..1f351417 100644 --- a/site/src/content/docs/codearbiter-directory.md +++ b/site/src/content/docs/codearbiter-directory.md @@ -1,6 +1,13 @@ --- title: "The .codearbiter/ Directory Reference" description: "Every file and directory under .codearbiter/, what writes it, what reads it, and what happens if it's deleted." +journey: + level: "Power user" + time: "12 minutes" + outcome: "Locate the authoritative project-state artifact for a task, decision, audit, or recovery." + prerequisites: + - "An initialized repository" + proof: "You can answer where a spec, plan, decision, gate event, checkpoint, and audit packet live." --- `.codearbiter/` is codeArbiter's project-state store: a root-level directory, outside `.claude/`, @@ -14,30 +21,32 @@ this directory is where to look. Enforcement itself is gated on one file in here: `CONTEXT.md`. Everything else described below only matters once that file carries `arbiter: enabled`. -This page documents every file and directory a stock install produces or reads. Some appear only -once the feature that uses them first runs (`.markers/`, `.provenance/`, `spikes/`, `reports/`); -those are noted below. +This page documents every stock path the scaffold or a governed lane produces or reads. Some appear +only while a flow is active or after the owning feature first runs (`.decompose-draft/`, +`.markers/`, `.provenance/`, `spikes/`, `reports/`); those lifecycles are noted below. ## At a glance | Path | Written by | Read by | Editable by hand? | |---|---|---|---| | `CONTEXT.md` | `/ca:init`, `/ca:decompose`, `/ca:create-context` | every enforcement hook (`arbiter_active()`), the orchestrator | Yes, but the frontmatter is guarded (see below) | +| `code-map.md` | `/ca:create-context`, `/ca:decompose`, commit-gate provenance heal | feature/task orientation, context checks | Yes; refresh source-backed claims through `/ca:context-check` | | `open-tasks.md` | `/ca:task` only | `/ca:status`, the statusline, `SessionStart` | No: guarded to `/ca:task` | | `open-questions.md` | the orchestrator, when a `[CONFIRM-NN]` is raised | `/ca:status`, `SessionStart`, the statusline | Yes | | `decisions/*.md`, `decision-log.md` | `/ca:adr` only | `/ca:adr-status`, `/ca:reconcile`, `post-write-edit.py` (H-12) | No: guarded to `/ca:adr` | | `specs/*.md` | `brainstorming` skill (via `/ca:feature`, `/ca:sprint`) | `writing-plans`, `/ca:status` | Yes | | `plans/*.md` | `writing-plans` skill | `executing-plans`, `subagent-driven-development`, `/ca:status` | Yes | +| `.decompose-draft/` | `/ca:decompose` while an interview is in progress | `/ca:decompose` on resume | No need: temporary resumable interview state | | `checkpoints/*.md` | `checkpoint-aggregator` agent (`/ca:checkpoint`) | `/ca:audit`, `/ca:status`-adjacent reads | Yes | | `audits/*.md` | `/ca:audit` | humans (report only) | Yes | -| `reports//` | `/ca:tribunal` | `/ca:tribunal` on resume, the filing gate | Yes, but not while a run is in flight | +| `reports/` | `/ca:tribunal` and named handoff/evidence workflows | the owning workflow on resume, humans | Yes, but not while an owning run is in flight | | `sprint-log.md` | `/ca:sprint` | `/ca:status`, `/ca:audit` | No: append-only, guarded (H-05) | | `overrides.log` | `/ca:override`, `/ca:dev` entry/exit | statusline, `/ca:status`, `/ca:audit`, staleness-warn | No: append-only, guarded (H-05) | | `triage.log` | `/ca:feature` small-lane triage | `/ca:metrics`, `/ca:audit` | No: append-only, guarded (H-05) | | `gate-events.log` | every `block()`/`remind()`/`warn()` call in the hooks | (durable sink; no reader ships yet) | No: append-only, guarded (H-05) | | `.markers/` | `security-pass.py`, `migration-pass.py`, `/ca:dev`, `/ca:adr` | the commit-gate hooks (`pre-bash.py`, `pre-write.py`, `pre-edit.py`) | No: guarded, and hand-writing one is friction and an audit trail, not a proof (see below) | | `.provenance/*.json` | `context-creation`, `decompose`, `context-check` (re-scout/re-baseline) | `SessionStart` (drift line), `commit-gate` (auto-heal) | Not by hand: regenerate via `/ca:context-check` | -| `security-controls.md`, `tech-stack.md`, `coding-standards.md` | `/ca:init` / `/ca:create-context` / `/ca:decompose`, kept current by hand or `/ca:context-check` | every reviewer agent, the crypto/secret gates | Yes | +| `security-controls.md`, `tech-stack.md`, `coding-standards.md` | `/ca:create-context` or `/ca:decompose`, kept current by hand or `/ca:context-check` | every reviewer agent, the crypto/secret gates | Yes | | `last-checkpoint` | `checkpoint-aggregator` agent | `/ca:status`, the statusline (overrides-since-checkpoint) | Not normally: it's a counter, not a note | | `spikes/*.md` | `/ca:spike` | humans, `/ca:feature` (seeds `brainstorming`) | Yes | @@ -80,6 +89,20 @@ issue #159). The file that turns every gate off can't itself be edited past the enforcement, nothing loads). The rest of `.codearbiter/` is untouched on disk; recreating `CONTEXT.md` (or running `/ca:init` again) reactivates it against whatever state remains. +## code-map.md + +A source-backed orientation map from repository concerns to paths and roles. Context creation or +decomposition writes it after inspecting the codebase; feature and planning lanes read it when +present so task discovery begins from known seams instead of a whole-tree search. + +The map is not an access-control list. Its claims carry provenance and can become stale when source +paths move. `/ca:context-check` reports that drift and offers re-scout, re-baseline, or defer. + +**Writers:** `/ca:create-context`, `/ca:decompose`, and provenance auto-heal. +**Readers:** feature/task planning and context-check flows. +**Editable by hand?** Yes, but source claims should be re-proven. +**Delete it:** governed lanes fall back to repository discovery; enforcement remains active. + ## open-tasks.md The [board](/glossary/#board): one top-level `- ` bullet per task, in one of three states: @@ -99,7 +122,7 @@ it (statusline, `/ca:status`) reports zero until tasks are re-added. The record of unresolved `[CONFIRM-NN]` items: numbered placeholders for a question only the user can answer, per the terminology lock in `ORCHESTRATOR.md` §0.1. An open `CONFIRM-NN` blocks -stage promotion until it's resolved; it is never guessed at or resolved inside an ADR. The +the dependent work until it is resolved; it is never guessed at or resolved inside an ADR. The `SessionStart` hook and the statusline both count occurrences here. **Writers:** the orchestrator, when a skill surfaces a genuine unknown. **Readers:** `/ca:status`, @@ -148,6 +171,20 @@ the ACCEPTED-vs-total count for an in-progress plan. plan from tasks already marked accepted. **Delete it:** an in-progress feature loses its resumption point; `/ca:status` can no longer report that pipeline's progress. +## .decompose-draft/ + +Temporary, resumable state for a greenfield decomposition interview. Each completed layer is +persisted here before the final project documents are assembled, so a closed session can continue +without repeating accepted answers. + +The directory is working state, not a second project context. A successful decomposition consumes +the draft into the normal scaffold and removes the temporary directory. + +**Writers/readers:** `/ca:decompose` only. +**Editable by hand?** No useful reason; resume through the command. +**Delete it:** an unfinished interview loses its saved progress and must restart. A completed +scaffold is unaffected because the directory should already be gone. + ## checkpoints/ One dated report per `/ca:checkpoint` sweep (`YYYY-MM-DD.md`), written by the @@ -174,18 +211,18 @@ presence, since `/ca:audit` re-derives everything from the underlying logs and f ## reports/ -`/ca:tribunal`'s working directory: `.codearbiter/reports//`, where `` is -`-`. A fresh run creates the directory and opens `run.jsonl`; a resumed run -reuses the same `run-id` (the date is the run's creation date and never changes on resume). Every -lens's findings are written one file per finding, plus append-only triage/run logs, so an -interrupted deep audit picks back up from disk instead of restarting. Tribunal writes are confined -to this directory until the filing gate. It never edits, refactors, or commits project code. +Durable evidence and handoff material owned by a named workflow. Tribunal uses +`.codearbiter/reports//`, where `` is `-`. A fresh run opens +`run.jsonl`; a resumed run reuses the same ID. Findings are written one file at a time with +append-only triage/run logs so an interruption resumes from disk. + +Other governed campaigns may write a clearly named report or handoff directory here. The owning +spec or plan defines its schema; the directory is not an unstructured dumping ground. -**Writers:** `/ca:tribunal` only. **Readers:** `/ca:tribunal` itself, on resume, and the filing -gate that turns approved findings into GitHub issues. **Editable by hand?** Not while a run is -in flight: you'd desync the resumable state. **Delete it:** an in-flight tribunal run can no -longer resume and restarts from scratch on next invocation; completed runs' local record is gone -(filed GitHub issues, if any, are unaffected: they live on GitHub, not here). +**Writers:** the owning governed workflow. **Readers:** that workflow on resume and humans reviewing +its evidence. **Editable by hand?** Not while an owning run is active. +**Delete it:** an in-flight workflow may lose its resumption record; completed evidence disappears. +Filed GitHub issues, if any, are unaffected. ## sprint-log.md @@ -300,8 +337,10 @@ re-baselined and a fresh provenance file is written. ## Other scaffold docs -`security-controls.md`, `tech-stack.md`, and `coding-standards.md` round out the scaffold `/ca:init` -produces. They're living reference documents, not audit logs: hand-editable, read by every +`/ca:init` creates the activation file, open boards, override log, and checkpoint counter. It then +routes an existing codebase to `/ca:create-context` or a greenfield repository to `/ca:decompose`. +Those context-building flows produce `security-controls.md`, `tech-stack.md`, and +`coding-standards.md`. They are living reference documents, not audit logs: hand-editable, read by every reviewer agent (`security-controls.md` especially: the crypto-compliance and secret-handling gates read it before every review, and it's level 1 in the conflict hierarchy). `last-checkpoint` is a one-line counter (the override count at the time of the last `/ca:checkpoint`) that the statusline diff --git a/site/src/content/docs/concepts.md b/site/src/content/docs/concepts.md index 1f95f62d..faa38453 100644 --- a/site/src/content/docs/concepts.md +++ b/site/src/content/docs/concepts.md @@ -1,15 +1,59 @@ --- -title: Concepts -description: "The core ideas behind codeArbiter: gated lanes, SMARTS, ADRs, checkpoints, and the full governance model." +title: Concept Map +description: "A practical map of codeArbiter's governance model: how work enters a lane, decisions are recorded, context stays current, and evidence becomes auditable." +journey: + level: "Practitioner" + time: "7 minutes" + outcome: "Connect lanes, decisions, repository context, review, and audit evidence." + prerequisites: + - "What Is codeArbiter" + proof: "You can trace one real change from its entry lane to the evidence retained after shipment." --- -codeArbiter is built on a set of interlocking ideas. Each page covers one concept in full. +codeArbiter is easiest to understand as a chain: -- [The gated-lane model](/concepts/gated-lanes/): every kind of work has a sanctioned path with gates scaled to its risk. -- [SMARTS](/concepts/smarts/): the structured scoring rubric for autonomous decisions, recorded in the sprint log. -- [ADRs and the decision log](/concepts/adrs/): numbered, dated, user-attributed records of architecturally significant choices. -- [Checkpoints](/concepts/checkpoints/): periodic read-only sweeps that catch drift and latent issues between feature work. -- [The persona-register split](/concepts/persona-and-context/): separate orchestrator, author, and reviewer personas that keep each role sharp. -- [Provenance & context drift](/concepts/provenance-drift/): source-hash tracking that surfaces stale claims and heals them at commit time. -- [Just-in-time context injection](/concepts/jit-context-injection/): a four-tier map that injects a governance pointer on every governed-file Read. -- [Auditability](/concepts/auditability/): how the pieces compose into an auditable record for any range of work. +1. **A request enters a lane.** The lane defines the work, its required evidence, and its stops. +2. **Material choices are explicit.** The user decides hard gates; SMARTS records bounded autonomous + choices; ADRs preserve architectural choices. +3. **The repository carries the context.** Provenance and just-in-time injection keep agents aligned + with the checked-in rules that govern the files they touch. +4. **Independent reviewers challenge the result.** Checkpoints and lane-specific reviewers produce + findings without silently rewriting the author's work. +5. **The audit record explains what happened.** Commits, decisions, overrides, questions, sprint + choices, and reports can be assembled for a range after the work is done. + +## Start with the problem you are trying to understand + +| Question | Read | +|---|---| +| Why must each kind of work follow a different path? | [The gated-lane model](/concepts/gated-lanes/) | +| How does an autonomous sprint choose without asking every time? | [SMARTS](/concepts/smarts/) | +| Which protections are deterministic and which are advisory? | [Enforcement & Security](/enforcement/) | +| How is a long-lived architecture choice recorded? | [ADRs and the decision log](/concepts/adrs/) | +| How do repository facts stay current as source changes? | [Provenance and context drift](/concepts/provenance-drift/) | +| How does the right rule reach an agent at file-read time? | [Just-in-time context injection](/concepts/jit-context-injection/) | +| Why are authors, reviewers, and routing kept separate? | [The persona-register split](/concepts/persona-and-context/) | +| What catches drift outside one feature lane? | [Checkpoints](/concepts/checkpoints/) | +| What evidence can be reconstructed later? | [Auditability](/concepts/auditability/) | +| Why does a particular hardening control exist? | [Selected hardening notes](/concepts/hardening-history/) | + +## A concrete example + +Suppose a feature adds an authentication dependency and changes a security entry point. The feature +lane provides the specification, tests, and author/reviewer sequence. The dependency lane vets the +new package before installation. Reading the security file injects the controlling security +document. An unresolved trust-boundary choice becomes an ADR instead of an implementation guess. +The commit gate requires the applicable evidence, and the final audit can show the decision, +dependency review, security findings, override history, and commit together. + +No single concept is the product. The value comes from their composition: the lane limits what may +happen, repository context says what must remain true, reviewers challenge the result, and the audit +trail makes the path inspectable. + +## Put the model to work + +- To complete a task, choose a page under [Workflows](/guides/feature-lane/) and follow its + host-native command. +- To investigate a block, start with [Troubleshooting](/guides/troubleshooting/) and the + [Hook Gates reference](/reference/hooks-gates/). +- To inspect exact command, skill, or agent behavior, use [All Reference](/reference/). diff --git a/site/src/content/docs/concepts/adrs.md b/site/src/content/docs/concepts/adrs.md index 4908fe9d..8d77ba24 100644 --- a/site/src/content/docs/concepts/adrs.md +++ b/site/src/content/docs/concepts/adrs.md @@ -1,6 +1,13 @@ --- title: ADRs and the Decision Log description: "How architecturally significant choices are captured as numbered, dated, user-attributed Architecture Decision Records with supersede chains and decision-health reporting." +journey: + level: Power user + time: 8 min + outcome: "the ability to distinguish ADR files from the append-only decision ledger, trace active governs globs, and follow a forward-only supersession chain." + prerequisites: + - Know why the decision matters to later work + proof: "You can find the active ADR for a path and explain why the prior record was not rewritten." --- Architecturally significant choices are written down as **Architecture Decision Records**: @@ -46,3 +53,15 @@ for the full gate catalog. `/ca:adr-status` MAY additionally dispatch [`decision-challenger`](/reference/agents/decision-challenger/), an optional adversarial reviewer that red-teams each decision and assigns it a confidence score from 1 to 5. + +## Use the Records in Practice + +When a write reminder says a path is governed by an ADR, open that numbered record and verify three +things before continuing: its status is `accepted`, the edited path matches a `governs:` glob, and no +later ADR supersedes it. Use `/ca:adr-status --adr N` (Codex: `$ca-adr-status --adr N`) when the +chain is not obvious. + +Use an ADR for a durable architectural choice future work must obey. Do not use one for a temporary +task, a debugging hypothesis, or an option you have not decided. Record a decision through the +[ADR workflow](/guides/recording-adrs/); use reconcile when competing artifacts still need SMARTS +arbitration. diff --git a/site/src/content/docs/concepts/auditability.md b/site/src/content/docs/concepts/auditability.md index a9ac6d41..82c6c39c 100644 --- a/site/src/content/docs/concepts/auditability.md +++ b/site/src/content/docs/concepts/auditability.md @@ -1,6 +1,13 @@ --- title: Auditability description: "How ADRs, the sprint log, checkpoints, and the append-only override trail combine to make a codeArbiter repository auditable after the fact for any range of work." +journey: + level: "Power user" + time: "8 minutes" + outcome: "Assemble and validate the governance record for a release or handoff." + prerequisites: + - "An initialized repository with at least one governed commit" + proof: "A dated audit packet whose range and verbatim records match their source files." --- Taken together, these concepts make a codeArbiter repository auditable after the fact. ADRs @@ -11,6 +18,13 @@ governance record can be assembled for any range of work on demand. For the concrete catalog of what runs in each lane, see the auto-generated [Reference](/reference/). +## When to use it + +Assemble an audit when you need to explain a release, hand work to another maintainer, review a +security-sensitive range, or establish whether an override or low-confidence autonomous choice +affected a result. The audit command is read-only: it projects existing evidence into a new report; +it does not repair missing records or rewrite the sources it reads. + ## The assembler [`/ca:audit`](/reference/commands/audit/) is the command that synthesizes the record. It @@ -35,3 +49,36 @@ The result is written to `.codearbiter/audits/.md` and is never over second audit on the same day gets a `-2` suffix, a third `-3`. Override lines and low-confidence sprint entries are quoted verbatim rather than summarized, and a section with nothing to report says so explicitly instead of being omitted. + +## Example + +```text +/ca:audit v2.8.12 HEAD +``` + +| Host | Invocation | +|---|---| +| Claude Code | `/ca:audit v2.8.12 HEAD` | +| Codex | `$ca-audit v2.8.12 HEAD` | +| Pi | `/ca-audit v2.8.12 HEAD` | + +Open the path printed by the command and verify that its range, commit list, unresolved questions, +and verbatim override lines match the source files. An empty section is evidence that the source +had nothing in that category, not proof that an expected record was created elsewhere. + +## Validate the packet + +Do not treat file creation as success. Check the packet in this order: + +1. Confirm the requested `from` and `to` refs resolved to the commits you intended. +2. Pick one commit and trace it back to `git log`. +3. If the range contains an override, compare the packet's line byte-for-byte with + `.codearbiter/overrides.log`. +4. If it contains an ADR, open the named file and verify its status and decision-log entry. +5. Review the unresolved questions section before handing the packet to someone else. + +Your proof is not “the command exited zero.” It is a packet whose selected evidence can be traced +back to the checked-in source of record. + +If evidence is missing, stop and trace the owning workflow. Do not hand-edit the assembled report +to make the record look complete. diff --git a/site/src/content/docs/concepts/checkpoints.md b/site/src/content/docs/concepts/checkpoints.md index 763aa278..b2903025 100644 --- a/site/src/content/docs/concepts/checkpoints.md +++ b/site/src/content/docs/concepts/checkpoints.md @@ -1,6 +1,14 @@ --- title: Checkpoints description: "Periodic, read-only sweeps of the whole codebase by the reviewer fleet, consolidated into dated reports to catch drift and latent issues between feature work." +journey: + level: Power user + time: 10–30 min + outcome: "a repeatable whole-tree review cadence, a dated triaged report, and a clear boundary between checkpoint findings and required change gates." + prerequisites: + - An initialized repository + - Enough time for six read-only review lenses + proof: "A dated checkpoint report exists, every finding has severity and location, and follow-up work is routed separately." --- A **checkpoint** is a periodic, read-only sweep of the whole codebase by the reviewer fleet. @@ -41,6 +49,23 @@ report. A checkpoint is a report, not a promotion gate. It enforces no sign-off and blocks nothing by itself. +## Run and Act on a Checkpoint + +Invoke `/ca:checkpoint` in Claude Code, `$ca-checkpoint` in Codex, or `/ca-checkpoint` in Pi. Let the +read-only fleet finish; do not edit in parallel and then mistake moving source for a stable review +target. + +Review `.codearbiter/checkpoints/YYYY-MM-DD.md` from highest severity down: + +1. Confirm each finding still points to the named file and line. +2. Route a confirmed defect through the fix lane, a structural change through refactor, and new + behavior through feature. A checkpoint does not implement its own findings. +3. Triage `[NEEDS-TRIAGE]` items into a scoped follow-up or an explicit defer condition. +4. Run status and confirm the override counter reset captured by `.codearbiter/last-checkpoint`. + +If a reviewer fails or its evidence is incomplete, the report must say so. A partially populated +file is not equivalent to a clean sweep. + ## The deep counterpart [`/ca:tribunal`](/reference/commands/tribunal/) convenes the rare, on-demand version of this diff --git a/site/src/content/docs/concepts/gated-lanes.md b/site/src/content/docs/concepts/gated-lanes.md index 471b40be..bfe316dc 100644 --- a/site/src/content/docs/concepts/gated-lanes.md +++ b/site/src/content/docs/concepts/gated-lanes.md @@ -1,6 +1,13 @@ --- title: The Gated-Lane Model description: "How codeArbiter assigns every kind of work to a sanctioned path with gates scaled to its risk, and what distinguishes soft gates from hard gates." +journey: + level: "Foundation" + time: "8 minutes" + outcome: "Choose the correct lane for a change and recognize who can clear its next gate." + prerequisites: + - "Read What Is codeArbiter" + proof: "You can classify a real repository task without reaching for override or implementation first." --- Every kind of work has a **lane**: a sanctioned path with gates scaled to its risk, and the @@ -21,6 +28,21 @@ at the spec rather than working around at the gate.
Soft gates surface and wait. Hard gates stop, and only the user can clear them.
+## Choose a lane from the work, not its size + +| Situation | Start here | Why | +|---|---|---| +| Add user-visible behavior | `/ca:feature` | A spec and failing test establish the new contract. | +| Repair a defect with a confirmed cause | `/ca:fix` | A regression test must fail before the minimal repair. | +| Find the cause of an unexplained failure | `/ca:debug` | Investigation comes before selecting a change lane. | +| Restructure without changing behavior | `/ca:refactor` | Existing tests must remain unmodified and prove parity. | +| Change docs, bump a dependency, or revert | `/ca:chore` | The lighter lane scales evidence to non-feature work. | +| Explore a disposable idea | `/ca:spike` | The branch cannot merge or become a PR. | +| Run a governed multi-task campaign | `/ca:sprint` | One approved spec and plan drive logged autonomous decisions. | + +The number of changed lines does not choose the lane. A one-line behavior change is still a feature +or fix; a 500-line generated reference refresh can still be a chore. + ## How many gates, concretely Lane depth is scaled to risk. The [`tdd` skill](/reference/skills/tdd/) runs 6 gated phases: @@ -58,3 +80,15 @@ merge to the default branch is forbidden. The one sanctioned way around any of this is [`/ca:override`](/reference/commands/override/): a deliberate bypass that appends one line to `overrides.log` and proceeds. It is logged, not silent. + +## Check your understanding + +Take the next item from your issue tracker and write down three things before invoking a command: + +1. the lane that owns it; +2. the first evidence that lane must produce; +3. whether the next stop is a soft decision or a hard gate. + +Then compare your choice with the command's [generated reference page](/reference/). A correct +classification is the proof for this module; invoking an override because the wrong lane stopped +you is not. diff --git a/site/src/content/docs/concepts/hardening-history.md b/site/src/content/docs/concepts/hardening-history.md index 1aeeb54d..cebb11aa 100644 --- a/site/src/content/docs/concepts/hardening-history.md +++ b/site/src/content/docs/concepts/hardening-history.md @@ -1,6 +1,13 @@ --- -title: Hardening History -description: "Design notes on why codeArbiter's enforcement gates are built the way they are, plus a dated log of hardening work as it shipped." +title: Selected Hardening Notes +description: "Design notes for several important enforcement choices, plus selected historical examples; the changelog remains the complete release record." +journey: + level: "Reference" + time: "8 minutes" + outcome: "Explain why selected controls use digest binding, single writers, and fail-closed reads." + prerequisites: + - "Enforcement & Security" + proof: "You can connect each design choice to the race, drift, or bypass it prevents." --- [Enforcement & Security](/enforcement/) states what is enforced. This page explains why some of those gates are built the way they are, and records the hardening work that shaped them over time. @@ -28,9 +35,10 @@ This design replaced an earlier pattern of a separate, lagging `chore(board)` PR `/ca:standup` and `/ca:doctor` each run a read-only reconciliation sweep and surface any merged-but-not-flipped task; they report findings without writing to the board themselves. -## Hardening Log +## Selected Historical Examples -Dated record of enforcement hardening as it shipped, newest first. Versions correspond to `CHANGELOG.md` entries in the repository root. +These examples explain the defenses established in v2.5.2; they are not a complete hardening log. +Use the generated [Changelog](/changelog/) for the current, chronological release record. - **v2.5.2 (2026-06-25)** — Broadened crypto detection: `CRYPTO_RE` now flags `rc2` and `blowfish` alongside MD5, SHA-1, DES, 3DES, and RC4, plus TLS-disable forms (`rejectUnauthorized: false`, `NODE_TLS_REJECT_UNAUTHORIZED`, `verify=False`, `InsecureSkipVerify`). - **v2.5.2 (2026-06-25)** — Compound-name secret detection: `SECRET_RE` now matches compound keys (`aws_secret_access_key`, `client_secret`, `private_key`) and known token shapes (`AKIA…`, `ghp_…`, `sk-ant-…`). @@ -43,3 +51,4 @@ Dated record of enforcement hardening as it shipped, newest first. Versions corr - [Enforcement & Security](/enforcement/): the user-facing statement of what is enforced. - [ADRs and the Decision Log](/concepts/adrs/) - [Hooks reference](/hooks/) +- [Changelog](/changelog/) diff --git a/site/src/content/docs/concepts/jit-context-injection.md b/site/src/content/docs/concepts/jit-context-injection.md index 53c4662c..154b6b6c 100644 --- a/site/src/content/docs/concepts/jit-context-injection.md +++ b/site/src/content/docs/concepts/jit-context-injection.md @@ -1,10 +1,20 @@ --- title: Just-in-Time Context Injection description: "How codeArbiter's PreToolUse:Read hook injects a budgeted knowledge pointer for governed files using a four-tier priority map, without ever blocking the Read." +journey: + level: "Power user" + time: "7 minutes" + outcome: "Predict which repository rule a file read will surface and verify the mapping." + prerequisites: + - "An initialized repository" + - "A security entry point, accepted ADR, approved spec, or provenance claim to inspect" + proof: "A fresh-session Read surfaces the highest-priority governing document you predicted." --- When any agent reads a file, a `PreToolUse:Read` hook fires first. It checks whether the file is governed by any knowledge document and, if so, injects a short pointer capped at a 150-token budget. The Read always proceeds. The hook is fail-open and never blocks it. +## How a governing document is chosen + The check follows a four-tier priority map. Each tier is evaluated in order; the first match wins: 1. **Security controls:** if the file path is a security-entry point in `security-controls.md`, that document governs it. @@ -14,9 +24,40 @@ The check follows a four-tier priority map. Each tier is evaluated in order; the A file that matches none of these tiers is non-governed. The hook injects nothing and makes no git call. Injection is deduplicated once per session and file, so repeated reads of the same file do not accumulate pointer tokens. +## What you should expect + +The additional context names the governing document and why it matched; it is a pointer, not the +entire policy. Open that document before making a scope-touching change. If no pointer appears, do +not infer that the file is safe or ungoverned: the hook is deliberately fail-open, so inspect the +repository's security controls, accepted ADRs, approved specs, and provenance map when the change is +sensitive. + +To verify a mapping, run the read through a fresh session and compare the injected pointer with the +matching `governs:` glob or provenance record. The same file is injected only once per session, so +use a new session when repeating the check. + +## Practice: predict one injection + +1. Choose a file named by `security-controls.md`, an accepted ADR `governs:` glob, or an approved + spec `**Governs:**` line. +2. If more than one source matches, use the priority order above to predict the winner. +3. Start a fresh host session and ask the host to read that exact file. +4. Confirm the additional context names the document you predicted and gives the same match reason. +5. Open the governing document before changing the file. + +If the pointer is absent or different, do not work around it. Check the glob, ADR status, provenance +hash, and opt-in state; then use the [troubleshooting workflow](/guides/troubleshooting/) to inspect +the read hook. + +## Where it fits + The hook is implemented as `pre-read.py`, backed by `_readinjectlib.py`; see the [hooks and gates reference](/reference/hooks-gates/) for the full gate catalog.
The four-tier file-to-knowledge map: a Read is matched against security-controls.md, accepted ADRs, approved specs, and provenance in priority order, and the highest-priority match's pointer is injected within a 150-token budget.
Four tiers, evaluated in priority order. The highest match governs; a non-governed Read injects nothing.
+ +See [Provenance and context drift](/concepts/provenance-drift/) for how the lowest tier stays fresh, +and [Enforcement & Security](/enforcement/) for the difference between this advisory read path and a +blocking hook. diff --git a/site/src/content/docs/concepts/persona-and-context.md b/site/src/content/docs/concepts/persona-and-context.md index 19fc9ff9..9040b20f 100644 --- a/site/src/content/docs/concepts/persona-and-context.md +++ b/site/src/content/docs/concepts/persona-and-context.md @@ -1,6 +1,13 @@ --- title: The Persona-Register Split description: "How codeArbiter separates the orchestrator, author, and reviewer personas to keep each role sharp and its context footprint small." +journey: + level: Power user + time: 6 min + outcome: "a clear model of who routes, who writes, who reviews, and how to inspect a dispatch without trying to invoke an internal role directly." + prerequisites: + - Understand command, skill, and agent terminology + proof: "You can trace one lane from public command to owning skill to its author or reviewer role." --- codeArbiter speaks with more than one voice, deliberately. The **orchestrator** persona is @@ -38,3 +45,25 @@ Every reviewer is read-only by construction. The roster splits into three famili The orchestrator persona itself lives in `plugins/ca/ORCHESTRATOR.md`. The split is a recorded decision, not an accident of file layout. See ADR-0005. + +## What this means in practice + +Invoke the lane command; do not try to dispatch a specialist agent by name. The orchestrator chooses +the owning skill, and that skill dispatches the author or reviewer with the narrow tools and context +it needs. A reviewer finding returns to the lane for triage; the reviewer does not silently edit the +author's files. + +Generated [agent reference pages](/reference/#agents) show each role's purpose, tools, and exact +source. Use them to understand who may be dispatched, not as a second command surface. + +## Inspect a Dispatch + +Start at the public command reference and follow its owning skill. The skill body names each role it +may dispatch and the condition that activates it. Then open the generated agent page to inspect that +role's tools, model tier, constraints, and exact source. + +Claude Code can dispatch packaged agents through its native task tool. Codex currently executes the +same reviewer and author responsibilities inline in the active thread; Pi uses its supervised child +path where supported. That host difference changes isolation mechanics, not ownership or the gate +that consumes the result. The [compatibility matrix](/getting-started/compatibility/#host-differences) +records the current boundary. diff --git a/site/src/content/docs/concepts/provenance-drift.md b/site/src/content/docs/concepts/provenance-drift.md index 946a4e87..7122e1f6 100644 --- a/site/src/content/docs/concepts/provenance-drift.md +++ b/site/src/content/docs/concepts/provenance-drift.md @@ -1,17 +1,48 @@ --- title: Provenance & Context Drift description: "How codeArbiter tracks source-file hashes to detect when derived documents have drifted from their backing sources, and how the commit-gate auto-heal step keeps them current." +journey: + level: Power user + time: 8 min + outcome: "the ability to trace a stale derived document to changed source and choose re-scout, re-baseline, or defer without hiding drift." + prerequisites: + - A repository with provenance-tracked context + proof: "Context-check reports no unexplained stale documents, or each defer has an explicit reopen condition." --- Every derived document under `.codearbiter/` traces its claims to source files in the repo. codeArbiter records this mapping as **provenance**: which files back a given doc, and the `git hash-object` hash of each file at the time the doc was authored. When a tracked source changes, the stored hash no longer matches. That mismatch is **context drift**. +## What happens when source changes + Drift is surfaced once, passively, as a single line at SessionStart. It does not interrupt work. `.codearbiter/code-map.md` provides a coarse orientation map: it names the main source files and the docs that depend on them. `/ca:context-check` runs the same detection on demand, as a manual audit. When a commit lands, the **commit-gate auto-heal** step checks whether any staged source files have drifted their dependent docs. If they have, it re-baselines the provenance in the same commit or proposes a doc update to accompany the work, so a stale claim gets flagged and corrected before it can be read as current. Auto-heal is commit-gate's Phase 5.5, per the [commit-gate skill](/reference/skills/commit-gate/); on an ordinary commit with an empty worklist, it costs nothing. Per-document provenance records live under `.codearbiter/.provenance/`. [`/ca:context-check`](/reference/commands/context-check/) runs the same drift detection as a manual, on-demand audit, and offers three dispositions per stale doc: re-scout it from the current source, re-baseline the stored hash without re-scouting, or defer. +## Choose the correct disposition + +| Choice | Use it when | Result | +|---|---|---| +| Re-scout | The source change may make the document's claims incomplete or false. | Rebuild the document from current evidence, then record new hashes. | +| Re-baseline | You inspected the source change and proved the existing claims remain true. | Keep the document and update only its source hashes. | +| Defer | The document needs work that is out of scope for the current change. | Leave the mismatch visible with an explicit reopen condition. | + +Re-baselining is an evidence claim, not a way to silence the warning. If you cannot explain why the +text remains correct, re-scout or defer. +
Context-drift provenance flow: a tracked source change is detected by a git hash-object mismatch, surfaced at SessionStart, and healed by commit-gate which re-baselines or proposes a doc update with the work commit.
Provenance tracks source hashes. A mismatch is detected at SessionStart and healed when the commit lands.
+ +Run the host-native context-check command to inspect drift without changing ordinary project files: +Claude Code `/ca:context-check`, Codex `$ca-context-check`, Pi `/ca-context-check`. + +Read the report one document at a time. A clean result names no stale provenance records. A stale +result should name the derived document, changed source, old hash, and current hash; if it cannot, +treat the record as untrustworthy and re-scout rather than blindly re-baseline. + +After a disposition, run context-check again. Re-scouted and proven re-baselined documents should +leave the stale set. Deferred documents should remain visible with the condition that will reopen +them; defer is tracked debt, not a green result. diff --git a/site/src/content/docs/concepts/smarts.md b/site/src/content/docs/concepts/smarts.md index 7e815b8a..3a20126a 100644 --- a/site/src/content/docs/concepts/smarts.md +++ b/site/src/content/docs/concepts/smarts.md @@ -1,6 +1,13 @@ --- title: SMARTS description: "The structured, multi-lens scoring rubric codeArbiter uses when weighing options during autonomous runs, with every verdict recorded in an append-only sprint log." +journey: + level: Power user + time: 8 min + outcome: "the ability to read, challenge, and audit a SMARTS verdict without mistaking the rubric for a complete business decision." + prerequisites: + - Understand autonomous sprint boundaries + proof: "You can explain all six lenses, the three recommendation strengths, and which non-SMARTS concerns must be surfaced separately." --- When codeArbiter must weigh options, and especially when `/ca:sprint` decides "as the user" diff --git a/site/src/content/docs/enforcement.md b/site/src/content/docs/enforcement.md index 7b2327fd..2735f24d 100644 --- a/site/src/content/docs/enforcement.md +++ b/site/src/content/docs/enforcement.md @@ -1,6 +1,13 @@ --- title: Enforcement & Security description: "How codeArbiter enforces its gates at the tool-call boundary: the activation contract, the blocking commit-time gates, advisory reminders, and the fail-loud posture." +journey: + level: "Power user" + time: "12 minutes" + outcome: "Distinguish deterministic hook enforcement from orchestrator policy and diagnose the right layer." + prerequisites: + - "The gated-lane model" + proof: "Given a blocked action, you can name its hook flank, gate id, and sanctioned remediation." --- codeArbiter's gates are not advice the model can talk past. They run as Claude Code or Codex @@ -44,8 +51,11 @@ These run in `pre-bash.py` on `PreToolUse(Bash|PowerShell)` (plus the Write/Edit | **H-09b / H-10b** | Crypto and secret commit gate. A commit that introduces a crypto/TLS or secret line is blocked unless the crypto-compliance / secret-handling gate has recorded a pass for **those exact lines**. | | **H-11** | ADRs are authored only via `/ca:adr`. Both the shell flank (redirects, `cp`, `rm`, `sed -i` into `decisions/`) and the Write/Edit flank are guarded; the skill drops a fresh authoring marker first. | | **H-14** | Migration review. A commit staging a database migration is blocked unless a migration-review pass is recorded for that file's current content. | +| **H-18 / H-19** | Repository activation and gate-pass markers cannot be disabled or forged through shell, Write, Edit, or patch operations. | +| **H-20** | `--no-verify` and its supported short forms cannot bypass the repository's pre-commit or pre-push backstop. | +| **H-21** | A write or edit envelope the host adapter cannot decompose into guardable per-file operations is refused instead of being allowed through uninspected. Retry as a plain supported patch or split the operation. | -H-09b/H-10b is digest-bound: a recorded pass covers only the exact sensitive lines it approved, which closes a time-of-check / time-of-use gap between approval and commit. The board-sync behavior implied by the commit gate (only `/ca:task` writes `open-tasks.md`) follows from ADR-0008. For the design rationale behind both, see [Hardening History](/concepts/hardening-history/). +H-09b/H-10b is digest-bound: a recorded pass covers only the exact sensitive lines it approved, which closes a time-of-check / time-of-use gap between approval and commit. The board-sync behavior implied by the commit gate (only `/ca:task` writes `open-tasks.md`) follows from ADR-0008. For the design rationale behind both, see [Selected Hardening Notes](/concepts/hardening-history/). ## Advisory, Non-Blocking Reminders @@ -62,7 +72,7 @@ These are advisory **because they bite at a later boundary, not at commit.** A b ## Sandbox Isolation for Untrusted Repositories -codeArbiter runs untrusted repositories inside `ca-sandbox`, isolated as non-root (`--user 1000:1000`), with a `--read-only` root, `--cap-drop ALL`, `--security-opt no-new-privileges`, and a fail-closed network policy (default `--network none`; an unknown policy is a hard error, not a silent pass-through). No host bind mounts; the docker socket is never mounted. For how this posture evolved release over release, see [Hardening History](/concepts/hardening-history/). +codeArbiter runs untrusted repositories inside `ca-sandbox`, isolated as non-root (`--user 1000:1000`), with a `--read-only` root, `--cap-drop ALL`, `--security-opt no-new-privileges`, and a fail-closed network policy (default `--network none`; an unknown policy is a hard error, not a silent pass-through). No host bind mounts; the docker socket is never mounted. For selected rationale behind this posture, see [Selected Hardening Notes](/concepts/hardening-history/).
` form and the hook set is reviewed through `/hooks`. Install and verify through +[Install: Codex](/getting-started/install/#codex). + +The dated [Claude Code + Codex verification record](/getting-started/claude-code-and-codex/) defines +the exact parity boundary and intentional host differences. + +## Choose Pi when + +You use Pi and accept a preview adapter with a green automated promotion matrix but less real-world +evidence than the two stable hosts. Pi uses Git tags, requires Node.js 22.19+, and will not activate +repository-aware behavior until project trust is affirmative. + +Follow the dedicated [Pi install and trust guide](/getting-started/pi/). + +## Use more than one host + +Two adapters can open different checkouts of the same repository and read the same committed +context, decisions, tasks, and audit history. If two sessions mutate the exact same working tree at +the same time, sequence task-board and dev-mode changes; that race exists for same-host sessions too. +Append-only audit writes are designed for concurrent attribution. + +For the complete surface matrix, platform requirements, network behavior, and preview flags, read +[Compatibility](/getting-started/compatibility/). diff --git a/site/src/content/docs/getting-started/claude-code-and-codex.md b/site/src/content/docs/getting-started/claude-code-and-codex.md index 8aa912e2..7b903256 100644 --- a/site/src/content/docs/getting-started/claude-code-and-codex.md +++ b/site/src/content/docs/getting-started/claude-code-and-codex.md @@ -1,13 +1,20 @@ --- title: Claude Code + Codex description: Verified shared enforcement and project-context parity across Claude Code and Codex, including evidence, installation status, and intentional host differences. +journey: + level: "Reference" + time: "9 minutes" + outcome: "Choose safely between Claude Code and Codex while sharing one governed repository state." + prerequisites: + - "Choose Your Host" + proof: "You can name the shared enforcement core, the host-specific command form, and the concurrency boundary." --- -# Shared enforcement and project-context parity across Claude Code and Codex - **Documentation launch: 2026-07-12.** This page announces the verified 2026-07-11 Codex support result while retaining the public-marketplace smoke test as a release gate. +**Shared enforcement and project-context parity across Claude Code and Codex.** + Of codeArbiter's four sibling plugins, this page covers two: `ca` for Claude Code and `ca-codex` for OpenAI Codex (see [Pi](/getting-started/pi/) for the third governance host, currently a Feature Forge `preview` with real use and feedback welcome, and diff --git a/site/src/content/docs/getting-started/compatibility.md b/site/src/content/docs/getting-started/compatibility.md index 39bce8bd..d9ca5cae 100644 --- a/site/src/content/docs/getting-started/compatibility.md +++ b/site/src/content/docs/getting-started/compatibility.md @@ -1,6 +1,12 @@ --- title: Compatibility description: "Platform, interpreter, and dependency requirements for codeArbiter: what the plugin itself needs, versus what's only required to develop the docs site." +journey: + level: "Reference" + time: "Lookup" + outcome: "Confirm host, Python, platform, and optional-feature prerequisites before installation." + prerequisites: [] + proof: "Your selected host and optional features meet every requirement in the relevant row." --- codeArbiter's requirements are deliberately narrow: Claude Code, Codex, or Pi, plus Python 3 on @@ -15,7 +21,7 @@ and the [Pi install page](/getting-started/pi/) for the `ca-pi` install flow. | **Claude Code** | Any version with plugin support | `plugin.json` states no explicit minimum version; the plugin uses standard hook events (`SessionStart`, `PreToolUse`, `PostToolUse`) and the plugin/marketplace install commands documented in [Install](/getting-started/install/). | | **Codex** | Minimum 0.143.0; live-verified on 0.144.1 | `ca-codex` uses one OS-specific handler per event and a Codex adapter that converts the shared guard verdict to structured deny output. Trust the hook set through `/hooks`. | | **Pi** | 0.80.5 or 0.80.10 (this release line) | `ca-pi` is a Feature Forge `preview`, available and welcomed for real use while broader testing continues before stable status or a claim of 100% validation. Install it Git-only: `pi install git:github.com/arbiterForge/codeArbiter@ca-pi-v`. Also requires Node.js 22.19+. Requires an affirmative project-trust decision before repository-aware startup. Its human-readable generated catalog is `plugins/ca-pi/SKILLS.md`. See [Install for Pi](/getting-started/pi/). | -| **Python** | Python 3, stdlib only, resolvable as `python3` **or** `python` on `PATH` | Every hook is registered twice in `hooks.json` (once under `python3`, once under a `python3 -c "" \|\| python` fallback), so it runs on a machine where only `python` resolves. No third-party Python packages are ever installed or imported (ADR-0004: database-free, stdlib-only architecture). On Pi, a missing interpreter blocks mutating calls and surfaces an interpreter breadcrumb rather than failing silently. | +| **Python** | Python 3, stdlib only, available under the interpreter name your adapter registers | Claude Code carries its documented `python3`/`python` fallback shape. Codex uses OS-specific `command` and `commandWindows` handlers and fails loud if its selected interpreter is absent. Pi installs final TypeScript wrappers first, then blocks mutating calls with an interpreter breadcrumb until the Python bridge is healthy. No adapter treats that state as active enforcement. No third-party Python packages are installed or imported (ADR-0004). | | **Operating system** | Windows, macOS, or Linux | Hooks are pure Python stdlib and carry no OS-specific code path beyond the interpreter-name fallback above. The `.git/hooks` backstop shim is a POSIX `sh` script; on Windows this runs under Git for Windows' bundled `sh.exe`, which ships with every standard Git install. Windows is also a promoted, tested platform for `ca-pi`; see [Windows notes](/getting-started/pi/#windows). | | **git** | Any reasonably current git | Required regardless of codeArbiter: the plugin reads repo state (`git config user.email`, branch, diff) via subprocess calls to your existing `git` binary, and installs the `.git/hooks` backstop through it. | | **Node.js** | Not required for Claude Code or Codex | Node is required for `ca-pi` (22.19+) and is only otherwise needed to build or develop **this documentation site** (`site/`) and the optional pluggable-execution-farm TypeScript dispatcher (`plugins/ca/tools/`) if you use `/ca:sprint --farm`. Node is not a runtime dependency of the Claude Code/Codex enforcement hooks themselves. | @@ -52,8 +58,9 @@ parent-interactive only and absent from JSON, RPC, print, and hardened children. Confirm both before installing, per [Install](/getting-started/install/): -- **Python 3 on `PATH`.** Without it, the gates and the session-startup injection silently do not - run. /ca:doctor catches this as an interpreter-resolution failure. +- **Python 3 on `PATH`.** Without it, Claude Code can be inactive, Codex's hook handler fails loud, + and Pi blocks mutation. Run the host-native doctor command and require a healthy interpreter and + live-fire row before treating the adapter as active. - **`git config user.email` set.** Overrides and ADRs are attributed to this identity; an unset email is asked for once, interactively, rather than silently defaulting. diff --git a/site/src/content/docs/getting-started/install.md b/site/src/content/docs/getting-started/install.md index 626f7138..f00c528b 100644 --- a/site/src/content/docs/getting-started/install.md +++ b/site/src/content/docs/getting-started/install.md @@ -1,6 +1,14 @@ --- title: Install description: "Install codeArbiter for Claude Code, Codex, or Pi, opt into the shared .codearbiter/ store, and verify enforcement." +journey: + level: Foundation + time: 10–15 min + outcome: "one installed and trusted host adapter, plus a clear boundary between plugin installation and repository activation." + prerequisites: + - Python 3 on PATH + - Git identity configured + proof: "The host lists the adapter, and doctor reports the cached payload version after repository opt-in." --- codeArbiter ships four sibling plugins from one marketplace. Three are governance hosts: `ca` (Claude @@ -16,8 +24,10 @@ and [Compatibility](/getting-started/compatibility/) has the full host-differenc Confirm both before installing: -- **Python 3 on `PATH`**: every enforcement hook is pure Python. Without it, the gates and the - session-startup injection silently do not run. Verify with: +- **Python 3 on `PATH`**: every shared enforcement hook is pure Python. The failure posture is + host-specific: Claude Code can leave an unresolved hook inactive, Codex reports a loud handler + failure, and Pi blocks mutating calls with an interpreter breadcrumb. None of those states is + active governance. Verify before installation with: ```sh python3 --version || python --version diff --git a/site/src/content/docs/getting-started/pi.md b/site/src/content/docs/getting-started/pi.md index 5179abba..f85d18f3 100644 --- a/site/src/content/docs/getting-started/pi.md +++ b/site/src/content/docs/getting-started/pi.md @@ -1,6 +1,14 @@ --- title: Pi description: "Install codeArbiter for Pi, grant project trust, and verify enforcement. Covers the ca-pi Git-only install, supported versions, and version pinning." +journey: + level: "Foundation" + time: "12 minutes" + outcome: "Install a pinned ca-pi preview, trust the project, opt in a repository, and verify a real gate." + prerequisites: + - "Pi 0.80.10 or newer" + - "Node.js 22.19 or newer" + proof: "Pi reports the pinned extension and a disposable broad-stage probe is blocked by H-03." --- codeArbiter ships `ca-pi` as a third sibling plugin, alongside `ca` (Claude Code) and `ca-codex` @@ -73,6 +81,16 @@ wrappers, and the H-03 wrapper self-test. The module-identity row proves self-co operator-launched Pi CLI, imported module, package root, and reported version. It does **not** prove publisher authenticity. Verify the source separately with `pi list` and `pi config`. +## Rich Footer + +In an interactive, trusted session, `ca-pi` installs a rich footer that preserves Pi's native +session information and adds codeArbiter state and usage metrics. There is no separate wiring +command. Non-interactive modes do not install footer UI. + +On session shutdown, the extension restores Pi's native footer. If custom-footer initialization +fails or the required interactive UI surface is absent, it also restores the native footer and +prints a bounded direction to run `/ca-doctor`; governance does not depend on the footer rendering. + ## Trust and Security See [Enforcement & Security](/enforcement/#pi-project-trust-and-child-processes) for the project-trust diff --git a/site/src/content/docs/getting-started/quickstart.md b/site/src/content/docs/getting-started/quickstart.md index 97807b26..810fe50f 100644 --- a/site/src/content/docs/getting-started/quickstart.md +++ b/site/src/content/docs/getting-started/quickstart.md @@ -1,74 +1,147 @@ --- -title: Quickstart -description: "Opt a repository into codeArbiter enforcement, run a first command through a gated lane, and watch the commit gate block a real crypto mistake." +title: Protect Your First Repository +description: "Opt a disposable repository into codeArbiter, verify the live hook path with doctor, and run a first host-native command." +journey: + level: Foundation + time: 15 min + outcome: "a disposable, opted-in repository with a verified SessionStart briefing, real H-03 hook block, and first status report." + prerequisites: + - One supported host adapter installed + - Python 3 on PATH + - Git identity configured + proof: "Doctor sees BLOCKED [H-03], and status names the branch, stage, tasks, questions, and overrides." --- -This walkthrough works in Claude Code and Codex against the same `.codearbiter/` state. Where a -step shows `/ca:`, Codex uses `$ca-`. The -[Claude Code + Codex evidence](/getting-started/claude-code-and-codex/) records the live parity test -and intentional differences. +This walkthrough produces a reproducible proof without relying on a fictional application bug or +an agent choosing a particular implementation. You will create a disposable Git repository, opt it +in, restart the host, and let `doctor` exercise a harmless blocking hook. -This tutorial walks three steps in order: opt a repository into enforcement, run a first command through a gated lane, and watch a gate catch a mistake before it reaches version control. +## Before you start -Before starting, complete the plugin install from the [Install](/getting-started/install/) page. -Python 3 must be on your `PATH` and `git config user.email` must be set. Verify the host with -`/ca:doctor` in Claude Code or `$ca-doctor` in Codex. +Complete [Install](/getting-started/install/) for one host. If you have more than one supported +host installed, use [Choose Your Host](/getting-started/choose-your-host/) and review the +[Claude Code and Codex parity boundary](/getting-started/claude-code-and-codex/) before continuing. +Confirm: -## 1. Opt the Repo In +```sh +python3 --version || python --version +git config user.email +``` -Open the target repository in Claude Code or Codex and run the host-native form: +Both commands must print a usable value. The examples below use `ca-first-repo`; delete that +directory when you finish. -```text -/ca:init -$ca-init +## 1. Create a disposable repository + +```sh +mkdir ca-first-repo +cd ca-first-repo +git init +printf "# My first protected repository\n" > README.md +mkdir -p src +printf 'print("hello from a real source file")\n' > src/app.py +git add README.md src/app.py +git commit -m "chore: create disposable repository" ``` -`/ca:init` scaffolds `.codearbiter/` at the repo root and routes to a context populator for your situation. See [Opt a Repository In](/guides/opt-in-a-repo/) for the two routing paths and how to confirm the `arbiter: enabled` activation flag. With that flag set in a properly closed frontmatter block, the next session opens with the orchestrator active and every gate armed. +On PowerShell, create the same fixture with: + +```powershell +New-Item -ItemType Directory ca-first-repo +Set-Location ca-first-repo +git init +Set-Content -Encoding utf8 README.md "# My first protected repository" +New-Item -ItemType Directory src +Set-Content -Encoding utf8 src/app.py 'print("hello from a real source file")' +git add README.md src/app.py +git commit -m "chore: create disposable repository" +``` -## 2. Run a First Command +You should have one clean initial commit and a meaningful source file under `src/`. That source file +is load-bearing: context creation intentionally ignores a README-only repository when it decides +between brownfield scouting and greenfield decomposition. -Send the first real work through a gated [lane](/glossary/#lane): +## 2. Opt the repository in -```text -/ca:fix "webhook retries create duplicate payment records" -``` +Open the directory in your installed host and invoke the native command: -The `fix` lane routes to the test-first skill. An author agent reads the relevant source, writes a failing test, then writes the minimum implementation to pass it. +| Host | Command | +|---|---| +| Claude Code | `/ca:init` | +| Codex | `$ca-init` | +| Pi | `/ca-init` | -As the author writes `payment.ts`, the [advisory](/glossary/#advisory) hook fires immediately after the `Write` call. You should see it in the tool-call output before the write is even done: +For a repository with source, init routes to context creation. Review the generated project context +and complete the activation flow. Before leaving the session, verify +`.codearbiter/CONTEXT.md` begins with a closed frontmatter block containing: -```text -REMINDER [H-09]: Crypto/TLS pattern detected. Run the crypto-compliance check + dispatch auth-crypto-reviewer (no MD5/SHA1/DES/3DES/RC2/RC4/Blowfish; do not disable TLS verification). The commit will block until the gate records a pass. +```yaml +--- +arbiter: enabled +--- ``` -`[H-09]` is a gate ID. Look up any gate ID you see bracketed like this in the -[hook gates reference](/reference/hooks-gates/). +Commit the initialized state through the host-native commit command if the context flow has not +already done so. -The author wrote `createHash("md5")` to derive an idempotency key from the payment payload. MD5 is in codeArbiter's banned-primitive list. The advisory does not stop the write. It tells you the commit will. (The scan behind this gate is language-agnostic: a Python `hashlib.md5` call trips the identical H-09/H-09b pair.) +## 3. Start a fresh session -## 3. Observe the Gate Catch +Close the current host session and open a new one in the same repository. SessionStart should show +the codeArbiter briefing: project stage, blocking questions, in-flight tasks, and the command-catalog +pointer. -The tests pass. The author proposes to commit the work. Run it: +If you see no briefing, stop here and use [Troubleshooting](/guides/troubleshooting/). Do not treat +installed files as proof that hooks are active. -```text -/ca:commit -``` +## 4. Run the live-fire verification + +Invoke doctor: -The commit gate runs `pre-bash.py` before the `git commit` shell call fires. It reads the staged diff, finds the MD5 line, and exits 2. You should see the commit rejected: +| Host | Command | +|---|---| +| Claude Code | `/ca:doctor` | +| Codex | `$ca-doctor` | +| Pi | `/ca-doctor` | + +Doctor checks the interpreter, installed payload, cache, activation state, and hook wiring. Its +live-fire probe attempts a harmless `git add --all --dry-run`. The pre-tool hook should refuse the +broad add with a message containing: ```text -BLOCKED [H-09b]: This commit introduces crypto/TLS changes, but no security-gate pass is recorded (.codearbiter/.markers/security-gate-passed). Run the crypto-compliance gate (it records the pass), then commit. +BLOCKED [H-03] ``` -`.codearbiter/.markers/security-gate-passed` is a [marker](/glossary/#marker), a small file that -records a gate's pass state so a later gate can check it without re-running the review. `[H-09b]` is -the blocking counterpart to the `[H-09]` advisory above; see the -[hook gates reference](/reference/hooks-gates/) for both. +The probe changes no files and stages nothing. That block is the important result: it proves the +host discovered the hook, delivered the tool payload, ran the shared guard, and honored the deny +verdict. + +Doctor then reports the probe as healthy. If the shell command runs instead of being blocked, follow +the remediation ladder printed by doctor and the matching symptom in +[Troubleshooting](/guides/troubleshooting/). + +## 5. Run your first real command + +Ask for the current governed state: + +| Host | Command | +|---|---| +| Claude Code | `/ca:status` | +| Codex | `$ca-status` | +| Pi | `/ca-status` | + +The report should name the current branch, maturity stage, open tasks, open questions, and overrides +since the last checkpoint. It is read-only. -The `git commit` did not run. The mistake did not reach version control. +From here, choose the work in front of you: -To clear the gate, the crypto-compliance skill reviews the flagged line. MD5 as a hashing primitive is a banned pattern; the skill proposes replacing it with `createHash("sha256")`, which is on the approved list. Once the fix lands and the tests still pass, run `/ca:commit` again. You should see it succeed, with a line confirming the security-gate pass was recorded and bound to the changed line. +- [Build a feature end to end](/guides/feature-lane/) +- [Fix a confirmed defect](/reference/commands/fix/) +- [Run an autonomous sprint](/guides/autonomous-sprints/) +- [Add a dependency safely](/guides/adding-a-dependency/) +- [Record an architecture decision](/guides/recording-adrs/) -That is the commit gate working as designed: the advisory surfaces the problem at write time, and the hard gate closes before the mistake ships. +## Clean up -For the full catalog of blocking gates, see [Enforcement & Security](/enforcement/). For the ideas behind lanes and gate strengths, see [Concepts](/concepts/). +When you are done, leave the repository directory and delete `ca-first-repo`. It contains only the +disposable Git history and generated `.codearbiter/` state you just reviewed. Removing it does not +uninstall the plugin from your host. diff --git a/site/src/content/docs/glossary.md b/site/src/content/docs/glossary.md index 43f51019..12fc97b9 100644 --- a/site/src/content/docs/glossary.md +++ b/site/src/content/docs/glossary.md @@ -1,6 +1,12 @@ --- title: Glossary description: "One-to-three-sentence definitions of codeArbiter's core terms, each linking to the page that explains it fully." +journey: + level: "Reference" + time: "Lookup" + outcome: "Use codeArbiter terms with their exact, non-overlapping meanings." + prerequisites: [] + proof: "You can distinguish command, skill, agent, phase, stage, layer, gate, and severity." --- Short definitions for terms used across these docs. Each entry links to the page that explains diff --git a/site/src/content/docs/guides/adding-a-dependency.md b/site/src/content/docs/guides/adding-a-dependency.md index 60300394..11e3d7d1 100644 --- a/site/src/content/docs/guides/adding-a-dependency.md +++ b/site/src/content/docs/guides/adding-a-dependency.md @@ -1,10 +1,23 @@ --- title: "Add a Dependency Safely" description: "Use /ca:add-dep to vet a new third-party package for license compliance, provenance, and supply-chain risk before any install runs." +journey: + level: Practitioner + time: 10–20 min + outcome: "a version-pinned dependency decision backed by the target repository's license, provenance, maintenance, CVE, and stack-fit policy." + prerequisites: + - An initialized repository + - A concrete package and purpose + proof: "The reviewed version matches the installed version, the audit command is green at the governing threshold, and manifest and lockfile move together." --- Route every new dependency through `/ca:add-dep` before touching the package manifest. The command holds installation until the `dependency-reviewer` agent clears the package on license, provenance, and supply-chain posture. +
+ Host syntax: Claude Code uses /ca:add-dep; Codex uses + $ca-add-dep; Pi uses /ca-add-dep. Examples below use Claude Code syntax. +
+
The /ca:sprint lane in three rows: Commands (/ca:sprint), Skills (writing-plans, subagent-driven-development, commit-gate, finishing-a-development-branch), and Agents (author subagent, reviewer fleet), with a connector weaving through them in execution order. -
The /ca:sprint lane by piece type: command (gold), skills (violet), agents (green), each loaded in execution order.
+
One interactive approval opens the autonomous lane; durable plan and log state make each later cell resumable.
-## Before You Begin +
+ Host syntax: Claude Code uses /ca:sprint; Codex uses + $ca-sprint; Pi uses /ca-sprint. Examples below use Claude Code syntax. +
-Your repository must be opted in (`arbiter: enabled` in `.codearbiter/CONTEXT.md`). If it isn't yet, run `/ca:init`. The [Quickstart](/getting-started/quickstart/) walks through the opt-in. +## Before you begin -## Approve the Spec and Plan +- Start on a non-default branch or allow the sprint to create an isolated worktree. +- State the outcome, boundaries, and any spending or external-side-effect limits. +- Resolve any existing `[CONFIRM-NN]` that the goal depends on. +- Expect one review package containing both the written spec and implementation plan. -Run the sprint with your goal as the argument: +Invoke the sprint with the outcome, not a list of implementation guesses: ```text -/ca:sprint "add pagination to the /posts API endpoint: cursor-based, 50 items per page, expose Link header" +/ca:sprint "Add an export flow for saved searches, including empty-state behavior, audit records, and user documentation" ``` -codeArbiter enters the spec and plan phase and works in two passes before touching any code. +## The one interactive approval -First, it drafts a full spec from your goal and presents it for approval. Read it carefully. This is your primary opportunity to correct scope, add constraints, or reject and restate the goal. The sprint will not proceed until you approve. +codeArbiter investigates the repository and prepares two linked artifacts: -Second, it runs the [`writing-plans`](/reference/skills/writing-plans/) skill. The approved spec is decomposed into individual tasks of roughly two to five minutes each. Every task carries the exact file paths it will touch and a concrete verification step that maps to a test obligation. The plan is written to `.codearbiter/plans/.md`, ordered with dependencies flagged. You review the plan and approve or send it back. +1. **Sprint spec:** the user outcome, scope, exclusions, acceptance criteria, and hard constraints. +2. **Implementation plan:** exact tasks, files, tests, dependencies, ordering, and verification + mapped back to every acceptance criterion. -That plan approval is the last required interaction before execution begins. +It presents the spec and plan together at one STOP. Approve the package, request changes, or decline. +Implementation does not begin until both artifacts are approved in that one decision. -## Autonomous Execution +This is not two approvals. If the plan exposes a weakness in the spec, codeArbiter revises the +package and presents the combined result again. -Each task in the approved plan dispatches a fresh subagent through [`subagent-driven-development`](/reference/skills/subagent-driven-development/): test first via the `tdd` gate, then spec-compliance review, quality review, and a fresh-run verification. No single agent context accumulates state across tasks. +## What happens after approval -When the sprint must make a choice in your place, such as which approach to take for an edge case the spec didn't cover, it uses **SMARTS**: a structured, multi-lens scoring rubric. SMARTS weighs the available options, scores each against a set of lenses, picks the highest-scoring option, and flags its confidence in that call. Every auto-decision and its score lands in an append-only **sprint log**. Low-confidence calls are marked so you can find them quickly after the run. +The sprint executes the plan in resumable task cells: -Nothing hides behind the autonomy. Every choice the sprint made on your behalf is in the log. +1. derive test obligations for the next task; +2. observe the new tests fail for the intended reason; +3. implement the smallest change that satisfies them; +4. run the task's verification and reviewer checks; +5. record the accepted task state on disk; and +6. continue to the next independent cell. -## Hard Gates That Always Stop +An interrupted session resumes from the approved spec, plan, task statuses, and sprint log. It does +not re-interview you or discard accepted work. -Some decisions are never auto-decided, regardless of confidence. The sprint halts and waits when it encounters: +## How autonomous decisions work -- A finding in the security controls, including any auth, crypto, or secret-handling change -- An irreversible operation -- A gate-bypass attempt -- An unresolved decision the spec does not cover and SMARTS cannot score with sufficient confidence +For a choice that is not a hard gate, the sprint compares options with +[SMARTS](/concepts/smarts/): Scalable, Maintainable, Available, Reliable, Testable, and Securable. +The options, lens verdicts, decision, and confidence are appended to +`.codearbiter/sprint-log.md`. -These stops are rare when the spec is thorough. A sprint that halts often on hard gates is a signal that the goal needed more detail before starting, not that the gates are miscalibrated. +Low-confidence decisions are called out in the pull request so you can review the places where the +evidence was genuinely close. Spending outside your stated limit is not treated as an ordinary +SMARTS choice. -## The Pull Request +## Stops that remain yours -When the commit gate clears on the final task, [`finishing-a-development-branch`](/reference/skills/finishing-a-development-branch/) runs. Under `/ca:sprint`, it auto-selects "open PR." It will not merge to the default branch. Direct merge is prohibited. A pull request is opened and the merge decision is yours to make on your own timeline. +Autonomy never clears these boundaries: -## Route Implementation Through Farm Workers +- authentication, cryptography, secrets, or a declared security control; +- irreversible deletion or another destructive operation; +- `/ca:override`, which is itself a user-authorized bypass; +- an unresolved `[CONFIRM-NN]`; +- external spending or side effects outside the approved scope; and +- merging to the default branch. -

Preview

--farm is a Feature Forge preview feature. It ships dormant and is off by default. It has not been promoted to stable. Turn it on deliberately; do not rely on it for production workflows until promotion is tracked and recorded.

+When one hard gate affects only one task, the sprint leaves that cell stopped and continues +independent safe work. It returns to the blocked cell once the required decision exists. -The `--farm` flag routes each implementation task to a pool of lower-cost worker agents rather than the primary agent context. The review chain is identical to a standard sprint: the same commit gate, the same reviewer fleet, the same hard stops. Only the execution workers change. +## Pull request and verification -To use it: +The normal sprint exit is a pull request, not a merge. The PR should contain: -1. Set `FARM_API_KEY` in your environment to a valid farm API key. -2. Run: +- the approved spec and plan; +- acceptance-criterion to task/test coverage; +- verification commands and results; +- reviewer findings and their dispositions; +- SMARTS decisions, with low-confidence calls highlighted; and +- remaining hard-gated or out-of-scope work. + +Confirm the PR is draft when you want a deliberate merge barrier. Hosted checks still need to run +against the exact current PR head before the work is treated as ready. + +## Optional farm backend + +`--farm` replaces the normal premium-author execution with a configured OpenAI-compatible worker: ```text -/ca:sprint --farm "add pagination to the /posts API endpoint: cursor-based, 50 items per page, expose Link header" +/ca:sprint "Normalize the public API error contract" --farm ``` -The spec and plan gate runs exactly as it does without the flag. Farm routing takes over for the execution phase. - -Because `--farm` is a Feature Forge preview, its behavior may change between releases. Promotion to stable requires real-world evidence and is recorded as a deliberate tracked decision in the project, not an automatic calendar event. +The same spec package, hard gates, task checks, review, and PR boundary apply. Farm use is a Feature +Forge preview and requires provider configuration. It may transmit secret-redacted, byte-capped +task context to the endpoint you configure; read [Compatibility: Network Calls](/getting-started/compatibility/#network-calls) +before enabling it. -## Review the Sprint Log +## Verify and review -After the PR opens, read the sprint log before merging. It lists every auto-decision in order: the options considered, the SMARTS lens scores, the chosen option, and the confidence flag. The low-confidence entries are the ones worth reading first. They are where the sprint was least certain and where a brief review returns the most value. +After the PR opens: -## Related +1. read the acceptance criteria and plan delta; +2. review low-confidence SMARTS entries in `.codearbiter/sprint-log.md`; +3. confirm every required local verification is present; +4. wait for hosted checks on the exact PR head; and +5. make the merge decision yourself. -- [`/ca:sprint` command](/reference/commands/sprint/): full command signature and flags -- [`writing-plans` skill](/reference/skills/writing-plans/): how the plan is built from the spec -- [`subagent-driven-development` skill](/reference/skills/subagent-driven-development/): the per-task implementation engine -- [`finishing-a-development-branch` skill](/reference/skills/finishing-a-development-branch/): how the PR decision is handled at the end -- [Concepts: SMARTS and the Feature Forge](/concepts/): the ideas behind scored decisions and the preview model +For the exact orchestration contract, see the [`sprint` command](/reference/commands/sprint/) and +the [`subagent-driven-development` skill](/reference/skills/subagent-driven-development/). diff --git a/site/src/content/docs/guides/ca-sandbox.md b/site/src/content/docs/guides/ca-sandbox.md index f0bfb49c..7838b8d8 100644 --- a/site/src/content/docs/guides/ca-sandbox.md +++ b/site/src/content/docs/guides/ca-sandbox.md @@ -1,6 +1,14 @@ --- title: "ca-sandbox: Explore Untrusted Code" description: "What ca-sandbox is, how its isolation model holds, and how to install and use it: a locally-hosted GitHub-Codespace equivalent, not part of the governance kernel." +journey: + level: Labs + time: 15–30 min + outcome: "a disposable offline sandbox you can create, inspect, copy an artifact out of, and tear down while preserving the host-filesystem boundary." + prerequisites: + - Docker running Linux containers + - A repository URL you do not need to trust + proof: "The box runs without bind mounts or Docker socket access, copied output leaves only through the host command, and destroy reports zero labeled objects remaining." --- `ca-sandbox` is one of codeArbiter's four sibling plugins (ADR-0007). But unlike `ca`, `ca-codex`, @@ -50,23 +58,74 @@ The isolation holds by construction, not by trust: /plugin install ca-sandbox@codearbiter ``` -Then, in the target repository: +The sandbox plugin is Claude Code-specific and has its own `/ca-sandbox:` namespace. It does not +activate through `.codearbiter/`. + +## Create an Offline Box + +Start with the default offline policy: + +```text +/ca-sandbox:sandbox "https://github.com/owner/repository" +``` + +The command checks Docker, clones into a named volume, builds the dependency-cached image, and +returns a sandbox ID. Save that ID for the remaining commands. + +Before running untrusted code, inspect the reported container configuration. It should have no bind +mount, no Docker socket, no privileged mode, all capabilities dropped, a read-only root filesystem, +and `--network none`. + +## Explore and Run ```text -/ca-sandbox:sandbox create +/ca-sandbox:sandbox-shell "" +/ca-sandbox:sandbox-exec " -- npm test" ``` -## Commands +Use `sandbox-shell` for interactive exploration. Use `sandbox-exec` for a bounded, scriptable command +that returns separate stdout, stderr, exit code, and a truncation flag. + +On Git Bash, wrap container paths to prevent MSYS rewriting: ```text -sandbox create [--net offline|clone-then-cut|allowlist] -sandbox shell [--shell sh|bash] -sandbox exec -- [args...] -sandbox cp : -sandbox destroy [--keep-volume] -sandbox prune +/ca-sandbox:sandbox-exec " -- sh -c 'ls /work/repo'" ``` +PowerShell and cmd do not need that workaround. + +## Copy Out and Destroy + +Copy only the artifact you intentionally want: + +```text +/ca-sandbox:sandbox-cp ":/work/repo/report.json ./review/report.json" +``` + +The host invokes `docker cp`; the container never receives a host path. Inspect the copied file as +untrusted data before opening it with a privileged application. + +Then tear the box down: + +```text +/ca-sandbox:sandbox-destroy "" +``` + +Destroy removes the container and named volume and reports any failed Docker operation. A non-zero +result is not success: use the destroy command's `--prune` recovery path and confirm no objects with +the `ca.sandbox=1` label remain. `--keep-volume` deliberately retains source state and therefore is +not a complete teardown. + +## Failure and Recovery + +| Symptom | Likely cause | Recovery | +|---|---|---| +| Docker preflight stops | Daemon unavailable or not using Linux containers | Start Docker, confirm `docker info`, retry | +| nixpacks is unavailable | Missing host tool or Windows WSL bridge | Install nixpacks in the documented host/WSL location, or accept the narrower fallback | +| Clone succeeds but later network access fails | Offline or clone-then-cut policy is working | Use the least permissive policy that supports the task; do not jump to experimental allowlist by default | +| Exec times out | Untrusted process exceeded its bounded operation deadline | Inspect captured output, destroy the box, retry only with a justified narrower command | +| Destroy reports failures | Docker could not remove one or more labeled objects | Run the explicit prune recovery and verify the remaining-object count | + ## Not part of the governance kernel `ca-sandbox` shares the marketplace with `ca`, `ca-codex`, and `ca-pi`, and its CI is path-scoped and diff --git a/site/src/content/docs/guides/feature-lane.md b/site/src/content/docs/guides/feature-lane.md index 8de2224a..ec97ccb7 100644 --- a/site/src/content/docs/guides/feature-lane.md +++ b/site/src/content/docs/guides/feature-lane.md @@ -1,6 +1,14 @@ --- title: Build a Feature End to End description: "Take an idea through spec, plan, test-first implementation, and pull request using the /ca:feature lane." +journey: + level: Practitioner + time: 15 min + outcome: "Run feature work from one-line intent through approved spec, obligation-mapped TDD, review, commit, and PR." + prerequisites: + - An opted-in repository + - A behavior-changing product idea + proof: "You can locate each persisted artifact and name the gate that must clear before the next phase begins." --- `/ca:feature` is the one entry point for new implementation work. Run it with a one-line description of what you want built: @@ -9,6 +17,12 @@ description: "Take an idea through spec, plan, test-first implementation, and pu /ca:feature "add rate limiting to the public search endpoint" ``` +
+

Host syntax

+ Claude Code uses /ca:feature "…"; Codex uses $ca-feature "…"; + Pi uses /ca-feature "…". The persisted artifacts and gates are the same. +
+ The lane runs in order and gates at each transition. Nothing moves until the previous gate clears. Each piece loads only when the lane reaches it: the command you run, the skills it routes through, and the author and reviewer agents it dispatches on demand. @@ -24,6 +38,24 @@ and the author and reviewer agents it dispatches on demand.
The /ca:feature lane by piece type: commands (gold), skills (violet), agents (green), each loaded in execution order.
+## First: full lane, small lane, or resume + +The command classifies the request before it writes anything. + +| Path | When it applies | First durable evidence | +|---|---|---| +| **Full lane** | Any scope touch, new public surface, new dependency, more than two implementation files, or uncertainty | An approved spec under `.codearbiter/specs/` | +| **Small lane** | At most two implementation files, no sensitive or architectural scope touch, and one to three testable criteria | Your confirmed mini-spec plus one append-only `triage.log` line | +| **Resume** | A matching spec or plan already exists | The existing pipeline re-enters at its furthest completed checkpoint | + +The small lane trims ceremony, not gates: it still starts with test obligations, clears review and +commit-gate, and finishes through a PR. If the classifier is uncertain, the full lane wins. + +An interrupted run is never silently restarted. Invoke the feature command with the same description, +or invoke it bare to list resumable pipelines. A plan with unfinished tasks resumes execution; a +fully accepted plan resumes at commit-gate; an approved spec without a plan resumes planning; an +unapproved spec returns to its approval gate. + ## Refine the Idea Into a Spec The [brainstorming skill](/reference/skills/brainstorming/) runs first. It takes your one-line prompt and drives it toward a concrete, approved spec through four gated phases: frame, refine, write, approve. @@ -77,6 +109,30 @@ Once all batches are committed, run: The [pr command](/reference/commands/pr/) runs the review fleet and clears every BLOCK-level finding before the PR is staged. A PR is never written directly to the default branch. Unresolved BLOCK findings are resolved first, not deferred. +## Know that the lane is complete + +Before calling the feature done, verify all of these: + +- the spec's acceptance criteria are unchanged or explicitly re-approved; +- every plan task is `ACCEPTED`; +- each acceptance criterion maps to a passing test obligation; +- the focused checks and the repository's full verification are green on the current head; +- commit-gate created the commit from an explicitly reviewed stage; +- the PR exists and its BLOCK-level review findings are clear. + +The presence of code or a green focused test is not completion. The persisted spec, plan status, +fresh verification, commit, and PR are the proof chain. + +## Common stops + +| Stop | What it means | Resume path | +|---|---|---| +| A blocking `[CONFIRM-NN]` | The user owns an unresolved fact | Answer it, update the spec or plan, and resume the same slug | +| A task has no exact path or verification | The plan is underspecified | Return that task to writing-plans | +| The test does not fail in Red | The obligation is not demonstrated | Repair the test or the premise before implementation | +| A reviewer reports BLOCK | The batch is not accepted | Resolve the finding and rerun fresh verification | +| The session ends | Durable state remains on disk | Reinvoke feature; do not start over | + ## Related | Skill or command | Where | diff --git a/site/src/content/docs/guides/opt-in-a-repo.md b/site/src/content/docs/guides/opt-in-a-repo.md index d5b2a902..4f316d9f 100644 --- a/site/src/content/docs/guides/opt-in-a-repo.md +++ b/site/src/content/docs/guides/opt-in-a-repo.md @@ -1,6 +1,15 @@ --- title: Opt a Repository In description: "Enable codeArbiter enforcement on an existing or new repository: scaffold .codearbiter/ and set the activation flag." +journey: + level: Foundation + time: 10–20 min + outcome: "a repository-owned state store populated for the project and an activation flag that all supported hosts recognize." + prerequisites: + - A supported host adapter installed + - Python 3 on PATH + - Git identity configured + proof: "A fresh session shows the startup briefing, and doctor completes its harmless live-fire probe." --- The repository opt-in is shared by Claude Code and Codex. Run `/ca:init` in Claude Code or @@ -24,11 +33,13 @@ The plugin installs once, globally. Enabling enforcement is a per-repo step you ## 1. Scaffold the State Store -In a Claude Code session with the target repository open, run: +Open the target repository in one supported host and invoke its native command: -```text -/ca:init -``` +| Host | Command | +|---|---| +| Claude Code | `/ca:init` | +| Codex | `$ca-init` | +| Pi | `/ca-init` | `/ca:init` creates `.codearbiter/` at the repo root and routes to the right context builder for your situation. If the directory already exists, it will not overwrite what is there. @@ -60,6 +71,34 @@ Two things to verify: A file with no frontmatter at all is silently dormant. A frontmatter block that opens but never closes surfaces a malformed-state error rather than treating the repo as disabled. -Once the flag is present and the block is closed, the next Claude Code session opens with the orchestrator active and every gate armed. +Once the flag is present and the block is closed, the next supported-host session opens with the +orchestrator active and every gate armed. + +## 4. Restart and Prove Enforcement + +Close the current session and open a fresh one in the same repository. The first response should +include the codeArbiter startup briefing: stage, blocking questions, in-flight tasks, and a pointer +to the command catalog. + +Then run doctor using the same host syntax convention: + +| Host | Command | +|---|---| +| Claude Code | `/ca:doctor` | +| Codex | `$ca-doctor` | +| Pi | `/ca-doctor` | + +Doctor's harmless H-03 probe should be blocked. If the briefing is absent or the probe executes, +the repository is not proven active. Follow [Troubleshooting](/guides/troubleshooting/) before +starting governed work. + +## Common Stops + +| Symptom | Meaning | Recovery | +|---|---|---| +| Init reports an existing `.codearbiter/` directory | Project state already exists | Inspect it; do not overwrite another session's context | +| The repo has source but no initialized marker | Brownfield context is incomplete | Complete the routed create-context flow | +| Frontmatter opens but does not close | Activation state is malformed | Repair the frontmatter structure, then restart | +| Fresh session has no briefing | Plugin trust, interpreter, cache, or activation failed | Run doctor and use the symptom table in Troubleshooting | For the full catalog of what the gates enforce and how they fail, see [Enforcement & Security](/enforcement/). diff --git a/site/src/content/docs/guides/overriding-a-gate.md b/site/src/content/docs/guides/overriding-a-gate.md index a917f2c4..47edbeb3 100644 --- a/site/src/content/docs/guides/overriding-a-gate.md +++ b/site/src/content/docs/guides/overriding-a-gate.md @@ -1,6 +1,14 @@ --- title: "Override a Gate Safely" description: "Bypass a blocked gate with /ca:override, the only sanctioned path: one audit line is appended to overrides.log, your identity comes from git config user.email, and the bypass is permanent in the trail." +journey: + level: "Power user" + time: "9 minutes" + outcome: "Distinguish a repair from a justified bypass and verify the immediate, permanent audit record." + prerequisites: + - "A specific blocked gate or review finding" + - "A configured git user email" + proof: "The immediate action proceeds and exactly one attributable line appears in overrides.log." --- A gate blocked your command and the action is genuinely justified. Use `/ca:override "reason"` to bypass it. That is the only sanctioned path, since any other bypass leaves no record in the audit trail. @@ -34,11 +42,14 @@ The bypass applies only to the immediate action; future commands still hit the g ## Run the Command ```text -/ca:override "H-03: wildcard staging blocked — the generated migration file was omitted from the explicit list; reviewed the staged diff, this commit only" +/ca:override "design review: retain the deprecated v2 compatibility selector until the documented v3 removal release; supported v2 clients still depend on it, this commit only" ``` The reason must name the gate and justify the action. A vague reason such as "just skip it" is rejected. codeArbiter asks for a specific one before proceeding. +Codex uses `$ca-override "reason"`; Claude Code uses `/ca:override "reason"`; Pi uses +`/ca-override "reason"`. + ## What Happens 1. codeArbiter reads your identity from `git config user.email`. If that value is unset, it asks once. No second prompt. @@ -52,6 +63,18 @@ The reason must name the gate and justify the action. A vague reason such as "ju The override covers only the immediate action. +## Verify the scope + +After the action: + +1. inspect the last line of `.codearbiter/overrides.log`; +2. confirm its timestamp, `BY`, gate, and reason match what you approved; +3. verify only the blocked immediate action advanced; +4. invoke or preview the next governed action and confirm the original gate is active again. + +If the log line is absent, malformed, or unattributed, the bypass did not complete safely. Stop; +do not add a line by hand to make the record appear complete. + ## Security-Critical Stops These require explicit per-finding acknowledgement and a heavier log entry. The single-confirm flow is not available for any of these. diff --git a/site/src/content/docs/guides/recording-adrs.md b/site/src/content/docs/guides/recording-adrs.md index b2001885..fd93c3fd 100644 --- a/site/src/content/docs/guides/recording-adrs.md +++ b/site/src/content/docs/guides/recording-adrs.md @@ -1,62 +1,113 @@ --- -title: "Record an Architecture Decision" -description: "How to author, supersede, and monitor Architecture Decision Records with /ca:adr and /ca:adr-status." +title: Record an Architecture Decision +description: "Create, supersede, and monitor user-attributed Architecture Decision Records without rewriting prior decisions." +journey: + level: "Practitioner" + time: "10 minutes" + outcome: "Create, inspect, and supersede an attributable ADR without rewriting history." + prerequisites: + - "An initialized repository" + - "A durable architectural choice" + proof: "The ADR and decision log agree, and a superseding record names the full prior filename stem." --- -Record an Architecture Decision Record (ADR) when a choice is significant enough to constrain future work: a data-store selection, a dependency commitment, a structural boundary, or a security posture. This guide walks through authoring, superseding, and monitoring ADRs. +Use this guide when a choice will shape later implementation: a storage model, trust boundary, +public contract, dependency policy, or other decision future work must respect. The result is a +numbered record under `.codearbiter/decisions/`, attributed to the person who made the call and +linked to the paths it governs.
The /ca:adr lane in two rows: Commands (/ca:adr, then /ca:adr-status) and Skills (decision-lifecycle), with a connector running in execution order from /ca:adr through decision-lifecycle to the later /ca:adr-status sweep. -
The /ca:adr lane by piece type: commands (gold) and the skill that backs them (violet), each loaded in execution order.
+
The ADR lane gives one consequential choice a numbered, user-attributed, forward-only record.
-## Before You Start +
+ Host syntax: Claude Code uses /ca:adr; Codex uses $ca-adr; + Pi uses /ca-adr. Examples below use Claude Code syntax. +
-Confirm codeArbiter is active. The first three lines of `.codearbiter/CONTEXT.md` must include `arbiter: enabled` inside a closed frontmatter block. If it does not, run `/ca:init` first. +## Before you start -## Run /ca:adr +- Set `git config user.email`. The record and decision ledger use that identity. +- Be ready to decide the material trade-off. codeArbiter can structure and challenge the options, + but it cannot attribute its own preference to you. +- If the question is still exploratory, use `/ca:btw` or a spike first. An ADR records a decision; + it is not a scratchpad. -Invoke the command with a short description of the decision: +## Create a decision + +Invoke the command with a short title: + +```text +/ca:adr "Store durable workflow state in the repository" +``` + +The decision-lifecycle skill gathers context, the options considered, the decision, consequences, +and the paths the decision governs. Any fact only you can supply becomes a numbered +`[CONFIRM-NN]`; it is not guessed. + +Review the proposed record. On approval, codeArbiter writes: ```text -/ca:adr "Use PostgreSQL as the primary datastore" +.codearbiter/decisions/0017-store-durable-workflow-state.md ``` -The `decision-lifecycle` skill opens an interview. It asks for the context that made this decision necessary, the options you considered, the rationale behind the choice, and any consequences or constraints it carries. The skill writes nothing until you have supplied that information. Every field in the record is your input, not a generated judgment. +The number is gap-free and the slug is derived from the title. The companion +`.codearbiter/decisions/decision-log.md` receives the corresponding ledger entry. -## What the Skill Produces +## Verify the result -`/ca:adr` writes a numbered, dated, user-attributed file to `.codearbiter/decisions/`. Files follow the sequence already in that directory: `ADR-0001.md`, `ADR-0002.md`, and so on. The `author:` field names you. codeArbiter never fills in that field with its own identity. +Open the new ADR and confirm: -H-11 makes that requirement durable. Shell operations that write directly to `decisions/` are blocked at the tool-call boundary: redirects, `cp`, `rm`, and `sed -i` targeting that directory all exit non-zero. Direct `Write` and `Edit` calls to files there are blocked the same way. The skill drops a fresh authoring marker before it writes; any write attempt without that marker is rejected. There is no path to create or modify an ADR outside of `/ca:adr`. +- the status is `accepted` only if you explicitly accepted it; +- `decided-by` matches your Git identity; +- `governs` lists the intended path globs, not the whole repository by accident; +- the options and consequences reflect the choice you made; and +- any `[CONFIRM-NN]` still present also appears in `.codearbiter/open-questions.md`. -## Supersede an Outdated Decision +Run `/ca:adr-status --adr 17` (Codex: `$ca-adr-status --adr 17`) to read the decision's current +health and any challenge result. + +## Supersede a decision + +Do not edit history to make an old ADR look current. Create a new ADR and tell the command which +accepted decision it replaces: + +```text +/ca:adr "Move durable workflow state to a signed event store" +``` -When a new decision replaces an earlier one, run `/ca:adr` again and tell the skill which ADR the new one supersedes. The skill marks the earlier ADR's status as superseded and links the two records in both directions. The original file stays intact; the chain shows which decision is current. +The new record receives `supersedes: 0017-store-durable-workflow-state`, using the prior ADR's full +filename stem rather than an ambiguous bare number. The earlier file is left byte-for-byte unchanged. +Supersession is a forward-only chain: the newest record names what it replaces, and the decision +ledger records the transition. -## Check Decision Health +Verify the chain with: ```text -/ca:adr-status +/ca:adr-status --adr 17 +/ca:adr-status --adr 24 ``` -This command sweeps `.codearbiter/decisions/` and reports: +The first report should identify the newer governing decision; the second should name `0017` as its +predecessor. -- ADRs that are aging without challenge -- records with unresolved `CONFIRM-NN` markers -- decisions that are candidates for supersession +## Common stops and recovery -`/ca:adr-status` is read-only. It surfaces information and never writes. Act on its output by running `/ca:adr`. +| Stop | Why it happens | What to do | +|---|---|---| +| Git identity is missing | Decisions require real attribution | Set `git config user.email`, then invoke the command again | +| A `[CONFIRM-NN]` is unresolved | Only you can supply the missing fact | Answer it, or leave the ADR proposed until you can | +| The number or file already exists | Another decision won the next sequence number | Re-run through `/ca:adr`; never hand-renumber files | +| A direct edit is blocked | ADR files have a protected authoring lane | Use `/ca:adr` to create a replacement or superseding record | -## Related +## Next steps -- [adr](/reference/commands/adr/) command reference -- [adr-status](/reference/commands/adr-status/) command reference -- [decision-lifecycle](/reference/skills/decision-lifecycle/) skill reference -- [Enforcement](/enforcement/) (H-11: ADR authoring gate) +- Read [ADRs and the Decision Log](/concepts/adrs/) for the enforcement and JIT-context model. +- Inspect the exact [`adr` command](/reference/commands/adr/) and + [`decision-lifecycle` skill](/reference/skills/decision-lifecycle/). +- Use [Auditability](/concepts/auditability/) to see how decisions appear in a range audit. diff --git a/site/src/content/docs/guides/releasing-a-version.md b/site/src/content/docs/guides/releasing-a-version.md index 0eddca50..1915280e 100644 --- a/site/src/content/docs/guides/releasing-a-version.md +++ b/site/src/content/docs/guides/releasing-a-version.md @@ -1,133 +1,162 @@ --- -title: "Cut a Release" -description: "Tag and publish a codeArbiter version with /ca:release: SemVer derivation from Conventional Commits, CHANGELOG.md update, and authorized GitHub Release publication." +title: Cut a Release +description: "Release one codeArbiter plugin through its own tag series, manifests, changelog, built artifacts, and verified GitHub Release." +journey: + level: "Power user" + time: "15 minutes" + outcome: "Cut one target-specific release with consistent versions, artifacts, tag, and hosted checks." + prerequisites: + - "Maintainer access" + - "A clean release branch with the target's changes committed" + proof: "Manifest, changelog, tag, release artifacts, and hosted verification all name the same target version." --- -`/ca:release` is the only permitted path to a version tag. It derives the SemVer bump from Conventional Commits history since the last `ca` tag, updates `CHANGELOG.md`, composes an annotated tag locally, and publishes a GitHub Release only after you explicitly authorize it. +codeArbiter ships four independently-versioned plugins. The release lane selects one target and +keeps every version input and output inside that plugin's row. Use it only when the target's work is +already landed on a clean release branch and its verification is green.
The /ca:release lane in two rows: Commands (/ca:release) and Skills (release, then commit-gate), with a connector running in execution order from /ca:release through the release skill to commit-gate. -
The /ca:release lane by piece type: command (gold) and the skills it routes through (violet), each loaded in execution order.
+
Each release is scoped to one plugin target; publication remains a separate explicit decision.
-## Prerequisites - -Four conditions must hold before `/ca:release` will proceed: - -- **On a feature branch.** The current branch must not be the default branch (`main`, `master`, or equivalent). Releases land through the normal branch-and-PR path; if HEAD is the default branch, the release stops immediately. -- **Clean working tree.** No uncommitted changes. Commit or stash through `/ca:commit` first. -- **Green suite.** The last suite run must have passed. A red suite blocks tagging. -- **No open CONFIRM blocks.** Any unresolved `[CONFIRM-NN]` block stops the release. +
+ Host syntax: Claude Code uses /ca:release; Codex uses + $ca-release; Pi uses /ca-release. Examples below use Claude Code syntax. +
-Every `feat` and `fix` commit in the release window must carry a `CHANGELOG:` footer. A commit missing that footer is a hard block. The skill never auto-fills one. +## Choose the target -### CHANGELOG: Footer Format +| Target | Tag | Manifest(s) | Changelog | Built artifacts | +|---|---|---|---|---| +| `ca` | `vX.Y.Z` | `plugins/ca/.claude-plugin/plugin.json` | `CHANGELOG.md` | `plugins/ca/tools/farm.js` | +| `ca-codex` | `ca-codex-vX.Y.Z` | `plugins/ca-codex/.codex-plugin/plugin.json` | `plugins/ca-codex/CHANGELOG.md` | none | +| `ca-sandbox` | `ca-sandbox-vX.Y.Z` | `plugins/ca-sandbox/.claude-plugin/plugin.json` | `plugins/ca-sandbox/CHANGELOG.md` | sandbox and Claude-inside bundles | +| `ca-pi` | `ca-pi-vX.Y.Z` | `plugins/ca-pi/package.json` and generated root `package.json` | `plugins/ca-pi/CHANGELOG.md` | parent and child extension bundles | -The `CHANGELOG:` footer is part of the commit body, following the Conventional Commits trailer convention: +A bare `/ca:release` means `ca`. Prefer the explicit target in maintainer work: -``` -feat(release): resolve baseline tag through tested helper - -CHANGELOG: Tag derivation now resolves the ca baseline tag through the tested helper, excluding pre-releases and ca-sandbox tags. +```text +/ca:release ca-codex ``` -`/ca:release` rolls these footers into the new changelog section. If a `feat` or `fix` commit has no footer, it surfaces the commit and stops. +The command accepts a target, not a version. SemVer is derived from the selected payload's commit +history. -## Check Readiness Without Tagging - -Before running the full release, use `--dry-run` to see the bump classification and the commit window: - -```text -/ca:release --dry-run -``` +## Prerequisites -This runs every gate and surfaces a readiness report. It stops before composing the tag. Use it to confirm the derived version and the commit set look correct before proceeding. +Before invoking the lane: -`--dry-run` combines with an explicit version: +- check out a non-default release branch; +- leave the working tree clean; +- confirm the target has new commits in its payload since its own latest stable tag; +- confirm the last applicable suite was green; +- make sure every `feat` and `fix` commit carries a `CHANGELOG:` footer; add one to `perf` when the + performance change should appear in user-facing release notes; and +- authenticate `gh` if you expect to authorize publication later. -```text -/ca:release "X.Y.Z" --dry-run -``` +Do not run release as a readiness query. It is a mutating lane that may update manifests, changelog, +generated surfaces, and a local tag. -## Run the Release +## 1. Target-scoped pre-flight -```text -/ca:release -``` +The release skill resolves the target through the shared release register. It does not use bare +`git describe`, because the nearest tag in a multi-plugin repository may belong to a sibling. -No version argument means `--auto`: the version is derived from the commit log. Supply it explicitly when you need a specific version: +It then verifies: -```text -/ca:release "X.Y.Z" -``` +1. the last tag belongs to the target's tag series; +2. the commit window contains only changes under the target payload; +3. the window contains a SemVer-earning change; +4. the target manifest version can move to the derived version; +5. every committed bundle rebuilds without a diff; and +6. target-specific generated surfaces agree. -An explicit version that disagrees with what the commit log requires is a hard block. The bump is never silently adjusted. An explicit version and `--auto` are mutually exclusive. +For `ca-pi`, the root `package.json` is generated install metadata and must equal the plugin +manifest. For `ca`, README badges, catalog counts, and command tables are synchronized from the +repository rather than incremented by hand. -### Phase 1: Version and CHANGELOG +## 2. Derive the version and changelog -The skill reads every `plugins/ca/`-scoped commit since the last `ca` SemVer tag. Commits under `plugins/ca-sandbox/` do not affect the bump or the changelog. The highest-precedence type in the window determines the bump: +The highest-precedence change in the target payload decides the bump: -| Highest-precedence type in the window | Bump | +| Commit evidence | Bump | |---|---| -| `BREAKING CHANGE:` footer or `!` after type/scope | major | +| `BREAKING CHANGE:` or a Conventional-Commit `!` | major | | `feat` | minor | | `fix`, `perf`, or `refactor` | patch | -| `test`, `docs`, `chore`, or `ci` only | no bump; nothing to release | +| only `test`, `docs`, `chore`, or `ci` | no release; stop | -The `CHANGELOG:` footers from `feat`, `fix`, and `perf` commits are appended to `CHANGELOG.md` under a new Keep-a-Changelog heading: +The same window supplies the changelog entries. Every `feat` and `fix` requires a `CHANGELOG:` +footer because release notes must not omit a user-visible change or invent one after the fact. +A `perf` footer is rolled when present. A `refactor` earns a patch version but does not require or +synthesize a user-facing changelog entry. -``` +The selected changelog receives: + +```markdown ## [X.Y.Z] — YYYY-MM-DD ### Added -- ... -### Fixed - ... ``` -Prior sections stay intact. If `CHANGELOG.md` does not exist, the skill creates it. The README version badge, command/skill/agent count badges, and the `COMMANDS.md` catalog are also updated to match the repo. If those changes require a commit before tagging, it routes through commit-gate. +Any manifest, changelog, or generated-surface update lands through the normal commit gate before +tagging. -### Phase 2: Tag +## 3. Compose the local tag -The annotated tag is composed locally from the Phase 1 changelog section. The tag is never pushed without authorization. Phase 2 ends with a report: version, bump rationale, per-commit classification, the new changelog section, and the tag SHA. +The skill writes a multi-line annotated tag in the selected namespace and reports: -### Phase 3: Publish (Requires Authorization) +- target and payload window; +- previous and next version; +- per-commit classification; +- changelog section; +- rebuilt artifact status; and +- local tag SHA. -The tag and GitHub Release publish together, and only after you authorize them. Without authorization, nothing leaves the local repository. +If the tag already exists at HEAD with the matching manifest version but has no GitHub Release, the +lane classifies that as a resumable publish rather than composing another tag. -## Review and Authorize +## 4. Review and authorize publication -Read the Phase 2 report. When the version, classification, and changelog section look correct, authorize publication. +Nothing leaves the repository until you explicitly authorize publishing. On authorization, the +lane: -On authorization: +1. pushes the selected tag; +2. verifies the release-notes heading matches that tag; +3. creates the GitHub Release from the exact changelog section; +4. passes `--latest=false` for every sibling series; +5. reads the release back and requires a non-draft result on the expected tag; and +6. records the remote tag SHA and dereferenced commit in `.github/published-tags.json`. -1. The tag is pushed to the remote. -2. A GitHub Release is created. The notes are the Phase 1 changelog section verbatim; they are never re-derived or hand-written. -3. The Release is read back via `gh release view` to confirm it is non-draft and on the correct tag. If `gh` is unavailable or the call fails, the skill surfaces the exact manual command so you can finish publication by hand rather than leave it in an incomplete state. +Only a `ca` release may take GitHub's single repo-wide Latest badge, and only when it is actually +the newest release across all four series. -The Release URL is reported only after the read-back confirms a non-draft Release on the correct tag. +## Common stops -## Common Blocks - -| Situation | Effect | Resolution | +| Stop | Meaning | Recovery | |---|---|---| -| A `feat` or `fix` commit has no `CHANGELOG:` footer | Phase 1 stops; the commit is surfaced | Add the footer to the commit; re-run | -| Explicit version disagrees with the commit log | Hard block | Use `--auto` or supply the correct version | -| Suite is not green | Pre-flight stops | Fix the failing tests; re-run | -| Window is non-bumping (`test`/`docs`/`chore`/`ci` only) | Phase 1 stops | Nothing user-visible to release | -| Dirty working tree | Pre-flight stops | Commit or stash via `/ca:commit` | -| HEAD is on the default branch | Pre-flight stops | Switch to a feature branch | +| Unknown target | No release row exists | Invoke one of the four exact target names | +| Empty or non-bumping window | This payload has nothing releasable | Land a bump-earning change or do not release | +| Manifest mismatch | The tag would advertise a version the payload does not report | Update the target surfaces through commit-gate | +| Bundle diff after rebuild | The repository would ship stale generated code | Commit the rebuilt artifact, verify, then re-run | +| Missing required `CHANGELOG:` footer on `feat` or `fix` | Release notes would silently omit a user-visible change | Correct the commit history through the governed path | +| Publish read-back fails | Tag/Release may be partially published | Keep the exact state and resume the missing step | + +## Recover from a bad published release -Any block may be bypassed only through `/ca:override`. +Never move, retarget, or delete a published tag. Consumers can pin exact tags; changing the object +behind a published version makes verification history describe different code. -## Related +Fix the defect through a normal PR, invoke `/ca:release ` again, and publish the next +version. Mark the bad GitHub Release as a prerelease and add a note naming the superseding version. +The old tag remains the immutable identity of what shipped. -- [release command reference](/reference/commands/release/): arguments and hard gates -- [release skill reference](/reference/skills/release-2/): full phase-by-phase specification +For the exact target register and every hard rule, read the +[`release` command](/reference/commands/release/) and [`release` skill](/reference/skills/release/). diff --git a/site/src/content/docs/guides/the-statusline.md b/site/src/content/docs/guides/the-statusline.md index 8855c598..427473ad 100644 --- a/site/src/content/docs/guides/the-statusline.md +++ b/site/src/content/docs/guides/the-statusline.md @@ -1,10 +1,19 @@ --- title: "Set Up the Statusline" description: "What codeArbiter's statusline shows, and how to wire it into Claude Code so every session shows usage metrics and enabled repos show the active project state." +journey: + level: "Practitioner" + time: "10 minutes" + outcome: "Wire, read, diagnose, and remove the Claude Code statusline safely." + prerequisites: + - "Claude Code with the ca plugin installed" + proof: "Status reports the codeArbiter-owned command, a fresh render shows live values, and uninstall restores prior settings." --- -> **Claude Code only.** Codex has no statusline surface. Its SessionStart briefing presents the -> shared `.codearbiter/` governance state instead; this is an intentional host difference. +> **Claude Code only for this settings-wired bar.** Codex has +> no statusline surface and presents the shared `.codearbiter/` state in its SessionStart briefing. +> Interactive Pi sessions install codeArbiter's rich footer automatically and restore Pi's native +> footer on shutdown or when the custom footer cannot initialize. codeArbiter ships a token-aware statusline for Claude Code: a compact, full-width box that shows your session usage in every repo and, in an [arbiter-enabled](/glossary/#arbiter-enabled-flag) one, the live project state. It is @@ -182,3 +191,5 @@ Open the file in a text editor, restore valid JSON, then re-run `/ca:statusline` --- For the `statusline.py` renderer and `wire-statusline.py` script reference, see [Hooks reference: non-event scripts](/hooks/#non-event-scripts). +For Pi's automatic rich-footer behavior and its fail-soft native fallback, see +[Pi host support](/getting-started/pi/). diff --git a/site/src/content/docs/guides/troubleshooting.md b/site/src/content/docs/guides/troubleshooting.md index 9d5ac2eb..37bb5604 100644 --- a/site/src/content/docs/guides/troubleshooting.md +++ b/site/src/content/docs/guides/troubleshooting.md @@ -1,6 +1,13 @@ --- title: Troubleshooting description: "Use /ca:doctor or $ca-doctor to diagnose dormant gates, persona loading, trust, payload, and host-specific installation problems." +journey: + level: Power user + time: Symptom-driven + outcome: "a deterministic diagnosis path from visible symptom to activation, interpreter, cache, hook, or host-adapter cause." + prerequisites: + - Run doctor first when possible + proof: "You can distinguish installed files from active enforcement and choose the smallest remediation that restores a live hook proof." --- Run `/ca:doctor` in Claude Code or `$ca-doctor` in Codex when codeArbiter is not behaving as @@ -23,9 +30,11 @@ Doctor prints a result for each check and exits 0 if all pass, non-zero otherwis ### Interpreter Health -Doctor confirms that at least one Python interpreter resolves. codeArbiter registers every hook twice in `hooks.json`: once under `python3`, and once under a `python3 -c "" || python` fallback. On a stock Windows machine with only `python` on PATH, the fallback fires and the gates still run. If neither name resolves, doctor warns loudly: the gates are dormant and no enforcement is active. - -Interpreter failure is the most common cause of a silent install. +Doctor confirms that the interpreter selected by the active adapter resolves. Claude Code carries +its documented `python3`/`python` fallback shape. Codex uses OS-specific hook handlers and reports a +loud failure if the selected interpreter is absent. Pi keeps its TypeScript wrappers active and +blocks mutating calls with an interpreter breadcrumb until the Python bridge is healthy. In every +case, a failed interpreter row means governance is not healthy. **To fix:** add Python 3 to PATH. Verify outside Claude Code with `python3 --version` or `python --version`. At least one must succeed. @@ -111,7 +120,9 @@ Pi has several distinct silent-inactivity states that look alike but have differ | Symptom | Likely cause | Suggested check | |---------|--------------|-----------------| -| Gates don't fire in any repo | `python3` and `python` both absent from PATH | Doctor's interpreter section; verify with `python --version` in a shell | +| Claude gates don't fire in any repo | neither registered interpreter resolves | Claude doctor's interpreter section; verify `python3 --version` or `python --version` in a shell | +| Codex hook handler fails before evaluating a gate | its OS-specific Python command does not resolve | Codex doctor's interpreter section and the exact handler error | +| Pi mutation is blocked with an interpreter breadcrumb | Python bridge is unavailable | `/ca-doctor`, then add Python 3 to `PATH` and start a fresh session | | Gates don't fire in one specific repo | `arbiter: enabled` missing or frontmatter unclosed | Doctor's repo activation section; inspect `.codearbiter/CONTEXT.md` | | Orchestrator persona not loading | Repo not opted in; `SessionStart` finds no activation flag | Doctor's repo activation section; run `/ca:init` if the file is absent | | Stale behavior after a plugin update | Cached payload or statusline path is outdated | Doctor's stale-cache and statusline sections | diff --git a/site/src/content/docs/guides/uninstalling.md b/site/src/content/docs/guides/uninstalling.md index fcab1693..5ae33eed 100644 --- a/site/src/content/docs/guides/uninstalling.md +++ b/site/src/content/docs/guides/uninstalling.md @@ -1,6 +1,13 @@ --- title: "Uninstall & Disable" description: "How to turn codeArbiter off in one repository, or remove it entirely: the activation flag, the plugin uninstall command, the statusline, and the git-hooks backstop." +journey: + level: "Practitioner" + time: "12 minutes" + outcome: "Disable one repository or remove a host plugin without losing project records or another tool's git hooks." + prerequisites: + - "Know whether you want repository dormancy, one-host removal, or complete removal" + proof: "A fresh session is dormant or the host plugin is absent, retained state is intentional, and only codeArbiter-owned hook shims were removed." --- codeArbiter's pitch is enforcement, so it documents the exit. This page covers three levels: turning @@ -8,13 +15,25 @@ it off in one repository, removing it globally, and what to check before uninsta Claude Code and Codex share `.codearbiter/`, so removing either plugin leaves project context and audit history available to the other host. +## Choose the exit you actually want + +| Goal | Action | Repository records | +|---|---|---| +| Pause governance in one repository | Change only that repository's activation flag | Kept | +| Stop using one host | Uninstall only that host's plugin | Kept and usable by another host | +| Remove codeArbiter from the machine | Remove every installed host plugin, the Claude statusline, and managed git-hook shims | Kept unless you separately archive and delete them | +| Erase project governance history | Archive, review, then delete `.codearbiter/` as a separate destructive action | Deleted | + +Do not delete `.codearbiter/` merely to uninstall a plugin. It contains project-owned history, not +plugin cache. + ## Disable in One Repository Enforcement in a repository is controlled by a single flag: `arbiter: enabled` in `.codearbiter/CONTEXT.md` frontmatter. Every enforcement hook checks that flag through `arbiter_active()` and exits immediately, without running any gate logic, when it is absent or set to anything other than `enabled`. Turning it off makes the plugin genuinely dormant in that repository: the orchestrator persona never loads, and -every `PreToolUse`/`PreToolUse`-guarded hook returns before doing anything. +every `PreToolUse`-guarded hook returns before doing anything. The flag is deliberately hard to flip from inside a session, by design. **H-18** blocks a shell redirect, `Write`, or `Edit` that would change `arbiter: enabled` to `arbiter: disabled` (or otherwise @@ -129,6 +148,12 @@ Consider whether the audit trail in `.codearbiter/overrides.log` and `.codearbit value to keep even after you stop using the plugin day to day: it's a plain-file record, readable without codeArbiter installed. +Before deletion, confirm `git rev-parse --show-toplevel` names the intended repository, inspect +`git status --short -- .codearbiter`, and copy any audit or decision records you must retain. On +Windows PowerShell the scoped equivalent is `Remove-Item -LiteralPath .codearbiter -Recurse` from +the verified repository root. This deletion is not required for dormancy or plugin removal and is +not recoverable unless Git or your archive contains the files. + ### 3. Remove the Statusline If you wired the statusline with /ca:statusline, remove it **before** uninstalling the @@ -204,3 +229,17 @@ what to check first. 3. If a commit is staged but hasn't cleared `commit-gate`, either finish the commit through the plugin first or be aware you're now committing without the gate's verification, secret-scan, and behavioral-proof checks. Nothing enforces those once the plugin is gone. + +## Final verification + +Start a fresh session after the chosen exit: + +- for repository dormancy, confirm no orchestrator startup state appears and a read-only status check + does not report an active arbiter; +- for one-host removal, confirm that host no longer lists the plugin and that another installed host + can still read the retained `.codearbiter/` state; +- for full removal, confirm the Claude statusline no longer points into the plugin cache and inspect + `pre-commit` and `pre-push` before removing only files with the codeArbiter sentinel. + +The proof is a fresh-session observation plus an intentional decision about retained repository +state, not merely a successful uninstall command. diff --git a/site/src/content/docs/hooks.md b/site/src/content/docs/hooks.md index d671b934..350a4ecb 100644 --- a/site/src/content/docs/hooks.md +++ b/site/src/content/docs/hooks.md @@ -1,6 +1,13 @@ --- title: Hooks Reference description: "A complete per-hook reference for codeArbiter: every registered hook across the three governance hosts, the gates it enforces, its fail posture, and the non-event scripts behind the commands." +journey: + level: "Reference" + time: "10 minutes" + outcome: "Locate a hook by event, entry point, or gate id and trace it to source." + prerequisites: + - "Enforcement & Security" + proof: "You can take one live hook message and find its event, implementation file, and detailed gate entry." --- The `ca`, `ca-codex`, and `ca-pi` plugins share the same Python guard core and `.codearbiter/` @@ -90,7 +97,8 @@ For the exact, word-for-word message text a hook prints for every gate ID (gener - **H-05:** a Write is a full overwrite, so any Write to an audit log (`overrides.log`/`triage.log`/`sprint-log.md`/`gate-events.log`) is blocked (append with Edit or `>>`). - **H-11:** a Write to any `.md` under `decisions/` is blocked unless a fresh `adr-authoring-active` marker is present (set by `/ca:adr`). - **H-18 / H-19:** a Write that would disable the `CONTEXT.md` activation switch, or a Write to a `.codearbiter/.markers/` gate-pass token, is blocked (the same integrity rules pre-bash enforces on the shell flank). -- **Why:** Closes the Write flank of the audit-trail, ADR-authoring, activation-switch, and gate-marker integrity rules. + - **H-21:** a host write envelope that cannot be decomposed into guardable per-file operations is blocked. Retry as a plain supported patch or split the operation; an opaque envelope never passes uninspected. +- **Why:** Closes the Write flank of the audit-trail, ADR-authoring, activation-switch, gate-marker, and opaque-envelope integrity rules. - **Fail posture:** Blocking (exit 2). --- @@ -103,7 +111,8 @@ For the exact, word-for-word message text a hook prints for every gate ID (gener - **H-05:** on an audit log, MultiEdit is blocked outright (cannot express a verifiable append), an Edit with an empty `old_string` is blocked (it can never be a pure append), a `replace_all` Edit is rejected outright, and an Edit is admitted only as a strict **tail append**: `new_string` must equal the current content plus an appended tail, with `old_string` occurring exactly once. This closes the earlier hole where a mid-file insertion or a multi-site suffix rewrite passed as an "append". - **H-11:** the same fresh `adr-authoring-active` marker requirement for `decisions/` `.md` files. - **H-18 / H-19:** the same activation-switch and gate-marker protections as the Write flank. -- **Why:** Closes the Edit/MultiEdit flank; an append-only log accepts only verifiable tail appends. + - **H-21:** an edit operation that cannot be normalized into guarded per-file operations is blocked and must be retried in a supported, decomposable form. +- **Why:** Closes the Edit/MultiEdit flank; an append-only log accepts only verifiable tail appends, and an opaque target set is never assumed safe. - **Fail posture:** Blocking (exit 2). --- diff --git a/site/src/content/docs/index.mdx b/site/src/content/docs/index.mdx index 68d4b0a8..84dbad92 100644 --- a/site/src/content/docs/index.mdx +++ b/site/src/content/docs/index.mdx @@ -1,215 +1,255 @@ --- -title: Arbitration for Claude Code, Codex, and Pi Workflows -description: Shared enforcement and project-context parity across Claude Code, Codex, and Pi. -template: doc +title: Hard Gates for Agentic Coding +description: "One repository-owned governance layer for Claude Code, Codex, and Pi: real stops, durable project context, and an audit trail that survives the host you use." +template: splash tableOfContents: false editUrl: false prev: false next: false --- -import GateCatchTerminal from '../../components/GateCatchTerminal.astro'; -import InstallTerminal from '../../components/InstallTerminal.astro'; +import HookProof from '../../components/HookProof.astro'; import TrustRow from '../../components/TrustRow.astro'; +import ArbiterIcon from '../../components/ArbiterIcon.astro'; +import heroArt from '../../assets/hero-gates.webp';
-
-
-

Arbitration for Claude Code, Codex, and Pi Workflows

-
- Shared enforcement and project-context parity across Claude Code, Codex, and Pi. - Tests, reviews, and security checks are real stops in any host. -
-
- Get started - How it works +
+
+

Governed autonomy for serious repositories

+

Hard gates for agentic coding.

+

One repository-owned governance layer for Claude Code, Codex, and Pi. Tests, reviews, security checks, decisions, and project context survive the host you use.

+

You decide. codeArbiter enforces.

+ +
+ Claude Code + Codex + Pi preview
-
-

A gate catching a real mistake

- -
- Not a description of gates. A gate doing its job. +
+
+
+

Direct hook evidence

+

A broad stage meets the shipped guard

+
+ Shipped H-03 hook
+ +

The exact stderr and audit effect from a direct invocation of plugins/ca/hooks/pre-bash.py, rendered as a faithful replay. The denied staging command never ran. This evidence verifies the shipped guard itself, not host discovery or registration; the quickstart's doctor probe verifies that end-to-end path.

-
+
-
-
- Three governance hosts, one shared project store. Gated lanes for every kind of work. +
+
+

Measured, not hand-typed

+

The shipped payload is the source of truth.

- -
- Lane flow: a command routes to the owning skill, clears its gate pillars, ships to version control, then opens a pull request to reach main. -
Every request flows: command → route → gate → ship → pull request. Nothing skips the gate.
-
- -
-
- -
- -
-

Get the Arbiter on Your Case

-

Choose Claude Code or Codex, shown here. All three governance hosts enforce the same project state. ca-pi is available as a Feature Forge preview, with real use and feedback welcome.

-
+ +
+ +
+
+

From first install to confident operation

+

A curriculum, not a pile of pages.

+

Follow one progressive route, perform a small exercise at each stage, and finish with a power-user capstone you can prove from repository state.

- -
- The codeArbiter statusline: a folder row; a git row with repo, branch, and a model pill; rate-limit percentages with reset countdowns; an arbiter row (green dot) showing stage, tasks, open questions, and overrides; and session/today token, cost, burn, and context-usage segments. -
Claude Code's optional statusline. Codex presents governance state at SessionStart.
-
-
- -
-
-

Gates are hooks, not suggestions

-
- Every codeArbiter gate runs at its host's hook - boundary before the tool call. The shared Python guard returns one verdict; the - Claude Code and Codex adapters surface that verdict in their native hook protocols. -
-
- Prompt-layer rules still matter. They are the model's judgment for everything a - hook does not reach. Hooks are the backstop for the moment that judgment slips: a - blocking gate does not ask the model to agree, it stops the call. -
-
- Every block names the gate that caught it. A BLOCKED [H-09b] line in - your terminal points at an entry in the hook gates reference, with the exact - condition and the exact message the hook prints. -
+ - -
-

How a gate fires

-
    -
  1. A shell or write call reaches the host's PreToolUse boundary.
  2. -
  3. The matching hook runs first, against the repository's current state.
  4. -
  5. It allows the call or blocks with a BLOCKED [H-xx] message.
  6. -
- + Start the complete learning path + + +
+
+

What changes when codeArbiter is active

+

Autonomy with evidence attached.

+

Every guarantee is backed by a mechanism you can inspect.

-
-
-

Who this is for

-
-
-

Built for

-
    -
  • Teams and solo developers who want an audit trail, not a changelog written after the fact.
  • -
  • Compliance-minded adopters who need to show a control actually ran.
  • -
  • Anyone who has watched an agent commit untested code and wants that failure mode closed.
  • -
  • Brownfield repos that need governance layered on, not a rewrite.
  • -
-
-
-

Not built for

-
    -
  • A quick throwaway script you plan to delete in an hour.
  • -
  • Anyone who finds any gate friction unacceptable, even a fixable one.
  • -
  • A repo where you do not want a .codearbiter/ state directory checked in.
  • -
-
+
+
+ +

Stops at the tool boundary

+

Host hooks run before shell and write calls. A blocking verdict prevents the call; it does not depend on the model remembering a prompt.

+ Inspect enforcement +
+
+ +

State belongs to the repository

+

Specs, plans, decisions, tasks, questions, and audit records live in .codearbiter/. Change hosts without creating parallel memory.

+ Explore the state store +
+
+ +

Bypasses leave a trail

+

Overrides, autonomous decisions, and checkpoint findings are durable artifacts. Audit a range from source records instead of reconstructing it later.

+ See the audit model +
-
- - - -
-
-

Docs source map

-

Update the right layer

-
- The site mixes handcrafted pages with generated reference. Edit the source - that owns the page, or the next generator run will overwrite the wrong change. -
+ + +
+
+

How work moves

+

Ask for the outcome. Keep the proof.

+

codeArbiter routes intent into the lane that owns it, scales the gates to the risk, and leaves a resumable record on disk.

+ Understand gated lanes
- -
-
-

Handcrafted docs

-
- Guides, concepts, install, FAQ, and this landing page live under - site/src/content/docs/. These pages are safe to tune directly. +
    +
  1. + 1 +
    +

    State the work

    +

    Feature, fix, sprint, dependency, decision, review, or release.

    -
- -
-

Generated reference

-
- Command, skill, agent, hook-gate, and changelog pages come from - site/scripts/gen.ts, plugins/ca/, and - site/src/curated/. Change those inputs, then regenerate. + +
  • + 2 +
    +

    Clear the lane

    +

    Specs, tests, reviewers, and security checks appear only when the change needs them.

    -
  • - -
    -

    Root context

    -
    - Repo-level docs such as README.md, CHANGELOG.md, - and docs/*.md stay source-of-truth material. The site should - point at them or render from them, not drift into a fork. + +
  • + 3 +
    +

    Ship through a PR

    +

    Commit evidence and review state travel with the branch. The default branch is never a direct write.

    -
  • + + +
    + +
    +
    +

    Start from the job, not the machinery

    +

    A clear entry point for the work in front of you.

    -
    + + -
    -

    Three ways in

    -
    -
    -

    Learn

    -

    Understand what a gate is and why the lane model exists before you touch a command.

    - -
    -
    -

    Use

    -

    Get codeArbiter installed and take your first change through a gated lane.

    +
    +
    +

    Choose your host

    +

    One policy core. Native entry points.

    +

    The project state is shared; command spelling and host capabilities are documented honestly.

    +
    +
    +
    +
    +

    Claude Code

    + stable +
    + /ca:feature +

    Marketplace install, native plugin agents, hook trust flow, and optional rich statusline.

    + Install for Claude Code +
    +
    +
    +

    Codex

    + stable +
    + $ca-feature +

    The same enforcement decisions and state through Codex-native skills and hook verdicts.

    + Install for Codex +
    +
    +
    +

    Pi

    + preview +
    + /ca-feature +

    Git-installed adapter with project trust, a rich footer, and supervised child dispatch.

    + Install the Pi preview +
    +
    +

    Need the exact differences? Read the compatibility matrix and the dated Claude Code + Codex verification record.

    +
    + +
    +
    +

    Fit before friction

    +

    Built for work that has to remain explainable.

    +

    codeArbiter adds deliberate process. That pays off when the repository outlives the current session, more than one person or host touches it, or a security and delivery claim needs evidence.

    +
    +
    +
    +

    Use it when

    -
    -

    Look up

    -

    Find the exact command, gate ID, or hook behavior you are checking against.

    +
    +

    Skip it when

      -
    • All reference
    • -
    • Hook gates
    • -
    • Glossary
    • +
    • the code is a disposable one-hour experiment;
    • +
    • any process friction is unacceptable;
    • +
    • the repository cannot carry a checked-in state directory.
    -
    - Turning it off is documented too: disable per-repo with one frontmatter flag, or - uninstall entirely. +
    + +
    +
    +

    Open source · AGPL-3.0-only · local enforcement

    +

    Protect a repository in five minutes.

    +

    Install for one host, opt the repository in, then run the live doctor probe.

    -
    + +

    No telemetry service is required for enforcement. Disable one repository or uninstall completely with the state implications documented.

    +
    diff --git a/site/src/content/docs/learn/index.mdx b/site/src/content/docs/learn/index.mdx new file mode 100644 index 00000000..4c3cd0d0 --- /dev/null +++ b/site/src/content/docs/learn/index.mdx @@ -0,0 +1,144 @@ +--- +title: The codeArbiter Learning Path +description: "A progressive, hands-on route from first install to confident operation, diagnosis, customization, audit, and release." +journey: + level: Foundation + time: 60–90 min total + outcome: "a working repository, a mental model of every major lane, and a capstone checklist that proves you can operate codeArbiter without guesswork." + prerequisites: + - Git installed + - Python 3 on PATH + proof: "Complete the power-user capstone at the end of this page." +--- + +import ArbiterIcon from '../../../components/ArbiterIcon.astro'; + +export const base = `${import.meta.env.BASE_URL}/`; + +You do not need to understand codeArbiter’s internals before using it. Follow this path in order, +perform the short exercise in each stage, and keep the proof it asks you to produce. The reference +stays available for lookup; the path teaches you when and why to use it. + + + +## Stage 1: Foundation + +
    +
    + +
    +

    Target: a verified install

    +

    Know what is enforcing your repository.

    +
    +
    +

    Choose one host, install its adapter, opt a disposable repository in, and prove the blocking hook actually runs. This is the minimum trustworthy starting point.

    +
      +
    1. Understand the promise and request flow5 min
    2. +
    3. Choose Claude Code, Codex, or Pi5 min
    4. +
    5. Install and trust the adapter10 min
    6. +
    7. Protect a disposable repository15 min
    8. +
    +
    Proof Doctor reports a healthy H-03 live-fire probe, and $ca-status or its host-native equivalent names your branch, stage, tasks, and questions.
    +
    + +## Stage 2: Daily operation + +
    +
    + +
    +

    Target: choose the right lane

    +

    Turn ordinary development work into repeatable evidence.

    +
    +
    +

    Learn the difference between new behavior, a known defect, investigation, dependency risk, and an architectural decision. The power move is choosing the smallest correct lane before work begins.

    +
      +
    1. Learn the gated-lane model8 min
    2. +
    3. Build a feature end to end15 min
    4. +
    5. Fix a confirmed defect test-first5 min
    6. +
    7. Add a dependency safely10 min
    8. +
    9. Record an architecture decision10 min
    10. +
    11. Use an override without hiding it5 min
    12. +
    +
    Proof Given a feature, a failing behavior with unknown cause, and a known regression, you can explain why they route to feature, debug, and fix respectively.
    +
    + +## Stage 3: Governed autonomy + +
    +
    + +
    +

    Target: delegate without losing control

    +

    Approve the destination, then audit the decisions.

    +
    +
    +

    An autonomous sprint is not a longer prompt. It has one interactive spec-and-plan gate, persisted tasks, SMARTS-scored non-hard decisions, hard stops, exact-head review, and a PR boundary.

    +
      +
    1. Run a resumable sprint15 min
    2. +
    3. Read and challenge SMARTS verdicts8 min
    4. +
    5. Use checkpoints for periodic review8 min
    6. +
    7. Detect context drift8 min
    8. +
    9. Assemble the audit story8 min
    10. +
    +
    Proof You can locate the approved spec, execution plan, task board, sprint decision log, low-confidence calls, and exact PR head without relying on chat history.
    +
    + +## Stage 4: Power user + +
    +
    + +
    +

    Target: diagnose and extend

    +

    Operate the system from source-backed evidence.

    +
    +
    +

    Power users do not memorize every hook or environment variable. They know where state lives, how to trace a verdict to source, how to recover a stale install, and which changes require a specialist lane.

    +
      +
    1. Read the repository-owned state store10 min
    2. +
    3. Configure scope and runtime behaviorLookup
    4. +
    5. Trace a hook from event to verdict10 min
    6. +
    7. Understand blocking and fail-loud behavior10 min
    8. +
    9. Diagnose installation and runtime failures10 min
    10. +
    11. Cut a target-aware release10 min
    12. +
    13. Evaluate previews without confusing them with stable features5 min
    14. +
    +
    Proof Starting from a hook ID, you can find its event, blocking behavior, implementation source, relevant configuration, remediation path, and durable audit record.
    +
    + +## Power-user capstone + +Use a disposable repository. Do not perform this exercise in active production work. + +
    +

    Final exercise

    +

    Prove the entire operating loop.

    +
      +
    1. Run doctor and save the live-fire result.
    2. +
    3. Use status to identify the stage, current branch, one task, and one open question.
    4. +
    5. Explain which lane owns a new feature, a confirmed bug, a risky dependency, and a structural refactor.
    6. +
    7. Locate the hook-gate entry for H-03 and the source file that emitted your block.
    8. +
    9. Find the configuration reference for migration, CI, and deployment scope.
    10. +
    11. Describe what an override records and what authority it does not grant.
    12. +
    13. Trace one sprint decision from the log to its SMARTS evidence and confidence flag.
    14. +
    15. Explain why a green local test is not proof that a pull request is safe to merge.
    16. +
    +

    You are a codeArbiter power user when you can complete all eight from the repository and docs—without reconstructing the answer from a prior chat.

    +
    + +## Keep this path honest + +The docs intentionally separate three kinds of evidence: + +- **Captured product evidence** comes from a real shipped path and includes its source digest. +- **Generated reference** comes from current plugin frontmatter and source files on every build. +- **Conceptual diagrams** explain relationships; they are not presented as runtime captures. + +When those disagree, trust the live source and file an issue. The site should make the discrepancy +obvious, never smooth it over. diff --git a/site/src/content/docs/overview.md b/site/src/content/docs/overview.md index 89426923..4c6abf52 100644 --- a/site/src/content/docs/overview.md +++ b/site/src/content/docs/overview.md @@ -1,6 +1,12 @@ --- title: What Is codeArbiter description: How codeArbiter orchestrates shared gated workflows in Claude Code, Codex, and Pi. +journey: + level: "Foundation" + time: "6 minutes" + outcome: "Explain the command-to-skill-to-agent flow and choose the next learning module." + prerequisites: [] + proof: "You can identify who routes, who implements, who reviews, and who resolves a gate." --- codeArbiter ships four sibling plugins from one marketplace. Three are governance hosts: `ca` for diff --git a/site/src/curated/commands/cleanup.md b/site/src/curated/commands/cleanup.md new file mode 100644 index 00000000..add443a6 --- /dev/null +++ b/site/src/curated/commands/cleanup.md @@ -0,0 +1,50 @@ +--- +entity: commands/cleanup +related: [commands/pr, commands/standup, commands/status, skills/post-merge-cleanup] +gates: + - gate: fetched ancestry proof + when: before any local artifact or branch can be removed + effect: fetches the default branch and stops unless the current HEAD is proven contained in that fetched ref + - gate: residue classification + when: the working tree or stash list contains leftover material + effect: every item is classified with evidence; uncertainty is treated as unique rather than disposable + - gate: per-item consent + when: any file, artifact, or local branch is offered for removal + effect: each target is named and confirmed separately; there is no batch approval +--- + +## What it does + +Use this after a pull request has merged but your local checkout is still on the topic branch. +`cleanup` proves that the branch's work reached the fetched default branch, inventories everything +left locally, and walks back to a clean default checkout without treating an upstream marked +`gone` as proof. + +## Usage + +```text +/ca:cleanup +``` + +The command takes no arguments. Run it from the merged topic branch. Codex uses `$ca-cleanup`; Pi +uses `/ca-cleanup`. + +## What you decide + +codeArbiter explains whether each leftover file is reproducible, replaced by landed work, or the +only remaining copy. You approve or decline each removal by name. A stash is reported but never +dropped. After the tree is safe, the default branch is checked out and fast-forwarded, and the +merged local branch is offered for deletion with `git branch -d`. + +## Successful exit + +The receipt names the fetched default ref used for proof, what was removed, what was kept, the +current checkout, and whether the local topic branch remains. Declining any deletion is a valid +result. + +## When to use another command + +- The PR has not merged: use `/ca:pr`. +- The current work needs committing: use `/ca:commit`. +- You want a read-only repository summary: use `/ca:status`. +- You want broad daily hygiene across other branches and worktrees: use `/ca:standup`. diff --git a/site/src/curated/commands/release.md b/site/src/curated/commands/release.md index 64afd106..940c7e94 100644 --- a/site/src/curated/commands/release.md +++ b/site/src/curated/commands/release.md @@ -1,54 +1,59 @@ --- entity: commands/release -related: [commit, pr, skills/release] +related: [commands/commit, commands/pr, skills/release] gates: - - gate: version derivation + - gate: target resolution + when: before release state is inspected + effect: selects exactly one plugin row and stops on an unknown target rather than inferring one + - gate: target-scoped version derivation when: before a tag is composed - effect: the SemVer bump is computed mechanically from Conventional Commits since the last tag — an explicit version that disagrees with that classification blocks rather than being silently adjusted + effect: only commits in the selected payload and tag series influence the SemVer bump and changelog - gate: changelog completeness - when: same phase as version derivation - effect: every feat/fix/perf commit in the release window must carry its own changelog note; a missing one blocks rather than being auto-filled + when: version and release notes are assembled + effect: every feat and fix must carry its own changelog note; perf notes roll when present and refactor notes are never invented - gate: publish authorization - when: after the tag is composed locally - effect: the tag and the GitHub Release publish together, and only once you explicitly say to + when: after the annotated tag and report exist locally + effect: the tag and GitHub Release publish together only after explicit approval --- ## What it does -This is the only sanctioned way a version tag gets created. It never invents a version number — it -walks every commit since the last tag, classifies each by its Conventional Commits type, and applies -whichever bump that history actually earns (a breaking change beats a feature, a feature beats a -fix). The changelog section is assembled the same mechanical way, pulled from the commit footers -rather than freehand summary, and a commit that should have carried one but didn't is a hard stop -rather than a gap silently papered over. Composing the tag locally and publishing it are two separate -moments: nothing pushes to the remote or shows up as a GitHub Release until you explicitly authorize -that second step. +codeArbiter contains four plugins with independent versions. This command selects the release target +and routes the matching tag namespace, payload, manifests, changelog, and built artifacts through +one release skill. A bare invocation preserves the default `ca` release path. ## Usage -``` -/ca:release [""] | --auto | --dry-run +```text +/ca:release [ca | ca-codex | ca-sandbox | ca-pi] ``` -An explicit version still gets checked against the derived classification; `--auto` derives it -outright (the default when no version is given); `--dry-run` runs every check and reports readiness -without composing anything. +Codex uses `$ca-release`; Pi uses `/ca-release`. The target defaults to `ca`. There is no +user-supplied version: the selected payload's Conventional-Commit history determines it. ## Example ```text -> /ca:release --dry-run - -Working tree clean. Branch: release/prep (not the default branch — OK). -Last tag: v2.4.1. Scanning 14 commits since... -Classification: 3 feat, 6 fix, 5 chore -> minor bump. -Derived version: v2.5.0. Matches plugin manifest? no — manifest still reads 2.4.1, would need updating. -Changelog: all 9 feat/fix commits carry a CHANGELOG: footer. -Dry run complete — no tag composed. Update the manifest version, then re-run without --dry-run. +> /ca:release ca-pi + +Target: ca-pi +Tag series: ca-pi-v* +Payload window: plugins/ca-pi/ (tools/ excluded) +Manifest parity: plugins/ca-pi/package.json = generated root package.json +Derived bump: patch +Next version: ca-pi-v0.1.33 +Built extensions: clean ``` -## When to reach for it +The command continues through changelog and local tag composition. It stops again before pushing +the tag or creating the GitHub Release. + +## Choose the target deliberately + +- `ca`: Claude Code governance plugin and the only series eligible for the repo-wide Latest badge. +- `ca-codex`: Codex adapter. +- `ca-sandbox`: isolated exploration plugin. +- `ca-pi`: Git-installed Pi adapter with two manifests and committed extension bundles. -Reach for `/ca:release` once the target branch is clean and its suite is green and you're ready to -cut a version. Work still in progress lands through `/ca:feature` or `/ca:fix` first — a release -aggregates what already passed, it doesn't chase down anything new. +Use the [release guide](/guides/releasing-a-version/) for the per-target verification checklist and +bad-release recovery procedure. diff --git a/site/src/curated/skills/post-merge-cleanup.md b/site/src/curated/skills/post-merge-cleanup.md new file mode 100644 index 00000000..06eaa933 --- /dev/null +++ b/site/src/curated/skills/post-merge-cleanup.md @@ -0,0 +1,38 @@ +--- +entity: skills/post-merge-cleanup +related: [commands/cleanup, commands/pr, commands/standup] +gates: + - gate: merge proof + when: phase 1 compares the topic HEAD with the fetched default ref + effect: work stops if containment cannot be proven from current remote state + - gate: item classification + when: phase 2 inventories tracked changes, untracked files, and stashes + effect: every item receives a reasoned class and any uncertainty receives the most conservative class + - gate: explicit removal + when: phases 3 and 5 offer local cleanup + effect: each artifact and the local branch require their own named confirmation + - gate: safe transition + when: phase 4 returns to the default branch + effect: verifies the checkout and permits only a fast-forward update +--- + +## What it does + +The `cleanup` command routes here after a pull request lands. The skill separates merge proof from +local tidying so no generated file, scratch note, stash, or branch is discarded merely because it +looks obsolete. + +## Phases + +1. Fetch the remote default branch and prove the topic commit is contained in it. +2. Inventory the working tree and stashes, then explain the evidence for each classification. +3. Ask about every possible removal separately, preserving anything declined or uncertain. +4. Check out the default branch, verify the checkout, and update it only by fast-forward. +5. Offer deletion of the merged local branch with Git's non-forcing safety check. +6. Report the final checkout and every keep/remove decision. + +## Exits + +A complete run leaves you on a clean, current default branch and may remove the merged local branch. +A safe early exit leaves the checkout unchanged and names the evidence or retained item that stopped +the transition. Remote branches and stashes are outside this skill's deletion authority. diff --git a/site/src/curated/skills/release.md b/site/src/curated/skills/release.md index 76dca242..19985429 100644 --- a/site/src/curated/skills/release.md +++ b/site/src/curated/skills/release.md @@ -1,34 +1,46 @@ --- entity: skills/release -related: [commands/release, commit-gate] +related: [commands/release, skills/commit-gate] gates: - - gate: version derivation + - gate: target isolation + when: pre-flight resolves the release row + effect: one plugin's tag or commit cannot influence another plugin's version, changelog, or payload + - gate: version and artifact proof when: before tagging - effect: the version bump is derived mechanically from the commit log, not guessed, and must match the manifest's version field before anything is tagged + effect: the derived version must match every target manifest and all shipped bundles must rebuild without a diff - gate: publication authorization - when: after the local tag is composed - effect: pushing the tag and creating the public release both wait for your explicit go-ahead — nothing about the tag composition authorizes publishing it + when: after the local tag and report exist + effect: pushing the tag and creating the public release wait for explicit approval + - gate: publication read-back + when: GitHub operations return + effect: the release is not accepted until a non-draft release on the expected tag is read back --- ## What it does -This is the only sanctioned path to a version tag, invoked by the release command on a -non-default branch with a green suite. It derives the version bump from the Conventional Commits -history since the last tag, rolls the relevant commits into the changelog, composes an annotated -tag locally, and — only once you authorize it — pushes the tag and publishes it as a public -release using that same changelog section as its notes. +The release command routes here with one of four plugin targets. The skill resolves that target's +registered tag prefix, payload path, manifests, changelog, built artifacts, and Latest-badge policy; +then it derives the release from that isolated row. ## Phases -1. Derive the version bump mechanically from the commit log, confirm it against the manifest, and - roll the qualifying commits into a new changelog section. -2. Compose the annotated tag locally from that changelog section and report the version, the - bump rationale, and the tag — without publishing anything yet. -3. On your explicit authorization, push the tag, create the public release from the same - changelog section, and read the result back to confirm it actually published. +1. Prove the branch, tree, target-specific tag baseline, payload window, manifests, and built + artifacts are ready. +2. Derive SemVer from the selected payload's commits, update its changelog and release surfaces, and + route any required commit through the commit gate. Every `feat` and `fix` must already carry a + `CHANGELOG:` footer; `perf` footers roll when present and `refactor` does not synthesize one. +3. Compose an annotated tag locally and report the classification, notes, and tag SHA. +4. After explicit authorization, publish the tag and GitHub Release, read the release back, and + record the published tag's provenance. -## Exits +## Target-specific state -A completed run leaves a pushed tag and a confirmed, non-draft public release whose notes are -exactly the changelog section composed in the first phase. Without your authorization, the tag -and changelog stay staged locally and nothing leaves the repository. +`ca-pi` has two manifests and generated extension bundles. `ca` owns README version/count surfaces +and may receive the repo-wide Latest badge. Every sibling explicitly publishes with +`--latest=false`. The helper register, not prose memory, supplies each tag prefix. + +## Exits and recovery + +Without publish authorization, the local release state remains unpushed. A partial publish is named +and resumed rather than repeated blindly. A bad published release is superseded with a new version; +the original tag is immutable. diff --git a/site/src/pages/404.astro b/site/src/pages/404.astro new file mode 100644 index 00000000..714a21cd --- /dev/null +++ b/site/src/pages/404.astro @@ -0,0 +1,53 @@ +--- +import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; + +const base = import.meta.env.BASE_URL.replace(/\/$/, ""); +--- + + +

    + If a link from this site brought you here, use search to find the current source-backed page. +

    +
    + + diff --git a/site/src/styles/design-system.css b/site/src/styles/design-system.css new file mode 100644 index 00000000..cf417d05 --- /dev/null +++ b/site/src/styles/design-system.css @@ -0,0 +1,783 @@ +@font-face { + font-family: "Manrope Variable"; + src: url("../assets/fonts/manrope-latin-wght-normal.woff2") format("woff2"); + font-display: swap; + font-style: normal; + font-weight: 200 800; +} + +@font-face { + font-family: "JetBrains Mono Variable"; + src: url("../assets/fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2"); + font-display: swap; + font-style: normal; + font-weight: 100 800; +} + +/* + * codeArbiter documentation design system + * + * This file owns the shared visual language used by the product landing page, + * hand-authored guides, and generated reference. Page-specific layout belongs + * in landing.css; one-off values do not belong in either file. + */ + +:root { + /* Typography */ + --ca-font-sans: "Manrope Variable", "Segoe UI Variable", "Segoe UI", sans-serif; + --ca-font-mono: "JetBrains Mono Variable", "Cascadia Code", Consolas, monospace; + --ca-text-xs: 0.75rem; + --ca-text-sm: 0.875rem; + --ca-text-md: 1rem; + --ca-text-lg: clamp(1.08rem, 1.5vw, 1.2rem); + --ca-text-xl: clamp(1.35rem, 2.4vw, 1.8rem); + --ca-text-2xl: clamp(2rem, 4vw, 3.3rem); + --ca-text-display: clamp(3rem, 7vw, 6.35rem); + + /* Spacing */ + --ca-space-1: 0.25rem; + --ca-space-2: 0.5rem; + --ca-space-3: 0.75rem; + --ca-space-4: 1rem; + --ca-space-5: 1.5rem; + --ca-space-6: 2rem; + --ca-space-7: 3rem; + --ca-space-8: 4.5rem; + --ca-space-9: 7rem; + + /* Shape and depth */ + --ca-radius-sm: 0.45rem; + --ca-radius-md: 0.8rem; + --ca-radius-lg: 1.25rem; + --ca-radius-pill: 999px; + --ca-shadow-sm: 0 10px 30px rgb(0 0 0 / 18%); + --ca-shadow-lg: 0 30px 90px rgb(0 0 0 / 36%); + + /* Surfaces */ + --ca-bg: #090d12; + --ca-bg-raised: #0e141c; + --ca-bg-panel: #121a24; + --ca-bg-soft: #17212d; + --ca-line: #273647; + --ca-line-strong: #3a4c60; + --ca-ink: #f6f7f9; + --ca-ink-soft: #c7d0da; + --ca-ink-muted: #91a0b2; + --ca-brand: #f0b92f; + --ca-brand-bright: #ffd568; + --ca-brand-deep: #9b6811; + --ca-brand-wash: rgb(240 185 47 / 10%); + --ca-info: #7ab7ff; + --ca-positive: #58d68d; + --ca-danger: #ff7b72; + --ca-preview: #ed946d; + + /* Starlight bridge */ + --sl-font: var(--ca-font-sans); + --sl-font-mono: var(--ca-font-mono); + --sl-color-bg: var(--ca-bg); + --sl-color-bg-nav: rgb(9 13 18 / 92%); + --sl-color-bg-sidebar: var(--ca-bg-raised); + --sl-color-hairline: var(--ca-line); + --sl-color-hairline-light: var(--ca-line); + --sl-color-text: var(--ca-ink-soft); + --sl-color-white: var(--ca-ink); +} + +html { + background: var(--ca-bg); + color-scheme: dark; + scroll-behavior: smooth; +} + +body { + font-family: var(--ca-font-sans); + color: var(--ca-ink-soft); + background: + radial-gradient(circle at 50% -20%, rgb(240 185 47 / 8%), transparent 34rem), + var(--ca-bg); + font-feature-settings: "ss01", "ss02", "cv02"; + text-rendering: optimizeLegibility; +} + +code, +pre, +kbd { + font-family: var(--ca-font-mono); +} + +::selection { + color: #100d06; + background: var(--ca-brand-bright); +} + +:focus-visible { + outline: 2px solid var(--ca-brand-bright); + outline-offset: 3px; +} + +header.header { + border-block-end-color: rgb(58 76 96 / 70%); + background: rgb(9 13 18 / 86%); + backdrop-filter: blur(18px) saturate(135%); +} + +.site-title img { + width: 10rem; + height: auto; +} + +.sidebar-pane { + border-inline-end-color: rgb(58 76 96 / 55%); + background: + linear-gradient(180deg, rgb(240 185 47 / 3%), transparent 15rem), + var(--ca-bg-raised); +} + +.sidebar-content a[aria-current="page"] { + color: var(--ca-ink); + background: var(--ca-brand-wash); + box-shadow: inset 2px 0 0 var(--ca-brand); +} + +.sl-markdown-content { + color: var(--ca-ink-soft); + font-size: 1.01rem; + line-height: 1.78; +} + +.sl-markdown-content > p:first-of-type { + color: var(--ca-ink-soft); + font-size: var(--ca-text-lg); + line-height: 1.72; +} + +.sl-markdown-content h1, +.sl-markdown-content h2, +.sl-markdown-content h3 { + color: var(--ca-ink); + letter-spacing: -0.025em; + text-wrap: balance; +} + +.sl-markdown-content h1 { + font-size: clamp(2.2rem, 5vw, 3.65rem); + line-height: 1.05; +} + +.sl-markdown-content h2 { + font-size: clamp(1.45rem, 2.4vw, 1.95rem); +} + +.sl-markdown-content h3 { + font-size: clamp(1.08rem, 1.8vw, 1.3rem); +} + +.sl-markdown-content a { + text-decoration-thickness: 0.08em; + text-underline-offset: 0.2em; +} + +.sl-markdown-content table { + display: table; + width: 100%; + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + overflow: hidden; + font-size: var(--ca-text-sm); +} + +.sl-markdown-content th { + color: var(--ca-ink); + background: var(--ca-bg-soft); +} + +.sl-markdown-content tr:nth-child(2n) { + background: rgb(255 255 255 / 1.5%); +} + +details.ca-source { + max-width: 100%; + overflow: hidden; +} + +details.ca-source .expressive-code, +details.ca-source pre { + max-width: 100%; + overflow-x: auto; +} + +.sl-markdown-content :not(pre) > code { + border: 1px solid rgb(58 76 96 / 65%); + border-radius: 0.35rem; + color: var(--ca-ink); + background: rgb(23 33 45 / 82%); +} + +.ca-host-syntax { + margin-block: var(--ca-space-5); + padding: 0.9rem 1rem; + border: 1px solid var(--ca-line); + border-inline-start: 3px solid var(--ca-brand); + border-radius: var(--ca-radius-sm); + color: var(--ca-ink-soft); + background: linear-gradient(90deg, var(--ca-brand-wash), rgb(14 20 28 / 76%)); +} + +.ca-host-syntax strong { + color: var(--ca-ink); +} + +.ca-eyebrow, +.ca-kicker { + margin: 0; + color: var(--ca-brand); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); + font-weight: 700; + letter-spacing: 0.14em; + line-height: 1.4; + text-transform: uppercase; +} + +.ca-muted { + color: var(--ca-ink-muted); +} + +.ca-actions { + display: flex; + flex-wrap: wrap; + gap: var(--ca-space-3); +} + +.ca-button { + display: inline-flex; + min-height: 2.9rem; + align-items: center; + justify-content: center; + gap: var(--ca-space-2); + padding: 0.72rem 1.2rem; + border: 1px solid var(--ca-line-strong); + border-radius: var(--ca-radius-sm); + color: var(--ca-ink); + background: rgb(14 20 28 / 72%); + font-weight: 750; + line-height: 1; + text-decoration: none; + transition: + transform 160ms ease, + border-color 160ms ease, + background 160ms ease; +} + +.ca-button:hover { + border-color: var(--ca-brand); + color: var(--ca-ink); + transform: translateY(-2px); +} + +.ca-button--primary { + border-color: var(--ca-brand); + color: #171005; + background: linear-gradient(135deg, var(--ca-brand-bright), var(--ca-brand)); + box-shadow: 0 10px 30px rgb(240 185 47 / 18%); +} + +.ca-button--primary:hover { + color: #171005; + background: var(--ca-brand-bright); +} + +.ca-panel { + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + background: + linear-gradient(145deg, rgb(255 255 255 / 2.5%), transparent 42%), + var(--ca-bg-panel); + box-shadow: var(--ca-shadow-sm); +} + +.ca-pill { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.65rem; + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-pill); + color: var(--ca-ink-muted); + background: rgb(14 20 28 / 78%); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); +} + +.ca-pill::before { + width: 0.42rem; + height: 0.42rem; + border-radius: 50%; + background: var(--ca-positive); + content: ""; + box-shadow: 0 0 0 4px rgb(88 214 141 / 9%); +} + +/* Every inner page starts with a task-oriented orientation band. */ +.ca-page-context { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: var(--ca-space-4); + margin: var(--ca-space-5) 0 var(--ca-space-7); + padding: var(--ca-space-5); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + background: + radial-gradient(circle at 100% 0%, rgb(240 185 47 / 8%), transparent 18rem), + linear-gradient(135deg, rgb(255 255 255 / 2.5%), transparent 42%), + var(--ca-bg-panel); + box-shadow: var(--ca-shadow-sm); +} + +.ca-page-context__icon { + display: grid; + width: 3rem; + height: 3rem; + place-items: center; + border: 1px solid color-mix(in srgb, var(--ca-brand) 35%, var(--ca-line)); + border-radius: 0.78rem; + color: var(--ca-brand-bright); + background: var(--ca-brand-wash); +} + +.ca-page-context__body { + min-width: 0; +} + +.ca-page-context__meta { + display: flex; + flex-wrap: wrap; + gap: var(--ca-space-2); + margin-block-end: var(--ca-space-2); +} + +.ca-page-context__meta span { + padding: 0.25rem 0.52rem; + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-pill); + color: var(--ca-brand); + background: rgb(9 13 18 / 48%); + font-family: var(--ca-font-mono); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.ca-page-context p { + margin: 0; + color: var(--ca-ink-soft); + font-size: var(--ca-text-md); + line-height: 1.6; +} + +.ca-page-context p strong { + color: var(--ca-ink); +} + +.ca-page-context .ca-page-context__support { + margin-block-start: var(--ca-space-2); + color: var(--ca-ink-muted); + font-size: var(--ca-text-sm); +} + +/* Section rhythm for long-form training pages. */ +.sl-markdown-content > h2 { + position: relative; + margin-block-start: clamp(3rem, 7vw, 4.75rem); + padding-block-start: var(--ca-space-5); + border-block-start: 1px solid var(--ca-line); +} + +.sl-markdown-content > h2::before { + position: absolute; + inset-block-start: -1px; + inset-inline-start: 0; + width: 3.25rem; + height: 2px; + background: linear-gradient(90deg, var(--ca-brand), transparent); + content: ""; +} + +.sl-markdown-content > h2 + p { + max-width: 68ch; + color: var(--ca-ink-soft); +} + +.sl-markdown-content > ol > li::marker { + color: var(--ca-brand); + font-family: var(--ca-font-mono); + font-weight: 750; +} + +.sl-markdown-content > ul > li::marker { + color: var(--ca-brand); +} + +/* Novice-to-power-user curriculum */ +.ca-learning-map { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--ca-space-3); + margin-block: var(--ca-space-7); +} + +.ca-learning-map a { + display: flex; + min-height: 5.5rem; + align-items: center; + gap: var(--ca-space-3); + padding: var(--ca-space-4); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + color: var(--ca-ink); + background: var(--ca-bg-panel); + font-weight: 700; + text-decoration: none; +} + +.ca-learning-map a:hover { + border-color: var(--ca-brand-deep); + transform: translateY(-2px); +} + +.ca-learning-map a span { + display: grid; + width: 2rem; + height: 2rem; + place-items: center; + border: 1px solid var(--ca-brand-deep); + border-radius: 50%; + color: var(--ca-brand-bright); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); +} + +.ca-learning-stage { + margin-block: var(--ca-space-5); + padding: clamp(1.25rem, 4vw, 2rem); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-lg); + background: + radial-gradient(circle at 100% 0, rgb(240 185 47 / 7%), transparent 20rem), + var(--ca-bg-panel); + box-shadow: var(--ca-shadow-sm); +} + +.ca-learning-stage__head { + display: flex; + gap: var(--ca-space-4); + align-items: center; +} + +.ca-learning-stage__icon { + display: grid; + width: 3.25rem; + height: 3.25rem; + flex: none; + place-items: center; + border: 1px solid color-mix(in srgb, var(--ca-brand) 35%, var(--ca-line)); + border-radius: 0.8rem; + color: var(--ca-brand-bright); + background: var(--ca-brand-wash); +} + +.ca-learning-stage__head h3 { + margin: var(--ca-space-2) 0 0; + color: var(--ca-ink); + font-size: var(--ca-text-xl); +} + +.ca-module-list { + margin: var(--ca-space-5) 0; + padding: 0; + border-block-start: 1px solid var(--ca-line); + list-style: none; +} + +.ca-module-list li { + display: flex; + gap: var(--ca-space-4); + align-items: baseline; + justify-content: space-between; + padding-block: var(--ca-space-3); + border-block-end: 1px solid var(--ca-line); +} + +.ca-module-list li::before { + color: var(--ca-positive); + content: "→"; + font-family: var(--ca-font-mono); +} + +.ca-module-list a { + flex: 1; + color: var(--ca-ink); + font-weight: 650; + text-decoration: none; +} + +.ca-module-list a:hover { + color: var(--ca-brand-bright); +} + +.ca-module-list span { + color: var(--ca-ink-muted); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); + white-space: nowrap; +} + +.ca-learning-proof { + padding: var(--ca-space-4); + border: 1px solid color-mix(in srgb, var(--ca-positive) 30%, var(--ca-line)); + border-radius: var(--ca-radius-sm); + color: var(--ca-ink-soft); + background: rgb(88 214 141 / 6%); +} + +.ca-learning-proof strong { + color: var(--ca-positive); +} + +.ca-capstone { + padding: clamp(1.5rem, 4vw, 2.5rem); + border: 1px solid var(--ca-brand-deep); + border-radius: var(--ca-radius-lg); + background: + radial-gradient(circle at 95% 5%, rgb(240 185 47 / 13%), transparent 22rem), + var(--ca-bg-panel); + box-shadow: var(--ca-shadow-lg); +} + +.ca-capstone h3 { + margin: var(--ca-space-3) 0 var(--ca-space-5); + color: var(--ca-ink); + font-size: var(--ca-text-xl); +} + +.ca-capstone > p:last-child { + margin-block-end: 0; + padding-block-start: var(--ca-space-4); + border-block-start: 1px solid var(--ca-line); + color: var(--ca-brand-bright); +} + +/* Generated reference portal */ +.ca-reference-map { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--ca-space-3); + margin-block: var(--ca-space-6); +} + +.ca-reference-map a { + display: grid; + min-height: 10rem; + align-content: start; + gap: var(--ca-space-2); + padding: var(--ca-space-5); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + color: var(--ca-ink); + background: + radial-gradient(circle at 100% 0, rgb(240 185 47 / 8%), transparent 11rem), + var(--ca-bg-panel); + text-decoration: none; +} + +.ca-reference-map a:hover { + border-color: var(--ca-brand-deep); + transform: translateY(-2px); +} + +.ca-reference-map a > span { + color: var(--ca-brand); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); + font-weight: 750; +} + +.ca-reference-map a > strong { + font-size: var(--ca-text-xl); +} + +.ca-reference-map a > small { + color: var(--ca-ink-muted); + font-size: var(--ca-text-sm); + line-height: 1.55; +} + +.ca-reference-guide { + margin-block: var(--ca-space-5) var(--ca-space-7); + padding: var(--ca-space-5); + border: 1px solid color-mix(in srgb, var(--ca-info) 34%, var(--ca-line)); + border-radius: var(--ca-radius-md); + color: var(--ca-ink-soft); + background: rgb(122 183 255 / 5%); +} + +.ca-reference-guide > strong { + color: var(--ca-ink); + font-size: var(--ca-text-lg); +} + +.ca-reference-guide ol { + margin-block: var(--ca-space-4); +} + +.ca-reference-guide p { + margin-block-end: 0; +} + +/* Generated reference orientation */ +.ca-reference-lead { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--ca-space-4) var(--ca-space-6); + margin: 0 0 var(--ca-space-6); + padding: var(--ca-space-5); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + background: + radial-gradient(circle at 100% 0, rgb(240 185 47 / 9%), transparent 14rem), + var(--ca-bg-panel); +} + +.ca-reference-lead > * { + min-width: 0; +} + +.ca-reference-lead__kind { + display: inline-flex; + width: fit-content; + margin-block-end: var(--ca-space-3); + padding: 0.3rem 0.55rem; + border: 1px solid var(--ca-line-strong); + border-radius: var(--ca-radius-pill); + color: var(--ca-brand-bright); + font-family: var(--ca-font-mono); + font-size: 0.69rem; + font-weight: 750; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.ca-reference-lead__summary { + max-width: 67ch; + margin: 0; + color: var(--ca-ink-soft); + font-size: var(--ca-text-lg); + line-height: 1.65; + overflow-wrap: anywhere; +} + +.ca-reference-lead__usage { + min-width: min(100%, 13rem); + align-self: center; + padding-inline-start: var(--ca-space-5); + border-inline-start: 1px solid var(--ca-line); +} + +.ca-reference-lead__usage-label { + display: block; + margin-block-end: var(--ca-space-2); + color: var(--ca-ink-muted); + font-size: var(--ca-text-xs); + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.ca-reference-lead__usage code { + display: block; + width: fit-content; + margin-block: 0.25rem; + color: var(--ca-ink); + font-size: var(--ca-text-sm); +} + +.ca-reference-lead__hosts { + display: grid; + gap: 0.3rem; + margin-block-end: var(--ca-space-3); +} + +.ca-reference-lead__host { + display: grid; + grid-template-columns: minmax(5.5rem, 1fr) auto; + gap: var(--ca-space-3); + align-items: center; + color: var(--ca-ink-muted); + font-size: var(--ca-text-xs); +} + +.ca-reference-lead__host code { + margin: 0; +} + +.ca-reference-lead__unsupported { + color: var(--ca-ink-muted); + font-family: var(--ca-font-mono); + font-size: 0.72rem; +} + +.ca-reference-lead__usage p { + max-width: 25ch; + margin: 0; + color: var(--ca-ink-muted); + font-size: var(--ca-text-sm); + line-height: 1.55; +} + +.sl-markdown-content table { + display: block; + max-width: 100%; + overflow-x: auto; + overscroll-behavior-inline: contain; +} + +@media (max-width: 42rem) { + .ca-page-context { + grid-template-columns: 1fr; + } + + .ca-learning-map { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .ca-reference-map { + grid-template-columns: 1fr; + } + + .ca-module-list li { + align-items: flex-start; + } + + .ca-reference-lead { + grid-template-columns: minmax(0, 1fr); + } + + .ca-reference-lead__usage { + padding-block-start: var(--ca-space-4); + padding-inline-start: 0; + border-block-start: 1px solid var(--ca-line); + border-inline-start: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + .ca-button { + transition: none; + } +} diff --git a/site/src/styles/landing.css b/site/src/styles/landing.css index 64a37196..1b38182b 100644 --- a/site/src/styles/landing.css +++ b/site/src/styles/landing.css @@ -1,655 +1,691 @@ -/* codeArbiter docs — landing.css - Bespoke landing layout: a two-column hero (intro + gate-catch terminal), the - command block, the lane-flow diagram, and the Feature Forge showcase. Design - tokens and shared component classes live in theme.css; build on those, do not - redefine tokens here. - - The landing runs on Starlight's `doc` template (so the sidebar is present) with - the right-hand table of contents disabled in frontmatter. -*/ - -/* ========================================================= - LANDING WRAPPER - Widen the content measure (no TOC on this page) so the two-column hero and the - wide lane-flow diagram have room. Scoped to the landing via :has(). - ========================================================= */ +/* + * Full-width product landing. + * + * Shared tokens and primitives live in design-system.css. This file owns only + * the splash-page composition and its responsive behavior. + */ :root:has(.ca-landing) { - --sl-content-width: 60rem; + --sl-content-width: 100%; +} + +:root:has(.ca-landing) .main-pane { + width: 100%; +} + +:root:has(.ca-landing) .content-panel { + padding: 0; + border: 0; +} + +:root:has(.ca-landing) .sl-container { + max-width: none; } .ca-landing { - margin-block-start: -0.5rem; + --ca-landing-width: 76rem; + width: 100%; + overflow: clip; } -/* ========================================================= - TWO-COLUMN HERO - Intro (headline + tagline + actions) on the left; the autoplaying gate-catch - terminal on the right — visible above the fold on first land. Collapses to a - single column on narrow viewports. - ========================================================= */ +.ca-landing section { + scroll-margin-top: 5rem; +} -.ca-landing__hero { +.ca-splash { + position: relative; display: grid; - /* 40/60 split: give the intro text less room, the terminal more, so the long - transcript lines breathe and the two sides balance better. */ - grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); - gap: clamp(1.5rem, 4vw, 3rem); + grid-template-columns: minmax(0, 0.9fr) minmax(32rem, 1.1fr); + gap: clamp(2.5rem, 7vw, 6rem); align-items: center; - margin-block: 1.5rem 3rem; + max-width: var(--ca-landing-width); + min-height: min(49rem, calc(100vh - 4rem)); + margin-inline: auto; + padding: clamp(5rem, 10vw, 8.5rem) clamp(1.25rem, 4vw, 3rem); } -@media (max-width: 50rem) { - .ca-landing__hero { - grid-template-columns: 1fr; - gap: 1.75rem; - } +.ca-splash > * { + min-width: 0; } -.ca-landing__title { - font-size: clamp(2rem, 4.5vw, 3rem); - line-height: 1.08; - font-weight: 700; - letter-spacing: -0.01em; - margin: 0 0 1.1rem; - color: var(--sl-color-white); +.ca-splash::before { + position: absolute; + z-index: -1; + inset: 0 -25vw; + background: + linear-gradient(90deg, var(--ca-bg) 0%, rgb(9 13 18 / 92%) 35%, rgb(9 13 18 / 50%) 66%, var(--ca-bg) 100%), + linear-gradient(180deg, rgb(9 13 18 / 8%) 58%, var(--ca-bg) 100%), + var(--ca-hero-art) center / cover no-repeat; + opacity: 0.62; + mask-image: linear-gradient(180deg, #000 0%, #000 75%, transparent 100%); + pointer-events: none; + content: ""; +} + +.ca-splash__copy h1 { + max-width: 9ch; + margin: var(--ca-space-4) 0 var(--ca-space-5); + color: var(--ca-ink); + font-size: var(--ca-text-display); + font-weight: 780; + letter-spacing: -0.065em; + line-height: 0.95; + text-wrap: balance; +} + +.ca-splash__lede { + max-width: 43rem; + margin: 0; + color: var(--ca-ink-soft); + font-size: clamp(1.1rem, 2vw, 1.35rem); + line-height: 1.65; } -.ca-landing__tagline { - max-width: 44ch; - margin: 0 0 1.75rem; - color: var(--ca-text-muted); - font-size: clamp(1rem, 1.5vw, 1.15rem); - line-height: 1.6; +.ca-splash__principle { + margin: var(--ca-space-4) 0 var(--ca-space-6); + color: var(--ca-brand-bright); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-sm); + font-weight: 650; } -.ca-landing__actions { +.ca-splash__hosts { display: flex; flex-wrap: wrap; - gap: 0.75rem; -} - -.ca-landing__cta { - display: inline-flex; - align-items: center; - padding: 0.6rem 1.25rem; - border: 1px solid var(--ca-slate-border); - border-radius: 0.5rem; - color: var(--sl-color-white); - font-weight: 600; - font-size: 0.95rem; - text-decoration: none; - transition: - border-color 0.2s ease, - background 0.2s ease, - transform 0.2s ease; + gap: var(--ca-space-2); + margin-block-start: var(--ca-space-6); } -.ca-landing__cta:hover { - border-color: var(--ca-gold-dim); - transform: translateY(-2px); +.ca-pill--preview::before { + background: var(--ca-preview); + box-shadow: 0 0 0 4px rgb(237 148 109 / 9%); } -.ca-landing__cta:focus-visible { - outline: 2px solid var(--ca-gold); - outline-offset: 2px; +.ca-splash__proof { + position: relative; + padding: clamp(1rem, 2vw, 1.4rem); + box-shadow: var(--ca-shadow-lg); } -.ca-landing__cta--primary { - background: var(--ca-gold); - border-color: var(--ca-gold); - color: #1a1206; +.ca-splash__proof::after { + position: absolute; + z-index: -1; + inset: 10% -8% -10% 15%; + border-radius: 50%; + background: rgb(240 185 47 / 9%); + filter: blur(70px); + content: ""; } -.ca-landing__cta--primary:hover { - background: var(--ca-gold-dim); - border-color: var(--ca-gold-dim); +.ca-splash__proof-head { + display: flex; + gap: var(--ca-space-4); + align-items: flex-start; + justify-content: space-between; + padding: 0.2rem 0.2rem var(--ca-space-3); } -/* Demo column: the gate-catch terminal with a small framing label + note. */ -.ca-landing__demo { - min-width: 0; +.ca-splash__proof-title { + margin: 0.35rem 0 0; + color: var(--ca-ink); + font-size: var(--ca-text-lg); + font-weight: 700; } -.ca-landing__demo-label { - margin: 0 0 0.6rem; - color: var(--ca-text-muted); - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.78rem; - letter-spacing: 0.08em; - text-transform: uppercase; +.ca-splash__proof-host { + color: var(--ca-ink-muted); + font-family: var(--ca-font-mono); + font-size: 0.68rem; } -.ca-landing__demo .ca-terminal { - margin-block: 0; +.ca-splash__proof-note { + margin: var(--ca-space-3) 0 0; + color: var(--ca-ink-muted); + font-size: var(--ca-text-xs); + line-height: 1.6; } -.ca-landing__demo-note { - margin: 0.6rem 0 0; - color: var(--ca-text-muted); - font-size: 0.8rem; - font-style: italic; +.ca-proof-strip, +.ca-section, +.ca-final-cta { + max-width: var(--ca-landing-width); + margin-inline: auto; + padding-inline: clamp(1.25rem, 4vw, 3rem); } -/* ========================================================= - BELOW THE FOLD — what-line + first-command block - ========================================================= */ - -.ca-hero { - margin-block: 1.5rem 0; +.ca-proof-strip { + padding-block: var(--ca-space-7) var(--ca-space-8); + border-block: 1px solid var(--ca-line); } -.ca-hero__what { - margin-block-end: 0.5rem; - color: var(--ca-text-muted); - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: clamp(1.05rem, 2vw, 1.2rem); - letter-spacing: 0.04em; +.ca-section { + padding-block: var(--ca-space-9); } -/* Install block: a two-column mirror of the above-the-fold hero. The hero is - intro (left) + terminal (right) at 4fr/6fr; this flips it to terminal (left) - + copy (right) at 6fr/4fr, so the two sections balance as opposites. */ -.ca-hero__install { - display: grid; - grid-template-columns: minmax(0, 6fr) minmax(0, 4fr); - gap: clamp(1.5rem, 4vw, 3rem); - align-items: center; - margin-block: 2.5rem 0; +.ca-section + .ca-section { + border-block-start: 1px solid var(--ca-line); } -@media (max-width: 50rem) { - .ca-hero__install { - grid-template-columns: 1fr; - gap: 1.75rem; - } +.ca-section-heading { + max-width: 48rem; + margin-block-end: var(--ca-space-7); } -.ca-hero__install-demo { - min-width: 0; +.ca-section-heading--compact { + margin-block-end: var(--ca-space-5); } -.ca-hero__install-copy { - min-width: 0; +.ca-section-heading h2, +.ca-flow__intro h2, +.ca-fit__copy h2, +.ca-final-cta h2 { + margin: var(--ca-space-3) 0 var(--ca-space-4); + color: var(--ca-ink); + font-size: var(--ca-text-2xl); + letter-spacing: -0.045em; + line-height: 1.08; + text-wrap: balance; } -/* Splashy headline, sized to match the above-the-fold hero title so the two - sections read as a matched pair. It is a real

    (honest document outline); - the selector is scoped past Starlight's `.sl-markdown-content h2` chrome - (border + top margin) so none of that leaks in. */ -.ca-hero .ca-hero__install-blurb { - margin: 0 0 1.1rem; - border: 0; - color: var(--sl-color-white); - font-size: clamp(2rem, 4.5vw, 3rem); - line-height: 1.08; - font-weight: 700; - letter-spacing: -0.01em; +.ca-section-heading p:last-child, +.ca-flow__intro > p, +.ca-fit__copy > p, +.ca-final-cta > div > p:last-child { + max-width: 62ch; + margin: 0; + color: var(--ca-ink-muted); + font-size: var(--ca-text-lg); + line-height: 1.65; } -.ca-hero__install-subtext { - max-width: 40ch; +.ca-proof-strip .ca-trust { margin: 0; - color: var(--ca-text-muted); - font-size: clamp(1rem, 1.5vw, 1.15rem); - line-height: 1.6; + padding: 0; + border: 0; } -/* The terminal fills its (now narrower) left column. */ -.ca-hero__install .ca-terminal { - margin-block: 0; +.ca-proof-strip .ca-trust__row { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: var(--ca-space-3); + margin: var(--ca-space-5) 0 0; + padding: 0; + list-style: none; } -/* Payoff: the rendered statusline spans the full content width below the - two-column row, echoing the full-width lane-flow diagram under the splash. */ -.ca-hero__statusline { - margin: 1.5rem 0 0; +.ca-proof-strip .ca-trust__tile { + min-width: 0; + min-height: 7.5rem; + display: flex; + align-items: stretch; + border-color: var(--ca-line); + border-style: solid; + border-width: 1px; + border-radius: var(--ca-radius-md); + background: var(--ca-bg-panel); + overflow: hidden; } -.ca-hero__statusline img { - display: block; +.ca-proof-strip .ca-trust__link, +.ca-proof-strip .ca-trust__tile--qual { width: 100%; - height: auto; - border-radius: 0.4rem; + padding: var(--ca-space-4); } -.ca-hero__statusline figcaption { - margin-top: 0.5rem; - color: var(--ca-text-muted); - font-size: 0.8rem; - font-style: italic; +.ca-proof-strip .ca-trust__link { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: var(--ca-space-3); + color: inherit; + text-decoration: none; + transition: + border-color var(--ca-motion-fast), + background var(--ca-motion-fast), + transform var(--ca-motion-fast); } -/* ========================================================= - LANE FLOW DIAGRAM - ========================================================= */ - -.ca-laneflow { - margin-block: 1rem; +.ca-proof-strip .ca-trust__link:hover { + background: var(--ca-bg-elevated); + transform: translateY(-2px); } -/* ========================================================= - FORGE SHOWCASE - ========================================================= */ - -.ca-forge { - margin-block: 3rem 2rem; - padding-block-start: 2rem; - border-block-start: 1px solid var(--ca-slate-border); +.ca-proof-strip .ca-trust__link:focus-visible { + outline: 2px solid var(--ca-brand-bright); + outline-offset: -3px; } -.ca-forge h2 { - margin-block-end: 1rem; - color: var(--ca-gold-text); - font-size: clamp(1.2rem, 2.5vw, 1.6rem); - font-weight: 700; +.ca-proof-strip .ca-trust__num { + color: var(--ca-brand-bright); + font-family: var(--ca-font-mono); + font-size: clamp(1.7rem, 3vw, 2.5rem); + font-weight: 650; + letter-spacing: -0.06em; + line-height: 1; } -.ca-forge p { - max-width: 60ch; +.ca-proof-strip .ca-trust__num--qual { + font-size: var(--ca-text-sm); + letter-spacing: 0.04em; + text-transform: uppercase; } -/* ========================================================= - WHY GATES - Two-column split: prose explaining the hook mechanism (left) and a compact - numbered "how it fires" list plus reference links (right). Collapses to a - single column on narrow viewports, matching the hero/install pattern. - ========================================================= */ +.ca-proof-strip .ca-trust__label { + color: var(--ca-ink-muted); + font-size: var(--ca-text-xs); + line-height: 1.5; +} -.ca-why-gates { - display: grid; - grid-template-columns: minmax(0, 6fr) minmax(0, 4fr); - gap: clamp(1.5rem, 4vw, 3rem); - margin-block: 3rem 2rem; - padding-block-start: 2rem; - border-block-start: 1px solid var(--ca-slate-border); +.ca-proof-strip .ca-trust__tile--qual { + flex-direction: column; + justify-content: space-between; + gap: var(--ca-space-3); } -@media (max-width: 50rem) { - .ca-why-gates { - grid-template-columns: 1fr; - gap: 1.75rem; - } +.ca-feature-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--ca-space-4); } -.ca-why-gates h2 { - margin-block-end: 1rem; - color: var(--ca-gold-text); - font-size: clamp(1.2rem, 2.5vw, 1.6rem); - font-weight: 700; +.ca-journey-preview__grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--ca-space-3); } -.ca-why-gates__paragraph { - max-width: 60ch; - margin: 0 0 1rem; - color: var(--ca-text-muted); - line-height: 1.65; +.ca-journey-preview__grid a { + position: relative; + min-height: 15rem; + padding: var(--ca-space-5); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + color: inherit; + background: + linear-gradient(155deg, rgb(255 255 255 / 2.5%), transparent 42%), + var(--ca-bg-panel); + text-decoration: none; + transition: + transform 160ms ease, + border-color 160ms ease; } -.ca-why-gates__paragraph:last-child { - margin-block-end: 0; +.ca-journey-preview__grid a:hover { + border-color: var(--ca-brand-deep); + transform: translateY(-3px); } -.ca-why-gates__copy code, -.ca-why-gates__mechanics code { - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.9em; +.ca-journey-preview__grid a > span { + display: grid; + width: 2.6rem; + height: 2.6rem; + place-items: center; + border: 1px solid color-mix(in srgb, var(--ca-brand) 32%, var(--ca-line)); + border-radius: 0.7rem; + color: var(--ca-brand-bright); + background: var(--ca-brand-wash); } -.ca-why-gates__mechanics { - padding: 1.25rem 1.5rem; - border: 1px solid var(--ca-slate-border); - border-radius: 0.6rem; - background: var(--ca-slate-mid); +.ca-journey-preview__grid small { + position: absolute; + top: var(--ca-space-5); + right: var(--ca-space-5); + color: var(--ca-ink-muted); + font-family: var(--ca-font-mono); } -.ca-why-gates__mechanics h3 { - margin: 0 0 0.85rem; - color: var(--sl-color-white); - font-size: 1rem; - font-weight: 700; +.ca-journey-preview__grid h3 { + margin: var(--ca-space-6) 0 var(--ca-space-3); + color: var(--ca-ink); + font-size: var(--ca-text-lg); } -.ca-why-gates__mechanics ol { - margin: 0 0 1.1rem; - padding-inline-start: 1.2rem; - color: var(--ca-text-muted); - font-size: 0.92rem; +.ca-journey-preview__grid p { + margin: 0; + color: var(--ca-ink-muted); line-height: 1.6; } -.ca-why-gates__mechanics li + li { - margin-block-start: 0.5rem; +.ca-journey-preview__cta { + display: inline-block; + margin-block-start: var(--ca-space-5); } -.ca-why-gates__links { - margin: 0; - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - font-size: 0.88rem; +.ca-feature { + position: relative; + padding: var(--ca-space-6); } -.ca-why-gates__links span { - color: var(--ca-text-muted); +.ca-feature__icon, +.ca-lane-card__icon { + display: inline-grid; + width: 2.75rem; + height: 2.75rem; + place-items: center; + border: 1px solid color-mix(in srgb, var(--ca-brand) 32%, var(--ca-line)); + border-radius: 0.72rem; + color: var(--ca-brand-bright); + background: + radial-gradient(circle at 25% 20%, rgb(255 213 104 / 12%), transparent 65%), + var(--ca-brand-wash); +} + +.ca-feature h3, +.ca-lane-card h3, +.ca-host h3, +.ca-flow__step h3, +.ca-fit h3 { + margin: var(--ca-space-5) 0 var(--ca-space-3); + color: var(--ca-ink); + font-size: var(--ca-text-xl); + letter-spacing: -0.025em; + line-height: 1.2; +} + +.ca-feature p, +.ca-lane-card p, +.ca-host p, +.ca-flow__step p, +.ca-fit li { + color: var(--ca-ink-muted); + line-height: 1.65; } -/* ========================================================= - AUDIENCE SPLIT - Two-column honest "who this is for / who it isn't" grid. Stacks on mobile. - ========================================================= */ +.ca-feature a, +.ca-host a, +.ca-text-link { + color: var(--ca-brand-bright); + font-weight: 700; + text-underline-offset: 0.25em; +} -.ca-audience { - margin-block: 2rem; - padding-block-start: 2rem; - border-block-start: 1px solid var(--ca-slate-border); +.ca-flow { + display: grid; + grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); + gap: clamp(3rem, 8vw, 7rem); + align-items: start; } -.ca-audience h2 { - margin-block-end: 1.25rem; - color: var(--ca-gold-text); - font-size: clamp(1.2rem, 2.5vw, 1.6rem); - font-weight: 700; +.ca-flow__intro { + position: sticky; + top: 7rem; } -.ca-audience__cols { +.ca-flow__steps { + position: relative; display: grid; - grid-template-columns: 1fr 1fr; - gap: clamp(1.5rem, 4vw, 3rem); + gap: var(--ca-space-5); + margin: 0; + padding: 0; + list-style: none; } -@media (max-width: 50rem) { - .ca-audience__cols { - grid-template-columns: 1fr; - gap: 1.5rem; - } +.ca-flow__steps::before { + position: absolute; + inset-block: 2rem; + inset-inline-start: 1.35rem; + width: 1px; + background: linear-gradient(var(--ca-brand), var(--ca-line)); + content: ""; } -.ca-audience__col h3 { - margin: 0 0 0.75rem; - font-size: 0.95rem; - font-weight: 700; +.ca-flow__step { + position: relative; + display: grid; + grid-template-columns: 2.7rem 1fr; + gap: var(--ca-space-5); + padding: var(--ca-space-5); + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-md); + background: var(--ca-bg-panel); } -.ca-audience__col--for h3 { - color: var(--ca-term-pass); +.ca-flow__step > span { + z-index: 1; + display: grid; + width: 2.7rem; + height: 2.7rem; + place-items: center; + border: 1px solid var(--ca-brand-deep); + border-radius: 50%; + color: var(--ca-brand-bright); + background: var(--ca-bg-raised); + font-family: var(--ca-font-mono); + font-weight: 700; } -.ca-audience__col--not h3 { - color: var(--ca-text-muted); +.ca-flow__step h3 { + margin-block-start: 0; } -.ca-audience__col ul { +.ca-flow__step p { margin: 0; - padding-inline-start: 1.2rem; - color: var(--ca-text-muted); - font-size: 0.92rem; - line-height: 1.6; } -.ca-audience__col li + li { - margin-block-start: 0.6rem; +.ca-lane-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--ca-space-4); } -.ca-audience__col code { - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.9em; +.ca-lane-card { + position: relative; + display: block; + min-height: 15rem; + padding: var(--ca-space-5); + color: inherit; + text-decoration: none; + transition: + transform 160ms ease, + border-color 160ms ease; } -/* ========================================================= - TRUST ROW - A compact strip of build-time-computed stat tiles plus one qualitative - tile. Wraps to two columns, then one, on narrow viewports. - ========================================================= */ - -.ca-trust { - margin-block: 2rem; - padding-block-start: 2rem; - border-block-start: 1px solid var(--ca-slate-border); +.ca-lane-card__icon { + position: absolute; + top: var(--ca-space-5); + right: var(--ca-space-5); + width: 2.4rem; + height: 2.4rem; } -.ca-trust__row { - display: grid; - grid-template-columns: repeat(5, minmax(0, 1fr)); - gap: 1rem; - margin: 0; - padding: 0; - list-style: none; +.ca-lane-card:hover { + border-color: var(--ca-brand-deep); + transform: translateY(-3px); } -@media (max-width: 62rem) { - .ca-trust__row { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } +.ca-lane-card > code { + color: var(--ca-brand-bright); + background: transparent; + font-size: var(--ca-text-sm); } -@media (max-width: 30rem) { - .ca-trust__row { - grid-template-columns: 1fr; - } +.ca-lane-card h3 { + margin-block-start: var(--ca-space-7); } -.ca-trust__tile { - padding: 1rem; - border: 1px solid var(--ca-slate-border); - border-radius: 0.6rem; - background: var(--ca-slate-mid); +.ca-lane-card p { + margin-block-end: 0; } -.ca-trust__link { - display: block; - text-decoration: none; - color: inherit; +.ca-host-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--ca-space-4); } -.ca-trust__link:focus-visible { - outline: 2px solid var(--ca-gold); - outline-offset: 2px; +.ca-host { + padding: var(--ca-space-6); } -.ca-trust__num { - display: block; - color: var(--ca-gold-text); - font-size: clamp(1.4rem, 3vw, 1.9rem); - font-weight: 700; - line-height: 1.15; +.ca-host__top { + display: flex; + gap: var(--ca-space-4); + align-items: center; + justify-content: space-between; } -.ca-trust__tile--qual .ca-trust__num--qual { - font-size: clamp(1rem, 2vw, 1.2rem); +.ca-host__top h3 { + margin: 0; } -.ca-trust__label { - display: block; - margin-block-start: 0.3rem; - color: var(--ca-text-muted); - font-size: 0.82rem; - line-height: 1.4; +.ca-host__status { + color: var(--ca-positive); + font-family: var(--ca-font-mono); + font-size: var(--ca-text-xs); + text-transform: uppercase; } -/* ========================================================= - DOCS SOURCE MAP - A maintenance-oriented section that explains what can be edited directly and - what is generated from plugin/root sources. - ========================================================= */ - -.ca-source-map { - display: grid; - grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); - gap: clamp(1.5rem, 4vw, 3rem); - margin-block: 2rem; - padding: clamp(1.25rem, 3vw, 1.75rem); - border: 1px solid color-mix(in srgb, var(--ca-gold) 18%, transparent); - border-radius: 0.8rem; - background: - radial-gradient(circle at top left, rgba(227, 179, 65, 0.12), transparent 36%), - color-mix(in srgb, var(--ca-slate-mid) 88%, transparent); +.ca-host__status--preview { + color: var(--ca-preview); } -@media (max-width: 56rem) { - .ca-source-map { - grid-template-columns: 1fr; - } +.ca-host > code { + display: block; + width: fit-content; + margin-block: var(--ca-space-7) var(--ca-space-4); + color: var(--ca-brand-bright); + background: transparent; } -.ca-source-map__intro { - align-self: center; +.ca-hosts__footnote { + margin: var(--ca-space-5) 0 0; + color: var(--ca-ink-muted); + font-size: var(--ca-text-sm); } -.ca-source-map__eyebrow, -.ca-source-map__label { - margin: 0 0 0.55rem; - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.72rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; +.ca-fit { + display: grid; + grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); + gap: clamp(3rem, 8vw, 7rem); } -.ca-source-map__eyebrow { - color: var(--ca-ember); +.ca-fit__columns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--ca-space-6); } -.ca-source-map h2 { - margin: 0 0 0.85rem; - border: 0; - color: var(--sl-color-white); - font-size: clamp(1.45rem, 3vw, 2rem); - line-height: 1.12; +.ca-fit h3 { + margin-block-start: 0; + font-size: 1rem; } -.ca-source-map__body { - max-width: 46ch; +.ca-fit ul { margin: 0; - color: var(--ca-text-muted); - line-height: 1.65; + padding-inline-start: 1.2rem; } -.ca-source-map__body > p { - margin: 0; +.ca-fit li + li { + margin-block-start: var(--ca-space-3); } -.ca-source-map__grid { +.ca-final-cta { display: grid; - gap: 0.85rem; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--ca-space-5) var(--ca-space-7); + align-items: center; + margin-block: 0 var(--ca-space-9); + padding: clamp(2rem, 6vw, 4.5rem); + border-color: var(--ca-brand-deep); + background: + radial-gradient(circle at 90% 20%, rgb(240 185 47 / 14%), transparent 22rem), + var(--ca-bg-panel); } -.ca-source-map__card { - padding: 1rem 1.1rem; - border: 1px solid var(--ca-slate-border); - border-radius: 0.6rem; - background: rgb(11 15 20 / 0.54); +.ca-final-cta__fine { + grid-column: 1 / -1; + margin: 0; + padding-block-start: var(--ca-space-5); + border-block-start: 1px solid var(--ca-line); + color: var(--ca-ink-muted); + font-size: var(--ca-text-sm); } -.ca-source-map__card--generated { - border-color: color-mix(in srgb, var(--ca-ember) 38%, var(--ca-slate-border)); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--ca-ember) 10%, transparent), - rgb(11 15 20 / 0.54) - ); -} +@media (max-width: 64rem) { + .ca-splash { + grid-template-columns: 1fr; + min-height: auto; + } -.ca-source-map__label { - color: var(--ca-gold-text); -} + .ca-splash__copy h1 { + max-width: 11ch; + } -.ca-source-map__card .ca-source-map__body { - margin: 0; - color: var(--ca-text-muted); - font-size: 0.9rem; - line-height: 1.6; -} + .ca-splash__proof { + max-width: 48rem; + } -.ca-source-map code { - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.86em; -} + .ca-feature-grid, + .ca-lane-grid, + .ca-journey-preview__grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } -/* ========================================================= - THREE DOORS - Learn / Use / Look up: three intent-routed cards. - ========================================================= */ + .ca-host-grid { + grid-template-columns: 1fr; + } -.ca-doors { - margin-block: 2rem 3rem; - padding-block-start: 2rem; - border-block-start: 1px solid var(--ca-slate-border); + .ca-proof-strip .ca-trust__row { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } } -.ca-doors h2 { - margin-block-end: 1.25rem; - color: var(--ca-gold-text); - font-size: clamp(1.2rem, 2.5vw, 1.6rem); - font-weight: 700; -} +@media (max-width: 46rem) { + .ca-splash { + padding-block-start: var(--ca-space-8); + } -.ca-doors__grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1.25rem; -} + .ca-splash__copy h1 { + font-size: clamp(2.8rem, 15vw, 4.6rem); + } + + .ca-splash__proof { + padding: var(--ca-space-3); + } + + .ca-section { + padding-block: var(--ca-space-8); + } -@media (max-width: 50rem) { - .ca-doors__grid { + .ca-feature-grid, + .ca-lane-grid, + .ca-journey-preview__grid, + .ca-flow, + .ca-fit, + .ca-fit__columns, + .ca-final-cta { grid-template-columns: 1fr; } -} -.ca-doors__card { - padding: 1.25rem 1.5rem; - border: 1px solid var(--ca-slate-border); - border-radius: 0.6rem; - background: var(--ca-slate-mid); -} + .ca-flow__intro { + position: static; + } -.ca-doors__label { - margin: 0 0 0.5rem; - color: var(--ca-gold-text); - font-family: ui-monospace, "Cascadia Code", Consolas, monospace; - font-size: 0.78rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; -} + .ca-proof-strip .ca-trust__row { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } -.ca-doors__promise { - margin: 0 0 1rem; - color: var(--ca-text-muted); - font-size: 0.92rem; - line-height: 1.55; -} + .ca-proof-strip .ca-trust__tile--qual { + grid-column: 1 / -1; + } -.ca-doors__card ul { - margin: 0; - padding-inline-start: 1.1rem; - font-size: 0.92rem; - line-height: 1.7; + .ca-final-cta { + margin-inline: 1rem; + } } -.ca-doors__footnote { - margin: 1.5rem 0 0; - color: var(--ca-text-muted); - font-size: 0.88rem; -} +@media (max-width: 22rem) { + .ca-proof-strip .ca-trust__row { + grid-template-columns: 1fr; + } -.ca-doors__footnote a { - color: var(--ca-gold-text); + .ca-proof-strip .ca-trust__tile--qual { + grid-column: auto; + } } -/* ========================================================= - REDUCED-MOTION OVERRIDE (landing-specific) - ========================================================= */ - @media (prefers-reduced-motion: reduce) { - .ca-landing__hero, - .ca-hero, - .ca-forge, - .ca-landing__cta { + .ca-lane-card { transition: none; } - /* Suppress the motion itself, not just its easing: no hover lift. */ - .ca-landing__cta:hover { - transform: none; - } } diff --git a/site/src/styles/theme.css b/site/src/styles/theme.css index 57a069aa..bc3dbd14 100644 --- a/site/src/styles/theme.css +++ b/site/src/styles/theme.css @@ -17,14 +17,6 @@ Diagram container: .ca-diagram centers + constrains an SVG figure with accessible border - - Gate-catch terminal: - .ca-terminal animated terminal shell for the gate-catch demo - .ca-terminal__body scrollable output area - .ca-terminal__line one output line (role="listitem") - .ca-terminal__prompt shell prompt prefix - .ca-terminal__error red-tinted blocked-commit line - .ca-terminal__pass green-tinted gate-pass line */ :root { @@ -57,15 +49,6 @@ --ca-badge-stable-text: #88b4ea; /* ---- Terminal palette ---- */ - --ca-term-bg: #0d1117; - --ca-term-border: color-mix(in srgb, var(--ca-gold) 18%, transparent); - --ca-term-header: #161c24; - --ca-term-text: #c9d1d9; - --ca-term-prompt: var(--ca-gold-dim); - --ca-term-error: #ff7b72; - --ca-term-pass: #3fb950; - --ca-term-dim: #6e7b8b; - /* ---- Starlight accent overrides ---- */ --sl-color-accent-low: var(--ca-gold-low); --sl-color-accent: #9a6e1a; @@ -238,7 +221,7 @@ .ca-badge { display: inline-flex; align-items: center; - font-family: ui-monospace, "Cascadia Code", Consolas, "Courier New", monospace; + font-family: var(--ca-font-mono); font-size: 0.65rem; font-weight: 600; letter-spacing: 0.06em; @@ -399,136 +382,6 @@ font-weight: 700; } -/* ========================================================= - GATE-CATCH TERMINAL - Usage:
    - -
      -
    1. - $ - git commit -m "add feature" -
    2. -
    3. - H-09 BLOCKED: banned crypto pattern detected. -
    4. -
    5. - Gate passed after fix. -
    6. -
    -
    - - Animation: lines fade in sequentially via --ca-term-delay CSS custom property. - prefers-reduced-motion: collapse animation, render transcript statically. - ========================================================= */ - -.ca-terminal { - background: var(--ca-term-bg); - border: 1px solid var(--ca-term-border); - border-radius: 0.6rem; - overflow: hidden; - font-family: ui-monospace, "Cascadia Code", Consolas, "Courier New", monospace; - font-size: 0.825rem; - line-height: 1.6; - margin-block: 1.5rem; - /* Prevent CLS when animation runs */ - contain: layout; -} - -.ca-terminal__titlebar { - display: flex; - align-items: center; - gap: 0.4rem; - padding: 0.5rem 0.875rem; - background: var(--ca-term-header); - border-block-end: 1px solid color-mix(in srgb, var(--ca-gold) 10%, transparent); -} - -.ca-terminal__dot { - width: 0.625rem; - height: 0.625rem; - border-radius: 50%; - background: var(--ca-slate-border); - display: inline-block; - flex-shrink: 0; -} - -.ca-terminal__title { - margin-inline-start: auto; - font-size: 0.7rem; - letter-spacing: 0.08em; - color: var(--ca-term-dim); - text-transform: lowercase; -} - -.ca-terminal__body { - padding: 0.875rem 1rem; - margin: 0; - list-style: none; - overflow-x: auto; - scrollbar-width: thin; - scrollbar-color: var(--ca-slate-border) transparent; -} - -.ca-terminal__line { - display: flex; - gap: 0.6rem; - align-items: flex-start; - color: var(--ca-term-text); - /* Wrap long lines instead of clipping/scrolling in the narrow hero column. - pre-wrap keeps intentional spacing; overflow-wrap breaks overlong tokens - (paths, sed commands) that have no natural break point. The prompt hangs at - the first wrapped line thanks to align-items: flex-start. */ - white-space: pre-wrap; - overflow-wrap: break-word; - /* Animation: each line fades in after its delay */ - opacity: 0; - animation: ca-term-fadein 0.25s ease forwards; - animation-delay: var(--ca-term-delay, 0s); -} - -/* Flex children must be allowed to shrink below their content size to wrap; the - prompt keeps its width via flex-shrink: 0 (below). */ -.ca-terminal__line > span { - min-width: 0; -} - -.ca-terminal__prompt { - color: var(--ca-term-prompt); - user-select: none; - flex-shrink: 0; -} - -.ca-terminal__line--error { - color: var(--ca-term-error); -} - -.ca-terminal__line--pass { - color: var(--ca-term-pass); -} - -.ca-terminal__line--dim { - color: var(--ca-term-dim); -} - -@keyframes ca-term-fadein { - from { opacity: 0; transform: translateY(3px); } - to { opacity: 1; transform: translateY(0); } -} - -/* Reduced-motion: skip animation, render all lines immediately and statically */ -@media (prefers-reduced-motion: reduce) { - .ca-terminal__line { - opacity: 1; - animation: none; - transform: none; - } -} - /* ========================================================= INLINE SEARCH (custom Search.astro — replaces the modal) The box lives in the header; results drop in an absolutely diff --git a/site/test/content/learning-contract.test.ts b/site/test/content/learning-contract.test.ts new file mode 100644 index 00000000..f9e8fc11 --- /dev/null +++ b/site/test/content/learning-contract.test.ts @@ -0,0 +1,50 @@ +import { readdirSync, readFileSync } from "node:fs"; +import { extname, join, relative } from "node:path"; +import { describe, expect, it } from "vitest"; + +const docsRoot = join(process.cwd(), "src", "content", "docs"); + +function contentFiles(directory: string): string[] { + return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const path = join(directory, entry.name); + if (entry.isDirectory()) return contentFiles(path); + return [".md", ".mdx"].includes(extname(entry.name)) ? [path] : []; + }); +} + +function frontmatter(source: string): string { + const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---/); + return match?.[1] ?? ""; +} + +describe("hand-authored learning contract", () => { + const handPages = contentFiles(docsRoot).filter((path) => { + const route = relative(docsRoot, path).replaceAll("\\", "/"); + return !route.startsWith("reference/") + && route !== "index.mdx" + && route !== "changelog.md"; + }); + + it("gives every hand-authored page an outcome, prerequisite context, and proof", () => { + const failures = handPages.flatMap((path) => { + const metadata = frontmatter(readFileSync(path, "utf8")); + const missing = ["journey:", "level:", "time:", "outcome:", "prerequisites:", "proof:"] + .filter((field) => !metadata.includes(field)); + return missing.length + ? [`${relative(docsRoot, path)}: ${missing.join(", ")}`] + : []; + }); + expect(failures).toEqual([]); + }); + + it("keeps hand-authored page descriptions specific enough to orient search results", () => { + const failures = handPages.flatMap((path) => { + const metadata = frontmatter(readFileSync(path, "utf8")); + const description = metadata.match(/^description:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? ""; + return description.length >= 45 + ? [] + : [`${relative(docsRoot, path)}: ${description.length} characters`]; + }); + expect(failures).toEqual([]); + }); +}); diff --git a/site/test/generator/configuration-reference.test.ts b/site/test/generator/configuration-reference.test.ts new file mode 100644 index 00000000..276cd686 --- /dev/null +++ b/site/test/generator/configuration-reference.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from "vitest"; +import { readdirSync, readFileSync } from "node:fs"; +import { dirname, extname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + CONFIGURATION_ENTRIES, + renderConfigurationReference, +} from "../../scripts/generator/configuration-reference"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); +const implementationExtensions = new Set([".py", ".ts", ".js", ".mjs", ".json", ".sh"]); + +function implementationCorpus(root: string): string { + const contents: string[] = []; + const walk = (dir: string) => { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (entry.name === "node_modules" || entry.name === "test" || entry.name === "tests") continue; + const path = join(dir, entry.name); + if (entry.isDirectory()) walk(path); + else if (implementationExtensions.has(extname(entry.name))) { + contents.push(readFileSync(path, "utf8")); + } + } + }; + walk(root); + return contents.join("\n"); +} + +describe("configuration reference", () => { + it("keeps names unique and uses only public prefixes", () => { + const names = CONFIGURATION_ENTRIES.map((entry) => entry.name); + expect(new Set(names).size).toBe(names.length); + expect(names.every((name) => name === "NO_COLOR" || /^(CODEARBITER|FARM)_/.test(name))).toBe(true); + }); + + it("documents the opt-in and spending-sensitive boundaries", () => { + const page = renderConfigurationReference(); + expect(page).toContain("CODEARBITER_DEV"); + expect(page).toContain("CODEARBITER_BASE_BRANCH"); + expect(page).toContain("CODEARBITER_BABYSIT_ONRED"); + expect(page).toContain("CODEARBITER_PRUNE"); + expect(page).toContain("CODEARBITER_PRUNE_METRICS"); + expect(page).toContain("FARM_API_KEY"); + expect(page).toContain("FARM_ALLOW_EXTERNAL_WORKTREE_ROOT"); + expect(page).toContain("FARM_MUTATION_ESCALATE_BELOW"); + expect(page).toContain("multiply provider token use"); + expect(page).toContain("never commit it"); + expect(page).toContain("destructive-scope override"); + }); + + it("covers every operator control promised by the farm and watch docs", () => { + const names = new Set(CONFIGURATION_ENTRIES.map((entry) => entry.name)); + const required = [ + "CODEARBITER_BABYSIT", + "CODEARBITER_BABYSIT_ONRED", + "FARM_BASE_BRANCH", + "FARM_INTEGRATION_BRANCH", + "FARM_API_MAX_RETRIES", + "FARM_ENTITLEMENT_PROBE_TIMEOUT_MS", + "FARM_ABORT_ESCALATION_RATE", + "FARM_ABORT_MIN_TASKS", + "FARM_RUN_ID", + "FARM_MUTATION", + "FARM_MUTATION_SAMPLE", + "FARM_MUTATION_BUDGET_MS", + "FARM_MUTATION_WARN_BELOW", + "FARM_MUTATION_ESCALATE_BELOW", + "FARM_MUTATION_CMD", + ]; + expect(required.filter((name) => !names.has(name))).toEqual([]); + }); + + it("renders every typed entry exactly once", () => { + const page = renderConfigurationReference(); + for (const entry of CONFIGURATION_ENTRIES) { + expect(page.match(new RegExp(`^\\| \\\`${entry.name}\\\` \\|`, "gm"))).toHaveLength(1); + } + }); + + it("backs every documented variable name with shipped implementation code", () => { + const source = implementationCorpus(join(repoRoot, "plugins")); + const missing = CONFIGURATION_ENTRIES + .map((entry) => entry.name) + .filter((name) => !source.includes(name)); + expect(missing).toEqual([]); + }); + + it("is deterministic", () => { + expect(renderConfigurationReference()).toBe(renderConfigurationReference()); + }); +}); diff --git a/site/test/generator/curated-coverage.test.ts b/site/test/generator/curated-coverage.test.ts new file mode 100644 index 00000000..53263653 --- /dev/null +++ b/site/test/generator/curated-coverage.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; +import { readdirSync, readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; + +const siteRoot = resolve(import.meta.dirname, "../.."); +const repoRoot = resolve(siteRoot, ".."); +const pluginRoot = join(repoRoot, "plugins", "ca"); +const curatedRoot = join(siteRoot, "src", "curated"); + +function sourceEntities(): string[] { + const commands = readdirSync(join(pluginRoot, "commands")) + .filter((name) => name.endsWith(".md")) + .map((name) => `commands/${name.replace(/\.md$/, "")}`); + + const agents = readdirSync(join(pluginRoot, "agents")) + .filter((name) => name.endsWith(".md") && name.toLowerCase() !== "index.md") + .map((name) => `agents/${name.replace(/\.md$/, "")}`); + + const skills = readdirSync(join(pluginRoot, "skills"), { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => `skills/${entry.name}`); + + return [...commands, ...agents, ...skills].sort(); +} + +function curatedEntities(): string[] { + const entities: string[] = []; + for (const collection of ["commands", "agents", "skills"]) { + for (const name of readdirSync(join(curatedRoot, collection))) { + if (!name.endsWith(".md")) continue; + const source = readFileSync(join(curatedRoot, collection, name), "utf8"); + const entity = source.match(/^entity:\s*(\S+)\s*$/m)?.[1]; + if (entity) entities.push(entity); + } + } + return entities.sort(); +} + +describe("generated reference curated coverage", () => { + it("has curated reader framing for every published command, agent, and skill", () => { + const source = sourceEntities(); + const curated = new Set(curatedEntities()); + const missing = source.filter((entity) => !curated.has(entity)); + + expect(missing, `Missing curated reference framing: ${missing.join(", ")}`).toEqual([]); + }); +}); diff --git a/site/test/generator/generate.test.ts b/site/test/generator/generate.test.ts index eedc0c1f..2e7c80e4 100644 --- a/site/test/generator/generate.test.ts +++ b/site/test/generator/generate.test.ts @@ -117,7 +117,7 @@ describe("generate", () => { const result = generate(pluginDir, outDir); const anyPage = result.pages[0]; expect(anyPage.markdown).toContain("## Source"); - expect(anyPage.markdown).toContain('
    '); + expect(anyPage.markdown).toContain('
    '); }); it("cleans stale files out of outDir before writing", () => { @@ -192,6 +192,8 @@ describe("generate", () => { "[How a Request Flows](/overview/#how-a-request-flows)", ); expect(indexContent).toMatch(/command routes to an owning skill.*specialist agents/); + expect(indexContent).toContain("Use the catalog from left to right."); + expect(indexContent).toContain("agents are not a second command surface"); }); it("renders one markdown table per non-empty collection, headers matching the collection", () => { diff --git a/site/test/generator/host-command-catalog.test.ts b/site/test/generator/host-command-catalog.test.ts new file mode 100644 index 00000000..98fd10ac --- /dev/null +++ b/site/test/generator/host-command-catalog.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + commandHostAvailability, + loadHostCommandCatalogs, + parseHostCommandCatalog, +} from "../../scripts/generator/host-command-catalog"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); + +describe("host command catalogs", () => { + it("parses each adapter's native command syntax", () => { + expect( + [...parseHostCommandCatalog("claude", "| `/ca:statusline` | none | status |")], + ).toEqual(["statusline"]); + expect( + [...parseHostCommandCatalog("codex", "| `$ca-doctor` | none | doctor |")], + ).toEqual(["doctor"]); + expect( + [...parseHostCommandCatalog("pi", "| `/ca-prune` | status | prune |")], + ).toEqual(["prune"]); + }); + + it("derives availability independently for all three hosts", () => { + const availability = commandHostAvailability("prune", { + claude: new Set(["prune"]), + codex: new Set(["doctor"]), + pi: new Set(["prune"]), + }); + expect(availability).toEqual({ claude: true, codex: false, pi: true }); + }); + + it("matches the current shipped statusline and prune boundaries", () => { + const catalogs = loadHostCommandCatalogs(resolve(repoRoot, "plugins/ca")); + expect(commandHostAvailability("statusline", catalogs)).toEqual({ + claude: true, + codex: false, + pi: false, + }); + expect(commandHostAvailability("prune", catalogs)).toEqual({ + claude: true, + codex: false, + pi: true, + }); + }); +}); diff --git a/site/test/generator/render-agent-page.test.ts b/site/test/generator/render-agent-page.test.ts index 334069cc..c94bdbd8 100644 --- a/site/test/generator/render-agent-page.test.ts +++ b/site/test/generator/render-agent-page.test.ts @@ -70,7 +70,7 @@ describe("renderAgentPage", () => { it("always renders a source embed, even with no curated content", () => { const md = renderAgentPage(input({ name: "myagent", tools: "Read" })); expect(md).toContain("## Source"); - expect(md).toContain('
    '); + expect(md).toContain('
    '); }); it("places Model tier / Tools before the curated body", () => { diff --git a/site/test/generator/render-command-page.test.ts b/site/test/generator/render-command-page.test.ts index 9c30e42d..5077451f 100644 --- a/site/test/generator/render-command-page.test.ts +++ b/site/test/generator/render-command-page.test.ts @@ -61,7 +61,7 @@ describe("renderCommandPage", () => { it("always renders a source embed, even with no curated content", () => { const md = renderCommandPage(input({ name: "commit", description: "d" })); expect(md).toContain("## Source"); - expect(md).toContain('
    '); + expect(md).toContain('
    '); expect(md).toContain("plugins/ca/commands/commit.md"); expect(md).toContain("v2.8.11"); }); @@ -114,4 +114,31 @@ describe("renderCommandPage", () => { expect(md).not.toContain("## Gates"); expect(md).not.toContain("## Related"); }); + + it("renders only the host syntaxes present in the shipped adapter catalogs", () => { + const md = renderCommandPage( + input({ + name: "statusline", + description: "d", + commandHosts: { claude: true, codex: false, pi: false }, + }), + ); + expect(md).toContain("/ca:statusline"); + expect(md).not.toContain("$ca-statusline"); + expect(md).not.toContain("/ca-statusline"); + expect(md.match(/Not shipped/g)).toHaveLength(2); + }); + + it("can show a command shared by Claude Code and Pi but absent from Codex", () => { + const md = renderCommandPage( + input({ + name: "prune", + description: "d", + commandHosts: { claude: true, codex: false, pi: true }, + }), + ); + expect(md).toContain("/ca:prune"); + expect(md).toContain("/ca-prune"); + expect(md).not.toContain("$ca-prune"); + }); }); diff --git a/site/test/generator/render-hooks-reference.test.ts b/site/test/generator/render-hooks-reference.test.ts index f7bcb719..4a075916 100644 --- a/site/test/generator/render-hooks-reference.test.ts +++ b/site/test/generator/render-hooks-reference.test.ts @@ -49,6 +49,11 @@ describe("buildEventMap / eventsFor", () => { expect(eventsFor("git-enforce.py", map)).toEqual(["git backstop"]); }); + it("attributes extracted shared-library gates to their registered entry hook", () => { + const map = buildEventMap(hooksJson); + expect(eventsFor("_bashguardlib.py", map)).toEqual(["PreToolUse (Bash|PowerShell)"]); + }); + it("returns an empty list for a script registered nowhere", () => { const map = buildEventMap(hooksJson); expect(eventsFor("nowhere.py", map)).toEqual([]); @@ -86,7 +91,7 @@ describe("renderHooksReference", () => { it("renders a line-pinned permalink in the expected format", () => { expect(md).toContain( - "https://github.com/arbiterForge/codeArbiter/blob/v2.8.11/plugins/ca/hooks/pre-bash.py#L10", + "https://github.com/arbiterForge/codeArbiter/blob/main/plugins/ca/hooks/pre-bash.py#L10", ); }); diff --git a/site/test/generator/render-skill-page.test.ts b/site/test/generator/render-skill-page.test.ts index 62a26e73..1fe87160 100644 --- a/site/test/generator/render-skill-page.test.ts +++ b/site/test/generator/render-skill-page.test.ts @@ -53,7 +53,7 @@ describe("renderSkillPage", () => { it("always renders a source embed, even with no curated content", () => { const md = renderSkillPage(input({ name: "tdd", description: "d" })); expect(md).toContain("## Source"); - expect(md).toContain('
    '); + expect(md).toContain('
    '); }); it("merges curated body, gates table, and related links", () => { diff --git a/site/test/generator/render-source-embed.test.ts b/site/test/generator/render-source-embed.test.ts index 0a9b6516..b6a1e033 100644 --- a/site/test/generator/render-source-embed.test.ts +++ b/site/test/generator/render-source-embed.test.ts @@ -29,13 +29,13 @@ describe("renderSourceEmbed", () => { it("pins the View in repo link to the given version tag", () => { const md = renderSourceEmbed("x", "plugins/ca/commands/sprint.md", "2.8.11"); expect(md).toContain( - 'View in repo', + 'View in repo', ); }); it("renders the details/summary structure with the repo-relative path and version", () => { const md = renderSourceEmbed("x", "plugins/ca/commands/sprint.md", "2.8.11"); - expect(md).toContain('
    '); + expect(md).toContain('
    '); expect(md).toContain( "Source — plugins/ca/commands/sprint.md (v2.8.11)", ); diff --git a/site/test/generator/source-ref.test.ts b/site/test/generator/source-ref.test.ts new file mode 100644 index 00000000..fd493447 --- /dev/null +++ b/site/test/generator/source-ref.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; + +import { repositorySourceRef } from "../../scripts/generator/source-ref"; + +describe("repositorySourceRef", () => { + it("uses the exact GitHub Actions commit when available", () => { + const sha = "0123456789abcdef0123456789abcdef01234567"; + expect(repositorySourceRef({ GITHUB_SHA: sha })).toBe(sha); + }); + + it("uses main for local and malformed environments", () => { + expect(repositorySourceRef({})).toBe("main"); + expect(repositorySourceRef({ GITHUB_SHA: "pull/123/merge" })).toBe("main"); + }); +}); diff --git a/site/test/generator/truncate-description.test.ts b/site/test/generator/truncate-description.test.ts index a368f96c..6984d1f2 100644 --- a/site/test/generator/truncate-description.test.ts +++ b/site/test/generator/truncate-description.test.ts @@ -21,4 +21,16 @@ describe("truncateDescription", () => { it("returns an empty string unchanged", () => { expect(truncateDescription("")).toBe(""); }); + + it("does not split at vs. abbreviations", () => { + expect( + truncateDescription("Surface persona vs. docs vs. code. Present both sides."), + ).toBe("Surface persona vs. docs vs. code."); + }); + + it("does not split at e.g. or i.e. abbreviations", () => { + expect( + truncateDescription("Use a source, e.g. the manifest. Then verify it."), + ).toBe("Use a source, e.g. the manifest."); + }); }); diff --git a/site/test/landing/landing-page.test.ts b/site/test/landing/landing-page.test.ts index 94d2d8ed..8d22a669 100644 --- a/site/test/landing/landing-page.test.ts +++ b/site/test/landing/landing-page.test.ts @@ -1,23 +1,6 @@ -/** - * TDD Phase 1 obligations — bespoke landing page (AC 6, 7, 8, 12, 15, 16, 19). - * - * These tests read the landing source files because the full Astro build is not - * run inside vitest. The "landing source" is the union of: - * - src/content/docs/index.mdx (frontmatter + MDX body) - * - src/components/GateCatchTerminal.astro - * - src/components/ForgeShowcase.astro - * - src/styles/landing.css - * - * Source-level assertions are reliable here because: - * - AC 6/7/8/12/15/16: structural/markup obligations are authored in the above - * files and render directly to the built HTML without transformation. - * - AC 19: em-dash cap is a source-level prose check by spec definition. - * - * The design-quality-reviewer (AC-11) is dispatched separately as a visual gate. - */ - -import { describe, it, expect } from "vitest"; -import { readFileSync, existsSync } from "node:fs"; +import { describe, expect, it } from "vitest"; +import { createHash } from "node:crypto"; +import { existsSync, readFileSync, statSync } from "node:fs"; import { fileURLToPath } from "node:url"; import * as path from "node:path"; @@ -28,372 +11,157 @@ function readSrc(rel: string): string { return readFileSync(path.join(siteRoot, rel), "utf8"); } -// --------------------------------------------------------------------------- -// Fixtures -// --------------------------------------------------------------------------- - const indexMdx = readSrc("src/content/docs/index.mdx"); -const terminalCmp = readSrc("src/components/GateCatchTerminal.astro"); -const installCmp = readSrc("src/components/InstallTerminal.astro"); -const searchCmp = readSrc("src/components/Search.astro"); +const proofCmp = readSrc("src/components/HookProof.astro"); const astroConfig = readSrc("astro.config.mjs"); const landingCss = readSrc("src/styles/landing.css"); - -/** Combined source of the landing page artifacts. The Feature Forge showcase - * moved off the home page into its own section — see feature-forge-content.test.ts. */ -const landingSrc = indexMdx + "\n" + terminalCmp + "\n" + installCmp; - -// --------------------------------------------------------------------------- -// AC-6: Bespoke landing — stock CardGrid replaced -// --------------------------------------------------------------------------- - -describe("AC-6: bespoke landing replaces stock CardGrid", () => { - it("index.mdx does not contain the stock component from Starlight", () => { - expect(indexMdx).not.toMatch(//); - }); - - it("landing source contains the gate-catch terminal (ca-terminal)", () => { - expect(landingSrc).toContain("ca-terminal"); - }); - - it("index.mdx contains the bespoke hero section (ca-hero)", () => { - expect(indexMdx).toContain("ca-hero"); - }); -}); - -// --------------------------------------------------------------------------- -// AC-7: prefers-reduced-motion honored for terminal animation -// --------------------------------------------------------------------------- - -describe("AC-7: terminal honors prefers-reduced-motion", () => { - it("theme.css contains a prefers-reduced-motion media query that collapses animation", () => { - const themeCss = readSrc("src/styles/theme.css"); - expect(themeCss).toContain("prefers-reduced-motion"); - expect(themeCss).toContain("animation: none"); - }); - - it("terminal lines carry the ca-terminal__line class (so the reduced-motion rule applies)", () => { - expect(terminalCmp).toContain("ca-terminal__line"); - }); - - it("theme.css reduced-motion block sets opacity: 1 so all lines are visible statically", () => { - const themeCss = readSrc("src/styles/theme.css"); - expect(themeCss).toMatch(/prefers-reduced-motion[\s\S]*?opacity:\s*1/); - }); -}); - -// --------------------------------------------------------------------------- -// AC-8: Terminal transcript is real DOM text -// --------------------------------------------------------------------------- - -describe("AC-8: terminal transcript is real DOM text, not canvas/image", () => { - it("terminal component does not use elements", () => { - expect(terminalCmp).not.toContain(" elements for the transcript", () => { - // The terminal transcript itself must not be an image - // (img is allowed in diagrams but not inside the terminal body) - const bodySection = terminalCmp.match(/ca-terminal__body[\s\S]*/)?.[0] ?? ""; - expect(bodySection).not.toContain(" { - expect(terminalCmp).toMatch(/git commit|ca:commit/i); - }); - - it("terminal body has role=list for screen-reader grouping", () => { - expect(terminalCmp).toContain('role="list"'); - }); - - it("terminal lines have role=listitem", () => { - expect(terminalCmp).toContain('role="listitem"'); - }); - - it("terminal region has an aria-label", () => { - expect(terminalCmp).toMatch(/aria-label="[^"]+"/); - }); -}); - -// --------------------------------------------------------------------------- -// AC-16: Above-the-fold: what / why / first command + single primary CTA -// --------------------------------------------------------------------------- - -describe("AC-16: above-fold hero answers what/why/first command + one primary CTA", () => { - it("hero tagline (the 'why') is present in the bespoke hero body", () => { - // The landing moved off Starlight's splash `hero:` frontmatter to a bespoke - // two-column hero in the MDX body (doc template + sidebar). The tagline now - // lives in a .ca-landing__tagline element, not frontmatter. - expect(indexMdx).toContain("ca-landing__tagline"); - expect(indexMdx).toMatch(/real stops/i); +const designCss = readSrc("src/styles/design-system.css"); + +describe("first-class product splash", () => { + it("uses the sidebar-free Starlight splash shell", () => { + expect(indexMdx).toMatch(/^template:\s*splash$/m); + expect(indexMdx).toContain('class="ca-splash"'); + expect(landingCss).toContain(":root:has(.ca-landing) .main-pane"); + }); + + it("leads with the reader outcome and supported hosts", () => { + expect(indexMdx).toContain("Hard gates for agentic coding."); + expect(indexMdx).toContain("You decide. codeArbiter enforces."); + expect(indexMdx).toContain("Claude Code"); + expect(indexMdx).toContain("Codex"); + expect(indexMdx).toContain("Pi preview"); + }); + + it("has one primary action above the fold", () => { + const primaryMatches = indexMdx.match(/ca-button--primary/g) ?? []; + expect(primaryMatches).toHaveLength(2); + const splash = indexMdx.match(/
    /)?.[0] ?? ""; + expect(splash.match(/ca-button--primary/g)).toHaveLength(1); + expect(splash).toContain("Protect a repository"); + }); + + it("shows a faithful direct-hook replay, not a fictional terminal sequence", () => { + expect(indexMdx).toContain(""); + expect(indexMdx).toContain("rendered as a faithful replay"); + expect(indexMdx).toContain("not host discovery or registration"); + expect(indexMdx).not.toContain(""); + expect(indexMdx).not.toContain("GateCatchTerminal"); + expect(indexMdx).not.toContain("ca-source-map"); + }); + + it("follows the adopter story through proof, guarantees, flow, hosts, fit, and final CTA", () => { + for (const className of [ + "ca-proof-strip", + "ca-guarantees", + "ca-flow", + "ca-hosts", + "ca-fit", + "ca-final-cta", + ]) { + expect(indexMdx).toContain(className); + } }); - it("exactly one primary CTA exists in the hero", () => { - const primaryMatches = indexMdx.match(/ca-landing__cta--primary/g); - expect(primaryMatches).not.toBeNull(); - expect(primaryMatches!.length).toBe(1); + it("links lifecycle and trust claims to their owning docs", () => { + expect(indexMdx).toContain("./enforcement/"); + expect(indexMdx).toContain("./codearbiter-directory/"); + expect(indexMdx).toContain("./concepts/auditability/"); + expect(indexMdx).toContain("./getting-started/compatibility/"); + expect(indexMdx).toContain("./guides/uninstalling/"); }); - it("primary CTA uses a base-safe page-relative link (no hardcoded base, no leading slash)", () => { - // The hero CTAs are raw in the MDX body. A page-relative `./…` - // link resolves against the page URL (served at /codeArbiter/) and - // auto-corrects if the base changes — unlike a leading-slash `/…` link, - // which would 404 under the /codeArbiter base on GH Pages, or a hardcoded - // `/codeArbiter/…`, which silently desyncs if the base ever changes. - expect(indexMdx).toMatch(/href="\.\/getting-started\/install\//); + it("uses page-relative internal links instead of a hardcoded deployment base", () => { expect(indexMdx).not.toMatch(/href="\/codeArbiter/); + expect(indexMdx).toMatch(/href="\.\/getting-started\/install\//); }); - it("first command is shown in the hero section", () => { - // The hero now leads with the install commands (the new user's actual first - // commands), rendered in the animated install terminal. The old - // /ca:feature|/ca:fix|/ca:commit sample box was removed in favor of this - // get-it-running demo. - expect(landingSrc).toMatch(/\/plugin install/); - }); - - it("hero body answers 'what' (the orchestrator description)", () => { - expect(indexMdx).toMatch(/orchestrat|gated|lane/i); - }); -}); - -// --------------------------------------------------------------------------- -// Install demo: the get-it-running terminal replaced the sample-command box -// --------------------------------------------------------------------------- - -describe("install demo terminal (get-it-running below the fold)", () => { - it("index.mdx imports and renders the InstallTerminal component", () => { - expect(indexMdx).toContain("InstallTerminal"); - }); - - it("the install title and shared-host subtext are present above the demo", () => { - expect(indexMdx).toMatch(/Get the Arbiter on Your Case/); - expect(indexMdx).toMatch(/Claude Code or Codex/i); - }); - - it("the install title is a real heading, not a paragraph styled as one", () => { - // Hierarchy honesty + a11y: a section title must be an

    so it joins the - // document outline and screen-reader heading navigation, not a

    wearing - // h1-sized type. - expect(indexMdx).toMatch( - /

    Get the Arbiter on Your Case<\/h2>/, - ); - }); - - it("the old sample-command box was removed", () => { - expect(indexMdx).not.toContain("ca-hero__first-command"); - }); - - it("the install terminal shows both install commands", () => { - expect(installCmp).toContain("/plugin marketplace add arbiterForge/codeArbiter"); - expect(installCmp).toContain("/plugin install ca@codearbiter"); - }); - - it("the install terminal runs /ca:statusline as the third command", () => { - expect(installCmp).toContain("/ca:statusline"); - }); - - it("the real rendered statusline sits below the terminal (single-source asset)", () => { - // The bar is shown below the terminal in index.mdx, using the canonical - // statusline.png — the same asset the statusline guide and README serve — - // via the base-safe root-absolute literal sanctioned for .mdx pages. - expect(indexMdx).toContain("ca-hero__statusline"); - expect(indexMdx).toContain("/codeArbiter/diagrams/statusline.png"); - }); - - it("the install terminal reuses the animated terminal contract (ca-terminal lines)", () => { - expect(installCmp).toContain("ca-terminal__line"); - expect(installCmp).toContain('role="list"'); - expect(installCmp).toMatch(/aria-label="[^"]+"/); - }); - - it("the install terminal carries the full screen-reader contract", () => { - expect(installCmp).toContain('role="region"'); - expect(installCmp).toContain('role="listitem"'); - expect(installCmp).toContain('aria-hidden="true"'); - }); -}); - -describe("Claude Code and Codex launch contract", () => { - it("landing metadata, heading, and tagline name the governance hosts", () => { - expect(indexMdx).toMatch(/Claude Code.*Codex|Codex.*Claude Code/); - expect(indexMdx).toContain( - "Shared enforcement and project-context parity across Claude Code, Codex, and Pi", - ); - }); - - it("install terminal presents both host paths", () => { - expect(installCmp).toContain("Claude Code"); - expect(installCmp).toContain("Codex"); - expect(installCmp).toContain("codex plugin marketplace add arbiterForge/codeArbiter"); - expect(installCmp).toContain("codex plugin add ca-codex@codearbiter"); - expect(installCmp).toContain("$ca-doctor"); + it("keeps raw HTML paragraphs on one line to avoid nested MDX paragraphs", () => { + expect(indexMdx).not.toMatch(/\s*\n/); }); }); -// --------------------------------------------------------------------------- -// Inline search: custom Search.astro override (replaces Starlight's modal) -// --------------------------------------------------------------------------- - -describe("inline search override (replaces Starlight's modal search)", () => { - it("is registered as the Starlight Search component override", () => { - expect(astroConfig).toMatch(/Search:\s*["']\.\/src\/components\/Search\.astro["']/); - }); - - it("defines the custom element that drives it", () => { - expect(searchCmp).toContain("class CaSiteSearch extends HTMLElement"); - expect(searchCmp).toContain('customElements.define("ca-site-search"'); - }); - - it("uses a semantic search form with a search input", () => { - expect(searchCmp).toContain('role="search"'); - expect(searchCmp).toContain('type="search"'); - }); - - it("implements the WAI-ARIA combobox pattern (combobox + listbox + option)", () => { - // Input is a combobox controlling a listbox popup; results are options the - // input points at via aria-activedescendant (virtual focus). - expect(searchCmp).toContain('role="combobox"'); - expect(searchCmp).toContain('aria-autocomplete="list"'); - expect(searchCmp).toContain('aria-controls="ca-search-listbox"'); - expect(searchCmp).toContain('aria-activedescendant'); - expect(searchCmp).toContain('id="ca-search-listbox"'); - expect(searchCmp).toContain('role="listbox"'); - // Result options are created in the script with role="option". - expect(searchCmp).toMatch(/setAttribute\("role",\s*"option"\)/); - }); - - it("carries the rest of the search ARIA contract (labelled, expands, live count)", () => { - expect(searchCmp).toMatch(/aria-label="[^"]+"/); - expect(searchCmp).toContain('aria-expanded="false"'); - expect(searchCmp).toContain('aria-live="polite"'); - }); - - it("wires the combobox keyboard contract (arrows move, Enter activates)", () => { - expect(searchCmp).toContain('"ArrowDown"'); - expect(searchCmp).toContain('"ArrowUp"'); - expect(searchCmp).toContain('"Enter"'); - // The highlight is tracked on the input via aria-activedescendant. - expect(searchCmp).toMatch(/setAttribute\("aria-activedescendant"/); - }); - - it("drives the Pagefind index via the base-safe runtime path", () => { - // Pagefind has no dev index, so the import is a runtime path (vite-ignored) - // built from BASE_URL, not a bundled/hardcoded one. - expect(searchCmp).toContain("import.meta.env.BASE_URL"); - expect(searchCmp).toMatch(/\/pagefind\/pagefind\.js/); - }); - - it("the active option is visibly highlighted (no invisible selection)", () => { - // Guards the WCAG 2.4.7 concern: focus stays in the input, so the active - // option (aria-selected) carries a real gold ring, not just a ~1.1:1 - // background shift. - const themeCss = readSrc("src/styles/theme.css"); - expect(themeCss).toMatch( - /\.ca-search__result-link\[aria-selected="true"\]\s*\{[^}]*outline:\s*2px solid var\(--ca-gold\)/, - ); +describe("captured gate proof", () => { + it("ships accessible video controls and an exact text transcript", () => { + expect(proofCmp).toContain(" { + const proofPath = path.join(siteRoot, "src/assets/proof/hook-proof.json"); + const proof = JSON.parse(readFileSync(proofPath, "utf8")); + const hookPath = path.resolve(siteRoot, "..", proof.source); + const digest = createHash("sha256").update(readFileSync(hookPath)).digest("hex"); + + expect(proof.sourceSha256).toBe(digest); + expect(proof.invocation.exitCode).toBe(2); + expect(proof.invocation.commandExecuted).toBe(false); + expect(proof.evidenceKind).toBe("direct-hook-invocation-rendered-replay"); + expect(proof.hostDiscoveryProven).toBe(false); + expect(proof.invocation.stderr).toContain("BLOCKED [H-03]"); + expect(proof.fixture.stagedAfter).toBe(""); + expect(proof.fixture.statusAfter).toContain("?? note.txt"); + expect(proof.fixture.gateEvent).toContain("BLOCK [H-03]"); + }); + + it("keeps both efficient video formats and a poster in the repository", () => { + for (const asset of ["hook-proof.mp4", "hook-proof.webm", "hook-proof-poster.webp"]) { + expect(statSync(path.join(siteRoot, "src/assets/proof", asset)).size).toBeGreaterThan(10_000); + } }); - it("Search.astro prose contains ≤3 em-dashes", () => { - const text = searchCmp + it("does not overuse em dashes in adopter-facing copy", () => { + const prose = indexMdx + .replace(/^---[\s\S]*?---\n/, "") .replace(/<[^>]+>/g, " ") - .replace(/\/\*[\s\S]*?\*\//g, " "); - expect((text.match(/—/g) ?? []).length).toBeLessThanOrEqual(3); + .replace(/^import\s+.*$/gm, ""); + expect((prose.match(/—/g) ?? []).length).toBeLessThanOrEqual(3); }); }); -// --------------------------------------------------------------------------- -// AC-19: Em-dash cap (≤3 per page, ≤1 per paragraph) on prose -// --------------------------------------------------------------------------- - -describe("AC-19: em-dash cap on landing prose", () => { - /** - * Strips frontmatter, code blocks, HTML/JSX tags, and import statements - * so the cap applies only to hand-written prose, per spec. - */ - function extractProse(src: string): string { - let text = src.replace(/^---[\s\S]*?---\n/, ""); - text = text.replace(/```[\s\S]*?```/g, ""); - text = text.replace(/`[^`\n]*`/g, ""); - text = text.replace(/<[^>]+>/g, " "); - text = text.replace(/^import\s+.*$/gm, ""); - return text; - } - - it("index.mdx contains ≤3 em-dashes in hand-written prose", () => { - const prose = extractProse(indexMdx); - const emDashes = (prose.match(/—/g) ?? []).length; - expect(emDashes).toBeLessThanOrEqual(3); - }); - - it("no paragraph in index.mdx prose has more than one em-dash", () => { - const prose = extractProse(indexMdx); - const paragraphs = prose.split(/\n{2,}/); - for (const para of paragraphs) { - const count = (para.match(/—/g) ?? []).length; - expect(count).toBeLessThanOrEqual(1); +describe("shared docs design system", () => { + it("is registered before page-specific styles", () => { + const designAt = astroConfig.indexOf("./src/styles/design-system.css"); + const landingAt = astroConfig.indexOf("./src/styles/landing.css"); + expect(designAt).toBeGreaterThan(-1); + expect(landingAt).toBeGreaterThan(designAt); + }); + + it("bundles local variable fonts and exposes shared tokens", () => { + expect(designCss).toContain('font-family: "Manrope Variable"'); + expect(designCss).toContain("../assets/fonts/manrope-latin-wght-normal.woff2"); + expect(designCss).toContain('font-family: "JetBrains Mono Variable"'); + expect(designCss).toContain("../assets/fonts/jetbrains-mono-latin-wght-normal.woff2"); + expect(designCss).toContain("--ca-space-9"); + expect(designCss).toContain("--ca-bg-panel"); + expect(designCss).toContain(".ca-reference-lead"); + }); + + it("pins the reviewed font assets beside their complete OFL texts", () => { + const fonts = [ + ["manrope-latin-wght-normal.woff2", "a30ddcd349703aff7464c34bef3fffdff405ee50c113440d7c8693c02d210972"], + ["jetbrains-mono-latin-wght-normal.woff2", "18be452724bfdc236c074ca94a249a7f41a86752c7d04ab258ce9ed5651f6a7e"], + ] as const; + for (const [file, expected] of fonts) { + const bytes = readFileSync(path.join(siteRoot, "src/assets/fonts", file)); + expect(createHash("sha256").update(bytes).digest("hex")).toBe(expected); } + expect(readSrc("src/assets/fonts/LICENSE-Manrope.txt")).toContain( + "SIL OPEN FONT LICENSE Version 1.1", + ); + expect(readSrc("src/assets/fonts/LICENSE-JetBrains-Mono.txt")).toContain( + "SIL OPEN FONT LICENSE Version 1.1", + ); }); - it("GateCatchTerminal.astro prose contains ≤3 em-dashes", () => { - const prose = extractProse(terminalCmp); - const emDashes = (prose.match(/—/g) ?? []).length; - expect(emDashes).toBeLessThanOrEqual(3); - }); - - it("InstallTerminal.astro prose contains ≤3 em-dashes", () => { - const prose = extractProse(installCmp); - const emDashes = (prose.match(/—/g) ?? []).length; - expect(emDashes).toBeLessThanOrEqual(3); - }); -}); - -// --------------------------------------------------------------------------- -// Lane-flow diagram embedded in landing -// --------------------------------------------------------------------------- - -describe("lane-flow diagram embedded in landing", () => { - it("lane-flow.svg asset exists", () => { - const svgPath = path.join(siteRoot, "public/diagrams/lane-flow.svg"); - expect(existsSync(svgPath)).toBe(true); - }); - - it("index.mdx references the lane-flow diagram", () => { - expect(indexMdx).toContain("lane-flow"); - }); -}); - -// --------------------------------------------------------------------------- -// Docs source-map: handcrafted vs generated update boundaries -// --------------------------------------------------------------------------- - -describe("docs source-map section", () => { - it("landing page makes handcrafted and generated docs boundaries visible", () => { - expect(indexMdx).toContain("ca-source-map"); - expect(indexMdx).toMatch(/Handcrafted docs/); - expect(indexMdx).toMatch(/Generated reference/); - expect(indexMdx).toMatch(/Root context/); - }); - - it("source-map points generated-reference edits at the generator/source files", () => { - expect(indexMdx).toContain("site/scripts/gen.ts"); - expect(indexMdx).toContain("plugins/ca/"); - expect(indexMdx).toContain("site/src/curated/"); - }); - - it("source-map cards have dedicated visual styling", () => { - expect(landingCss).toContain(".ca-source-map"); - expect(landingCss).toContain(".ca-source-map__grid"); - expect(landingCss).toContain(".ca-source-map__card"); - }); -}); - -// --------------------------------------------------------------------------- -// MDX raw HTML: avoid nested paragraph output -// --------------------------------------------------------------------------- - -describe("landing MDX paragraph wrappers", () => { - it("does not use multiline raw-HTML

    wrappers that render as nested paragraphs", () => { - expect(indexMdx).not.toMatch(/\s*\n/); + it("keeps the canonical gate diagram asset available to inner docs", () => { + expect(existsSync(path.join(siteRoot, "public/diagrams/lane-flow.svg"))).toBe(true); }); }); diff --git a/site/test/search/search-contract.test.ts b/site/test/search/search-contract.test.ts new file mode 100644 index 00000000..fb751490 --- /dev/null +++ b/site/test/search/search-contract.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "vitest"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const siteRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const search = readFileSync(resolve(siteRoot, "src/components/Search.astro"), "utf8"); +const config = readFileSync(resolve(siteRoot, "astro.config.mjs"), "utf8"); +const theme = readFileSync(resolve(siteRoot, "src/styles/theme.css"), "utf8"); + +describe("inline Pagefind search", () => { + it("remains registered as the Starlight search override", () => { + expect(config).toMatch(/Search:\s*["']\.\/src\/components\/Search\.astro["']/); + }); + + it("implements the labelled WAI-ARIA combobox contract", () => { + expect(search).toContain('role="search"'); + expect(search).toContain('type="search"'); + expect(search).toContain('role="combobox"'); + expect(search).toContain('aria-autocomplete="list"'); + expect(search).toContain('aria-controls="ca-search-listbox"'); + expect(search).toContain('aria-activedescendant'); + expect(search).toContain('id="ca-search-listbox"'); + expect(search).toContain('role="listbox"'); + expect(search).toMatch(/setAttribute\("role",\s*"option"\)/); + expect(search).toMatch(/aria-label="[^"]+"/); + expect(search).toContain('aria-expanded="false"'); + expect(search).toContain('aria-live="polite"'); + }); + + it("supports arrow navigation, activation, and dismissal", () => { + expect(search).toContain('"ArrowDown"'); + expect(search).toContain('"ArrowUp"'); + expect(search).toContain('"Enter"'); + expect(search).toContain('"Escape"'); + expect(search).toMatch(/setAttribute\("aria-activedescendant"/); + }); + + it("loads Pagefind from the deployment base and visibly marks virtual focus", () => { + expect(search).toContain("import.meta.env.BASE_URL"); + expect(search).toMatch(/\/pagefind\/pagefind\.js/); + expect(theme).toMatch( + /\.ca-search__result-link\[aria-selected="true"\]\s*\{[^}]*outline:\s*2px solid var\(--ca-gold\)/, + ); + }); +}); From e494ac279cd6916a39e37f6cca8a16e7db7ce8e6 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 10:00:03 -0400 Subject: [PATCH 03/21] fix(core): render release contract from canonical surface Move the target-aware release documentation into core/surface and regenerate Claude, Codex, and Pi host files so hosted parity checks cannot overwrite or reject the reviewed contract. Restore the required Feature Forge preview disclosure on the product splash. CHANGELOG: Keep the target-aware release command synchronized across every generated governance host. --- core/surface/commands/release.md | 88 +++++++++++++------- core/surface/skills/release/SKILL.md | 4 +- plugins/ca-codex/routines/release/SKILL.md | 4 +- plugins/ca-codex/skills/INDEX.md | 2 +- plugins/ca-codex/skills/ca-release/SKILL.md | 88 +++++++++++++------- plugins/ca-pi/SKILLS.md | 2 +- plugins/ca-pi/generated/command-catalog.json | 2 +- plugins/ca-pi/routines/release/SKILL.md | 4 +- plugins/ca-pi/skills/ca-release/SKILL.md | 88 +++++++++++++------- plugins/ca/commands/release.md | 13 ++- site/src/content/docs/index.mdx | 2 +- 11 files changed, 187 insertions(+), 110 deletions(-) diff --git a/core/surface/commands/release.md b/core/surface/commands/release.md index e099a916..83b0d12c 100644 --- a/core/surface/commands/release.md +++ b/core/surface/commands/release.md @@ -1,52 +1,76 @@ --- -description: Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. -argument-hint: [""] | --auto | --dry-run +description: Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. +argument-hint: "[ca | ca-codex | ca-sandbox | ca-pi]" --- -# {{CMD:release}} — tagged release +# {{CMD:release}} — target-aware tagged release -The only permitted path to a version tag. A release is a deployment-readiness assertion: the codebase at this SHA satisfies the bar for shipping. `{{CMD:release}}` aggregates existing compliance — it does not duplicate it. +The only permitted path to a version tag. A release is a deployment-readiness assertion for one +specific plugin payload at one commit. `{{CMD:release}}` routes to the `release` skill; it does not +duplicate the release gates here. + +## Target + +The optional argument selects one independently-versioned plugin. A bare command defaults to `ca`. + +| Target | Tag series | Manifest | Changelog | +|---|---|---|---| +| `ca` | `vX.Y.Z` | `plugins/ca/.claude-plugin/plugin.json` | `CHANGELOG.md` | +| `ca-codex` | `ca-codex-vX.Y.Z` | `plugins/ca-codex/.codex-plugin/plugin.json` | `plugins/ca-codex/CHANGELOG.md` | +| `ca-sandbox` | `ca-sandbox-vX.Y.Z` | `plugins/ca-sandbox/.claude-plugin/plugin.json` | `plugins/ca-sandbox/CHANGELOG.md` | +| `ca-pi` | `ca-pi-vX.Y.Z` | `plugins/ca-pi/package.json` plus generated root `package.json` | `plugins/ca-pi/CHANGELOG.md` | + +An unknown target STOPs. It is never guessed from the current branch or changed files. ## Flow -Routes to the `release` skill. `{{CMD:release}}` targets the **`ca`** plugin only (ADR-0007): `LAST_TAG` -is the newest `ca` SemVer tag (`v*`, never the sibling `ca-sandbox-v*`), and the release window is the -`plugins/ca/`-scoped commit set — a `ca-sandbox` tag or commit never bumps `ca` or lands in its -changelog. +1. **Resolve the target row.** The shared release helper supplies the tag prefix. The last tag and + commit window are scoped to that tag series and payload only. +2. **Prove readiness.** The tree is clean, HEAD is not the default branch, the target has a + bump-earning commit window, its manifests agree, and every shipped bundle rebuilds cleanly. +3. **Derive version and changelog.** Conventional Commits determine the bump. Every `feat` and + `fix` requires a `CHANGELOG:` footer; `perf` footers are rolled when present, while `refactor` + earns a patch without inventing a user-facing entry. +4. **Sync release surfaces.** The selected manifest and target-specific generated or README + surfaces move together through the normal commit gate. +5. **Compose and report.** An annotated tag is created locally and the target, classification, + changelog section, and tag SHA are reported. +6. **Publish on authorization.** The tag and GitHub Release publish together only after the user + explicitly approves publication. The release is read back and the tag provenance is recorded. -1. **Pre-flight** — working tree clean, on the configured release branch, suite green, no blocking - `[CONFIRM-NN]` open, HEAD not produced via `{{CMD:override}}`. Identify `LAST_TAG` and the release window. -2. **Version bump (SemVer)** — classify every commit in `LAST_TAG..HEAD` by Conventional Commits type - and apply the highest-precedence bump (major beats minor beats patch). An explicit version that - disagrees with the classification BLOCKS — the bump is never silently up- or downgraded. -3. **Changelog** — roll up the `CHANGELOG:` footers from `feat`, `fix`, and `perf` commits into a new - section. BLOCK if any `feat`/`fix` commit lacks the footer; never auto-fill it. -4. **Tag** — compose the annotated tag. Never push it to a remote without explicit user - authorization — publication is a separate decision. +Only `ca` may hold GitHub's repo-wide Latest badge. Every sibling release explicitly declines it. -`--dry-run` runs every gate and surfaces the readiness report, then STOPs before composing the tag. +## Usage -## Arguments +```text +{{CMD:release}} +{{CMD:release}} ca +{{CMD:release}} ca-codex +{{CMD:release}} ca-sandbox +{{CMD:release}} ca-pi +``` -- **``** (e.g. `"1.2.3"`) — explicit version; Phase 2 still classifies the window and BLOCKs - on disagreement. -- **`--auto`** — derive the version from the commit log (default when no version is given). -- **`--dry-run`** — run all gates, compose nothing. Combines with `--auto` or an explicit version. - An explicit version never combines with `--auto` — they are mutually exclusive. +Claude Code uses `/ca:release `; Codex uses `$ca-release `; Pi uses +`/ca-release `. ## Routes to `release` (`{{PLUGIN_ROOT}}/skills/release/SKILL.md`). -## When NOT to use +## When not to use it -- Tagging an in-progress branch → land work first via `{{CMD:feature}}` / `{{CMD:fix}}`. -- Pushing an already-composed tag → that is a separate user-authorized step. -- A changelog only → it is a phase output, not a standalone deliverable. +- Work is still in progress: land it through the owning feature, fix, or chore lane. +- The tree is dirty or HEAD is the default branch: prepare a release branch first. +- You only want readiness information: inspect the target's release workflow and commit window + without invoking the mutating release lane. +- A published release is wrong: fix forward and publish a new version. Never move or delete the + published tag. ## Hard gate -MUST NOT compose a tag on a red suite. MUST NOT silently up- or downgrade the SemVer classification — -an explicit version that disagrees with the commit log BLOCKS. MUST NOT auto-fill a missing -`CHANGELOG:` footer. MUST NOT write to the default branch or force-push. MUST NOT push the tag without -explicit user authorization. Any BLOCK may be bypassed only via `{{CMD:override}}`. +MUST resolve exactly one supported target before release work begins. MUST scope the tag series, +commit window, manifest, changelog, payload, and built artifacts to that target. MUST derive SemVer +from the target's Conventional-Commit history and MUST NOT tag a non-bumping window. MUST NOT +auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release +without explicit authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, +retarget, or delete a published tag; correction is a new version. diff --git a/core/surface/skills/release/SKILL.md b/core/surface/skills/release/SKILL.md index f086619f..7a1e435f 100644 --- a/core/surface/skills/release/SKILL.md +++ b/core/surface/skills/release/SKILL.md @@ -63,7 +63,7 @@ Derive the bump mechanically from the commit log; do not guess it. A badge, prose-count, README-table, catalog, or root-manifest drift is a **BLOCK** — reconcile it before tagging. CI's badge-consistency guard (`.github/scripts/check_badge_consistency.py`) and the per-plugin version-bump gates are the mechanical backstop; if either is red, this step is not done. 6. If the changelog edit or the surface sync needs to land as a commit before tagging, route it through `commit-gate`. Do not reimplement the commit path here. -Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a bumping commit's `CHANGELOG:` footer is missing, or any surface drifts. +Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a `feat`/`fix` commit's required `CHANGELOG:` footer is missing, or any surface drifts. ## Phase 2 — Tag & report · gate: BLOCK @@ -121,7 +121,7 @@ The one case that is **not** a correction: a tag pushed by mistake with **no** G - MUST NOT assert `--latest` for any target except `ca`, and not even for `ca` unless the tag is the newest release across all four series. - MUST use the Phase-1 changelog section verbatim as the GitHub Release notes — never re-derive or hand-write them. - MUST NOT guess the version — derive it from the commit log. A `feat` in the window cannot ship as a `patch`. -- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past one — a missing footer on a bumping commit is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. +- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past a missing required footer — a `feat`/`fix` commit without one is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. A `perf` footer is rolled when present; `refactor` earns a patch but does not require or synthesize a user-facing entry. - MUST NOT tag a non-bumping window — `test`/`docs`/`chore`/`ci`-only sets do not release. - **MUST NOT move, retarget, delete, or re-point a published tag**, in any namespace (`v*`, `ca-codex-v*`, `ca-sandbox-v*`, `ca-pi-v*`), for any reason — no `git push --force` on a tag, no `git push --delete`, no `gh release delete`. A bad release is corrected by publishing a NEW version; see "Recovering from a bad release". There is no break-glass path (issue #386). - MUST record every newly published tag in `.github/published-tags.json`, and MUST NOT edit an existing entry to silence a red `[CHECK] | [REPO] | Published tag immutability` run — a red run means a ref moved, and the manifest is the evidence of where it belonged. diff --git a/plugins/ca-codex/routines/release/SKILL.md b/plugins/ca-codex/routines/release/SKILL.md index 5f8299a5..e6b249a0 100644 --- a/plugins/ca-codex/routines/release/SKILL.md +++ b/plugins/ca-codex/routines/release/SKILL.md @@ -63,7 +63,7 @@ Derive the bump mechanically from the commit log; do not guess it. A badge, prose-count, README-table, catalog, or root-manifest drift is a **BLOCK** — reconcile it before tagging. CI's badge-consistency guard (`.github/scripts/check_badge_consistency.py`) and the per-plugin version-bump gates are the mechanical backstop; if either is red, this step is not done. 6. If the changelog edit or the surface sync needs to land as a commit before tagging, route it through `commit-gate`. Do not reimplement the commit path here. -Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a bumping commit's `CHANGELOG:` footer is missing, or any surface drifts. +Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a `feat`/`fix` commit's required `CHANGELOG:` footer is missing, or any surface drifts. ## Phase 2 — Tag & report · gate: BLOCK @@ -121,7 +121,7 @@ The one case that is **not** a correction: a tag pushed by mistake with **no** G - MUST NOT assert `--latest` for any target except `ca`, and not even for `ca` unless the tag is the newest release across all four series. - MUST use the Phase-1 changelog section verbatim as the GitHub Release notes — never re-derive or hand-write them. - MUST NOT guess the version — derive it from the commit log. A `feat` in the window cannot ship as a `patch`. -- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past one — a missing footer on a bumping commit is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. +- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past a missing required footer — a `feat`/`fix` commit without one is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. A `perf` footer is rolled when present; `refactor` earns a patch but does not require or synthesize a user-facing entry. - MUST NOT tag a non-bumping window — `test`/`docs`/`chore`/`ci`-only sets do not release. - **MUST NOT move, retarget, delete, or re-point a published tag**, in any namespace (`v*`, `ca-codex-v*`, `ca-sandbox-v*`, `ca-pi-v*`), for any reason — no `git push --force` on a tag, no `git push --delete`, no `gh release delete`. A bad release is corrected by publishing a NEW version; see "Recovering from a bad release". There is no break-glass path (issue #386). - MUST record every newly published tag in `.github/published-tags.json`, and MUST NOT edit an existing entry to silence a red `[CHECK] | [REPO] | Published tag immutability` run — a red run means a ref moved, and the manifest is the evidence of where it belonged. diff --git a/plugins/ca-codex/skills/INDEX.md b/plugins/ca-codex/skills/INDEX.md index e7b29f3b..7da0639a 100644 --- a/plugins/ca-codex/skills/INDEX.md +++ b/plugins/ca-codex/skills/INDEX.md @@ -34,7 +34,7 @@ skill is invoked — never bulk-read this directory. | `$ca-preview` | Zero-onboarding, read-only dry-run of the reviewer fleet against the current uncommitted diff. Predicts reviewers, runs the state-free secret scan, writes nothing. | | `$ca-reconcile` | SMARTS arbitration — reconcile architectural artifacts against the scaffold and prior decisions; every variance resolved by an explicit, user-attributed choice. | | `$ca-refactor` | Restructure code with behavioral parity proven through unmodified pre-existing tests, then refactor. No behavior change. | -| `$ca-release` | Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. | +| `$ca-release` | Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. | | `$ca-review` | Review a diff with the reviewer fleet, funneled to one triaged verdict. Targets the current working diff, a path, or an inbound GitHub PR. | | `$ca-spike` | Exploratory spike on a throwaway branch — answer a named question with disposable code. Never merges; exits to a findings note or $ca-feature. | | `$ca-sprint` | Autonomous sprint — one interactive spec gate, then plan-to-PR execution with every auto-decision SMARTS-scored and logged. Hard gates remain true stops. | diff --git a/plugins/ca-codex/skills/ca-release/SKILL.md b/plugins/ca-codex/skills/ca-release/SKILL.md index 8fd5012b..88be62d2 100644 --- a/plugins/ca-codex/skills/ca-release/SKILL.md +++ b/plugins/ca-codex/skills/ca-release/SKILL.md @@ -1,53 +1,77 @@ --- name: ca-release -description: Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. -argument-hint: "[\"\"] | --auto | --dry-run" +description: Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. +argument-hint: "[ca | ca-codex | ca-sandbox | ca-pi]" --- -# $ca-release — tagged release +# $ca-release — target-aware tagged release -The only permitted path to a version tag. A release is a deployment-readiness assertion: the codebase at this SHA satisfies the bar for shipping. `$ca-release` aggregates existing compliance — it does not duplicate it. +The only permitted path to a version tag. A release is a deployment-readiness assertion for one +specific plugin payload at one commit. `$ca-release` routes to the `release` skill; it does not +duplicate the release gates here. + +## Target + +The optional argument selects one independently-versioned plugin. A bare command defaults to `ca`. + +| Target | Tag series | Manifest | Changelog | +|---|---|---|---| +| `ca` | `vX.Y.Z` | `plugins/ca/.claude-plugin/plugin.json` | `CHANGELOG.md` | +| `ca-codex` | `ca-codex-vX.Y.Z` | `plugins/ca-codex/.codex-plugin/plugin.json` | `plugins/ca-codex/CHANGELOG.md` | +| `ca-sandbox` | `ca-sandbox-vX.Y.Z` | `plugins/ca-sandbox/.claude-plugin/plugin.json` | `plugins/ca-sandbox/CHANGELOG.md` | +| `ca-pi` | `ca-pi-vX.Y.Z` | `plugins/ca-pi/package.json` plus generated root `package.json` | `plugins/ca-pi/CHANGELOG.md` | + +An unknown target STOPs. It is never guessed from the current branch or changed files. ## Flow -Routes to the `release` skill. `$ca-release` targets the **`ca`** plugin only (ADR-0007): `LAST_TAG` -is the newest `ca` SemVer tag (`v*`, never the sibling `ca-sandbox-v*`), and the release window is the -`plugins/ca/`-scoped commit set — a `ca-sandbox` tag or commit never bumps `ca` or lands in its -changelog. +1. **Resolve the target row.** The shared release helper supplies the tag prefix. The last tag and + commit window are scoped to that tag series and payload only. +2. **Prove readiness.** The tree is clean, HEAD is not the default branch, the target has a + bump-earning commit window, its manifests agree, and every shipped bundle rebuilds cleanly. +3. **Derive version and changelog.** Conventional Commits determine the bump. Every `feat` and + `fix` requires a `CHANGELOG:` footer; `perf` footers are rolled when present, while `refactor` + earns a patch without inventing a user-facing entry. +4. **Sync release surfaces.** The selected manifest and target-specific generated or README + surfaces move together through the normal commit gate. +5. **Compose and report.** An annotated tag is created locally and the target, classification, + changelog section, and tag SHA are reported. +6. **Publish on authorization.** The tag and GitHub Release publish together only after the user + explicitly approves publication. The release is read back and the tag provenance is recorded. -1. **Pre-flight** — working tree clean, on the configured release branch, suite green, no blocking - `[CONFIRM-NN]` open, HEAD not produced via `$ca-override`. Identify `LAST_TAG` and the release window. -2. **Version bump (SemVer)** — classify every commit in `LAST_TAG..HEAD` by Conventional Commits type - and apply the highest-precedence bump (major beats minor beats patch). An explicit version that - disagrees with the classification BLOCKS — the bump is never silently up- or downgraded. -3. **Changelog** — roll up the `CHANGELOG:` footers from `feat`, `fix`, and `perf` commits into a new - section. BLOCK if any `feat`/`fix` commit lacks the footer; never auto-fill it. -4. **Tag** — compose the annotated tag. Never push it to a remote without explicit user - authorization — publication is a separate decision. +Only `ca` may hold GitHub's repo-wide Latest badge. Every sibling release explicitly declines it. -`--dry-run` runs every gate and surfaces the readiness report, then STOPs before composing the tag. +## Usage -## Arguments +```text +$ca-release +$ca-release ca +$ca-release ca-codex +$ca-release ca-sandbox +$ca-release ca-pi +``` -- **``** (e.g. `"1.2.3"`) — explicit version; Phase 2 still classifies the window and BLOCKs - on disagreement. -- **`--auto`** — derive the version from the commit log (default when no version is given). -- **`--dry-run`** — run all gates, compose nothing. Combines with `--auto` or an explicit version. - An explicit version never combines with `--auto` — they are mutually exclusive. +Claude Code uses `/ca:release `; Codex uses `$ca-release `; Pi uses +`/ca-release `. ## Routes to `release` (`${CLAUDE_PLUGIN_ROOT}/routines/release/SKILL.md`). -## When NOT to use +## When not to use it -- Tagging an in-progress branch → land work first via `$ca-feature` / `$ca-fix`. -- Pushing an already-composed tag → that is a separate user-authorized step. -- A changelog only → it is a phase output, not a standalone deliverable. +- Work is still in progress: land it through the owning feature, fix, or chore lane. +- The tree is dirty or HEAD is the default branch: prepare a release branch first. +- You only want readiness information: inspect the target's release workflow and commit window + without invoking the mutating release lane. +- A published release is wrong: fix forward and publish a new version. Never move or delete the + published tag. ## Hard gate -MUST NOT compose a tag on a red suite. MUST NOT silently up- or downgrade the SemVer classification — -an explicit version that disagrees with the commit log BLOCKS. MUST NOT auto-fill a missing -`CHANGELOG:` footer. MUST NOT write to the default branch or force-push. MUST NOT push the tag without -explicit user authorization. Any BLOCK may be bypassed only via `$ca-override`. +MUST resolve exactly one supported target before release work begins. MUST scope the tag series, +commit window, manifest, changelog, payload, and built artifacts to that target. MUST derive SemVer +from the target's Conventional-Commit history and MUST NOT tag a non-bumping window. MUST NOT +auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release +without explicit authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, +retarget, or delete a published tag; correction is a new version. diff --git a/plugins/ca-pi/SKILLS.md b/plugins/ca-pi/SKILLS.md index 503574f5..1eaacc4c 100644 --- a/plugins/ca-pi/SKILLS.md +++ b/plugins/ca-pi/SKILLS.md @@ -35,7 +35,7 @@ skill is invoked — never bulk-read this directory. | `/ca-prune` | Trim transcript clutter to extend session lifetime — analyze, prune a copy, or toggle the after-each-turn service. Dry-run by default; gains land at resume/compaction, not the current turn. | | `/ca-reconcile` | SMARTS arbitration — reconcile architectural artifacts against the scaffold and prior decisions; every variance resolved by an explicit, user-attributed choice. | | `/ca-refactor` | Restructure code with behavioral parity proven through unmodified pre-existing tests, then refactor. No behavior change. | -| `/ca-release` | Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. | +| `/ca-release` | Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. | | `/ca-review` | Review a diff with the reviewer fleet, funneled to one triaged verdict. Targets the current working diff, a path, or an inbound GitHub PR. | | `/ca-spike` | Exploratory spike on a throwaway branch — answer a named question with disposable code. Never merges; exits to a findings note or /ca-feature. | | `/ca-sprint` | Autonomous sprint — one interactive spec gate, then plan-to-PR execution with every auto-decision SMARTS-scored and logged. Hard gates remain true stops. | diff --git a/plugins/ca-pi/generated/command-catalog.json b/plugins/ca-pi/generated/command-catalog.json index 006eaf5d..b8ce2c07 100644 --- a/plugins/ca-pi/generated/command-catalog.json +++ b/plugins/ca-pi/generated/command-catalog.json @@ -146,7 +146,7 @@ }, { "name": "release", - "description": "Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag.", + "description": "Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization.", "skillPath": "skills/ca-release/SKILL.md" }, { diff --git a/plugins/ca-pi/routines/release/SKILL.md b/plugins/ca-pi/routines/release/SKILL.md index 061e628e..1f5e07ed 100644 --- a/plugins/ca-pi/routines/release/SKILL.md +++ b/plugins/ca-pi/routines/release/SKILL.md @@ -63,7 +63,7 @@ Derive the bump mechanically from the commit log; do not guess it. A badge, prose-count, README-table, catalog, or root-manifest drift is a **BLOCK** — reconcile it before tagging. CI's badge-consistency guard (`.github/scripts/check_badge_consistency.py`) and the per-plugin version-bump gates are the mechanical backstop; if either is red, this step is not done. 6. If the changelog edit or the surface sync needs to land as a commit before tagging, route it through `commit-gate`. Do not reimplement the commit path here. -Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a bumping commit's `CHANGELOG:` footer is missing, or any surface drifts. +Gate: version confirmed, strictly monotonic within `$TARGET`'s series, matching the commit log, and equal to `$MANIFEST` (both, for ca-pi); `$CHANGELOG` updated; `$TARGET`'s surfaces reconciled to the repo. BLOCK if the classification disagrees with the log, the window is non-bumping, a `feat`/`fix` commit's required `CHANGELOG:` footer is missing, or any surface drifts. ## Phase 2 — Tag & report · gate: BLOCK @@ -121,7 +121,7 @@ The one case that is **not** a correction: a tag pushed by mistake with **no** G - MUST NOT assert `--latest` for any target except `ca`, and not even for `ca` unless the tag is the newest release across all four series. - MUST use the Phase-1 changelog section verbatim as the GitHub Release notes — never re-derive or hand-write them. - MUST NOT guess the version — derive it from the commit log. A `feat` in the window cannot ship as a `patch`. -- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past one — a missing footer on a bumping commit is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. +- MUST NOT auto-fill a missing `CHANGELOG:` footer, and MUST NOT tag past a missing required footer — a `feat`/`fix` commit without one is a Phase-1 BLOCK, surfaced as `[NEEDS-TRIAGE]` and stopped. A `perf` footer is rolled when present; `refactor` earns a patch but does not require or synthesize a user-facing entry. - MUST NOT tag a non-bumping window — `test`/`docs`/`chore`/`ci`-only sets do not release. - **MUST NOT move, retarget, delete, or re-point a published tag**, in any namespace (`v*`, `ca-codex-v*`, `ca-sandbox-v*`, `ca-pi-v*`), for any reason — no `git push --force` on a tag, no `git push --delete`, no `gh release delete`. A bad release is corrected by publishing a NEW version; see "Recovering from a bad release". There is no break-glass path (issue #386). - MUST record every newly published tag in `.github/published-tags.json`, and MUST NOT edit an existing entry to silence a red `[CHECK] | [REPO] | Published tag immutability` run — a red run means a ref moved, and the manifest is the evidence of where it belonged. diff --git a/plugins/ca-pi/skills/ca-release/SKILL.md b/plugins/ca-pi/skills/ca-release/SKILL.md index 70c7a197..3055e562 100644 --- a/plugins/ca-pi/skills/ca-release/SKILL.md +++ b/plugins/ca-pi/skills/ca-release/SKILL.md @@ -1,53 +1,77 @@ --- name: ca-release -description: Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. The only path to a version tag. -argument-hint: "[\"\"] | --auto | --dry-run" +description: Cut a release for one of the four independently-versioned plugins. Selects the target's tag series, payload, manifests, changelog, and built artifacts; derives SemVer from that payload's commit window; publishes only on explicit authorization. +argument-hint: "[ca | ca-codex | ca-sandbox | ca-pi]" --- -# /ca-release — tagged release +# /ca-release — target-aware tagged release -The only permitted path to a version tag. A release is a deployment-readiness assertion: the codebase at this SHA satisfies the bar for shipping. `/ca-release` aggregates existing compliance — it does not duplicate it. +The only permitted path to a version tag. A release is a deployment-readiness assertion for one +specific plugin payload at one commit. `/ca-release` routes to the `release` skill; it does not +duplicate the release gates here. + +## Target + +The optional argument selects one independently-versioned plugin. A bare command defaults to `ca`. + +| Target | Tag series | Manifest | Changelog | +|---|---|---|---| +| `ca` | `vX.Y.Z` | `plugins/ca/.claude-plugin/plugin.json` | `CHANGELOG.md` | +| `ca-codex` | `ca-codex-vX.Y.Z` | `plugins/ca-codex/.codex-plugin/plugin.json` | `plugins/ca-codex/CHANGELOG.md` | +| `ca-sandbox` | `ca-sandbox-vX.Y.Z` | `plugins/ca-sandbox/.claude-plugin/plugin.json` | `plugins/ca-sandbox/CHANGELOG.md` | +| `ca-pi` | `ca-pi-vX.Y.Z` | `plugins/ca-pi/package.json` plus generated root `package.json` | `plugins/ca-pi/CHANGELOG.md` | + +An unknown target STOPs. It is never guessed from the current branch or changed files. ## Flow -Routes to the `release` skill. `/ca-release` targets the **`ca`** plugin only (ADR-0007): `LAST_TAG` -is the newest `ca` SemVer tag (`v*`, never the sibling `ca-sandbox-v*`), and the release window is the -`plugins/ca/`-scoped commit set — a `ca-sandbox` tag or commit never bumps `ca` or lands in its -changelog. +1. **Resolve the target row.** The shared release helper supplies the tag prefix. The last tag and + commit window are scoped to that tag series and payload only. +2. **Prove readiness.** The tree is clean, HEAD is not the default branch, the target has a + bump-earning commit window, its manifests agree, and every shipped bundle rebuilds cleanly. +3. **Derive version and changelog.** Conventional Commits determine the bump. Every `feat` and + `fix` requires a `CHANGELOG:` footer; `perf` footers are rolled when present, while `refactor` + earns a patch without inventing a user-facing entry. +4. **Sync release surfaces.** The selected manifest and target-specific generated or README + surfaces move together through the normal commit gate. +5. **Compose and report.** An annotated tag is created locally and the target, classification, + changelog section, and tag SHA are reported. +6. **Publish on authorization.** The tag and GitHub Release publish together only after the user + explicitly approves publication. The release is read back and the tag provenance is recorded. -1. **Pre-flight** — working tree clean, on the configured release branch, suite green, no blocking - `[CONFIRM-NN]` open, HEAD not produced via `/ca-override`. Identify `LAST_TAG` and the release window. -2. **Version bump (SemVer)** — classify every commit in `LAST_TAG..HEAD` by Conventional Commits type - and apply the highest-precedence bump (major beats minor beats patch). An explicit version that - disagrees with the classification BLOCKS — the bump is never silently up- or downgraded. -3. **Changelog** — roll up the `CHANGELOG:` footers from `feat`, `fix`, and `perf` commits into a new - section. BLOCK if any `feat`/`fix` commit lacks the footer; never auto-fill it. -4. **Tag** — compose the annotated tag. Never push it to a remote without explicit user - authorization — publication is a separate decision. +Only `ca` may hold GitHub's repo-wide Latest badge. Every sibling release explicitly declines it. -`--dry-run` runs every gate and surfaces the readiness report, then STOPs before composing the tag. +## Usage -## Arguments +```text +/ca-release +/ca-release ca +/ca-release ca-codex +/ca-release ca-sandbox +/ca-release ca-pi +``` -- **``** (e.g. `"1.2.3"`) — explicit version; Phase 2 still classifies the window and BLOCKs - on disagreement. -- **`--auto`** — derive the version from the commit log (default when no version is given). -- **`--dry-run`** — run all gates, compose nothing. Combines with `--auto` or an explicit version. - An explicit version never combines with `--auto` — they are mutually exclusive. +Claude Code uses `/ca:release `; Codex uses `$ca-release `; Pi uses +`/ca-release `. ## Routes to `release` (`/routines/release/SKILL.md`). -## When NOT to use +## When not to use it -- Tagging an in-progress branch → land work first via `/ca-feature` / `/ca-fix`. -- Pushing an already-composed tag → that is a separate user-authorized step. -- A changelog only → it is a phase output, not a standalone deliverable. +- Work is still in progress: land it through the owning feature, fix, or chore lane. +- The tree is dirty or HEAD is the default branch: prepare a release branch first. +- You only want readiness information: inspect the target's release workflow and commit window + without invoking the mutating release lane. +- A published release is wrong: fix forward and publish a new version. Never move or delete the + published tag. ## Hard gate -MUST NOT compose a tag on a red suite. MUST NOT silently up- or downgrade the SemVer classification — -an explicit version that disagrees with the commit log BLOCKS. MUST NOT auto-fill a missing -`CHANGELOG:` footer. MUST NOT write to the default branch or force-push. MUST NOT push the tag without -explicit user authorization. Any BLOCK may be bypassed only via `/ca-override`. +MUST resolve exactly one supported target before release work begins. MUST scope the tag series, +commit window, manifest, changelog, payload, and built artifacts to that target. MUST derive SemVer +from the target's Conventional-Commit history and MUST NOT tag a non-bumping window. MUST NOT +auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release +without explicit authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, +retarget, or delete a published tag; correction is a new version. diff --git a/plugins/ca/commands/release.md b/plugins/ca/commands/release.md index f2fdac0e..6eb80596 100644 --- a/plugins/ca/commands/release.md +++ b/plugins/ca/commands/release.md @@ -50,7 +50,12 @@ Only `ca` may hold GitHub's repo-wide Latest badge. Every sibling release explic /ca:release ca-pi ``` -Codex uses `$ca-release `; Pi uses `/ca-release `. +Claude Code uses `/ca:release `; Codex uses `$ca-release `; Pi uses +`/ca-release `. + +## Routes to + +`release` (`${CLAUDE_PLUGIN_ROOT}/skills/release/SKILL.md`). ## When not to use it @@ -66,6 +71,6 @@ Codex uses `$ca-release `; Pi uses `/ca-release `. MUST resolve exactly one supported target before release work begins. MUST scope the tag series, commit window, manifest, changelog, payload, and built artifacts to that target. MUST derive SemVer from the target's Conventional-Commit history and MUST NOT tag a non-bumping window. MUST NOT -auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release without explicit -authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, retarget, or -delete a published tag; correction is a new version. +auto-fill a missing required `feat`/`fix` changelog footer. MUST NOT publish a tag or GitHub Release +without explicit authorization. MUST NOT give the Latest badge to a sibling series. MUST NOT move, +retarget, or delete a published tag; correction is a new version. diff --git a/site/src/content/docs/index.mdx b/site/src/content/docs/index.mdx index 84dbad92..914bfa04 100644 --- a/site/src/content/docs/index.mdx +++ b/site/src/content/docs/index.mdx @@ -205,7 +205,7 @@ import heroArt from '../../assets/hero-gates.webp'; preview

    /ca-feature -

    Git-installed adapter with project trust, a rich footer, and supervised child dispatch.

    +

    Git-installed Feature Forge preview with project trust, a rich footer, and supervised child dispatch.

    Install the Pi preview
    From ac962350ac606ce39b2643fbc238e1c64c0a564d Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 10:01:15 -0400 Subject: [PATCH 04/21] docs(site): satisfy authored voice contract Remove the lone prose-separator dash from the learning capstone so the site voice check passes on the hosted Linux generator job. --- site/src/content/docs/learn/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/content/docs/learn/index.mdx b/site/src/content/docs/learn/index.mdx index 4c3cd0d0..6a4038db 100644 --- a/site/src/content/docs/learn/index.mdx +++ b/site/src/content/docs/learn/index.mdx @@ -129,7 +129,7 @@ Use a disposable repository. Do not perform this exercise in active production w
  • Trace one sprint decision from the log to its SMARTS evidence and confidence flag.
  • Explain why a green local test is not proof that a pull request is safe to merge.
  • -

    You are a codeArbiter power user when you can complete all eight from the repository and docs—without reconstructing the answer from a prior chat.

    +

    You are a codeArbiter power user when you can complete all eight from the repository and docs without reconstructing the answer from a prior chat.

    ## Keep this path honest From d8821933fbf48fdf5787fde488a733a02413cd49 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 10:04:01 -0400 Subject: [PATCH 05/21] fix(site): make source-link tests revision-independent Allow source renderers to accept an explicit source ref in tests while preserving the production default from GITHUB_SHA. This keeps exact-revision links and makes detached CI runs deterministic. --- site/scripts/generator/render-hooks-reference.ts | 2 +- site/scripts/generator/render-source-embed.ts | 2 +- site/test/generator/render-hooks-reference.test.ts | 4 ++-- site/test/generator/render-source-embed.test.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/scripts/generator/render-hooks-reference.ts b/site/scripts/generator/render-hooks-reference.ts index 4c58a2c1..e1d34c84 100644 --- a/site/scripts/generator/render-hooks-reference.ts +++ b/site/scripts/generator/render-hooks-reference.ts @@ -105,9 +105,9 @@ export function renderHooksReference( callSites: HookCallSite[], eventMap: Map, pluginVersion: string, + sourceRef: string = repositorySourceRef(), ): string { const tag = `v${pluginVersion}`; - const sourceRef = repositorySourceRef(); const byTag = new Map(); for (const site of callSites) { const list = byTag.get(site.tag) ?? []; diff --git a/site/scripts/generator/render-source-embed.ts b/site/scripts/generator/render-source-embed.ts index 85c81179..22c3d99c 100644 --- a/site/scripts/generator/render-source-embed.ts +++ b/site/scripts/generator/render-source-embed.ts @@ -38,10 +38,10 @@ export function renderSourceEmbed( sourceRaw: string, sourceRelPath: string, pluginVersion: string, + sourceRef: string = repositorySourceRef(), ): string { const fence = chooseFence(sourceRaw); const tag = `v${pluginVersion}`; - const sourceRef = repositorySourceRef(); const repoUrl = `https://github.com/arbiterForge/codeArbiter/blob/${sourceRef}/${sourceRelPath}`; return `
    diff --git a/site/test/generator/render-hooks-reference.test.ts b/site/test/generator/render-hooks-reference.test.ts index 4a075916..fe56942f 100644 --- a/site/test/generator/render-hooks-reference.test.ts +++ b/site/test/generator/render-hooks-reference.test.ts @@ -62,7 +62,7 @@ describe("buildEventMap / eventsFor", () => { describe("renderHooksReference", () => { const eventMap = buildEventMap(hooksJson); - const md = renderHooksReference(callSites, eventMap, "2.8.11"); + const md = renderHooksReference(callSites, eventMap, "2.8.11", "main"); it("renders frontmatter with title Hook Gates", () => { expect(md).toContain("title: Hook Gates"); @@ -103,7 +103,7 @@ describe("renderHooksReference", () => { line: 5, message: "{rel}: found an issue on line {line}.", }; - const out = renderHooksReference([site], eventMap, "2.8.11"); + const out = renderHooksReference([site], eventMap, "2.8.11", "main"); expect(out).toContain("`{rel}`"); expect(out).toContain("`{line}`"); }); diff --git a/site/test/generator/render-source-embed.test.ts b/site/test/generator/render-source-embed.test.ts index b6a1e033..d468d62f 100644 --- a/site/test/generator/render-source-embed.test.ts +++ b/site/test/generator/render-source-embed.test.ts @@ -26,8 +26,8 @@ describe("renderSourceEmbed", () => { expect(md).toContain("`````md"); }); - it("pins the View in repo link to the given version tag", () => { - const md = renderSourceEmbed("x", "plugins/ca/commands/sprint.md", "2.8.11"); + it("pins the View in repo link to an explicit source ref", () => { + const md = renderSourceEmbed("x", "plugins/ca/commands/sprint.md", "2.8.11", "main"); expect(md).toContain( 'View in repo', ); From 0b3e8fa906d37264245cc060c006107a1deedfdb Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 10:07:00 -0400 Subject: [PATCH 06/21] chore(release): advance regenerated host payloads Advance ca-codex to 0.3.5 and ca-pi to 0.1.41, regenerate Pi root package metadata, and record the canonical release-surface synchronization in both host changelogs. --- package.json | 2 +- plugins/ca-codex/.codex-plugin/plugin.json | 2 +- plugins/ca-codex/CHANGELOG.md | 7 +++++++ plugins/ca-pi/CHANGELOG.md | 7 +++++++ plugins/ca-pi/package.json | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index cbc91e51..794364a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ca-pi", - "version": "0.1.40", + "version": "0.1.41", "private": true, "license": "AGPL-3.0-only", "engines": { diff --git a/plugins/ca-codex/.codex-plugin/plugin.json b/plugins/ca-codex/.codex-plugin/plugin.json index e4ff6553..7c601631 100644 --- a/plugins/ca-codex/.codex-plugin/plugin.json +++ b/plugins/ca-codex/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ca-codex", "description": "Governance kernel for OpenAI Codex CLI: the full codeArbiter surface — 37 ca-prefixed governance skills (spec-driven /feature pipeline, nine-gate commit gate, ADRs, audits) plus enforcement hooks (persona injection, blocking pre-exec and pre-write gates, append-only audit trail) — sharing one .codearbiter/ store with the Claude Code sibling plugin. Standalone: opt a repo in with ca-init; enforcement stays dormant until .codearbiter/CONTEXT.md carries 'arbiter: enabled'. Requires Python 3 and Codex >= 0.143.0. CI continuously verifies, through a real Codex host at 0.143.0 and 0.145.0, that the plugin installs, reads back enabled, and ships every hook script it declares; an advisory lane tracks npm latest for upstream drift. Hook FIRING - live persona injection and live blocks inside a turn - is verified by hand per release against docs/codex-parity-testing.md, because a turn needs a model and a provider credential cannot gate fork pull requests.", - "version": "0.3.4", + "version": "0.3.5", "author": { "name": "arbiterForge" }, diff --git a/plugins/ca-codex/CHANGELOG.md b/plugins/ca-codex/CHANGELOG.md index 0a6ffc15..9c01a636 100644 --- a/plugins/ca-codex/CHANGELOG.md +++ b/plugins/ca-codex/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to the **ca-codex** plugin are recorded here. Format follows ## [Unreleased] +## [0.3.5] — 2026-07-30 + +### Fixed + +- Render the target-aware release command and footer rules from the canonical shared surface, so + the Codex package stays synchronized with the Claude Code and Pi hosts. + ## [Unreleased] ## [Unreleased] diff --git a/plugins/ca-pi/CHANGELOG.md b/plugins/ca-pi/CHANGELOG.md index e198b405..a36f4712 100644 --- a/plugins/ca-pi/CHANGELOG.md +++ b/plugins/ca-pi/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to `ca-pi` are documented in this file. ## [Unreleased] +## [0.1.41] - 2026-07-30 + +### Fixed + +- Render the target-aware release command and footer rules from the canonical shared surface, so + the Pi package stays synchronized with the Claude Code and Codex hosts. + ## [0.1.40] - 2026-07-30 ### Changed diff --git a/plugins/ca-pi/package.json b/plugins/ca-pi/package.json index 3b6e11c0..e3eeec25 100644 --- a/plugins/ca-pi/package.json +++ b/plugins/ca-pi/package.json @@ -1,6 +1,6 @@ { "name": "ca-pi", - "version": "0.1.40", + "version": "0.1.41", "private": true, "license": "AGPL-3.0-only", "type": "module", From 7a0a08980c6d0b4c8b397e2d39a75abc720f1134 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 13:57:56 -0400 Subject: [PATCH 07/21] feat(docs): build a first-class learning site Reframe the documentation as a novice-to-power-user product, upgrade the shared visual system and generated references, and align the README with the same source-backed learning path. CHANGELOG: Rebuilds the documentation and README into a branded, source-backed learning path from first install to power-user operation. --- README.md | 753 +++++++++--------- docs/architecture.md | 2 +- docs/banner.svg | 125 --- docs/feature-forge.svg | 76 -- docs/feature-forge.webp | Bin 0 -> 68474 bytes docs/parity.md | 8 +- docs/readme-hero.webp | Bin 0 -> 55404 bytes .../docs-site/2026-07-30-page-audit.md | 230 ++++++ site/astro.config.mjs | 3 +- site/public/art/feature-forge.webp | Bin 0 -> 68474 bytes site/scripts/generator/generate.ts | 14 +- site/scripts/generator/render-agent-page.ts | 4 +- site/scripts/generator/render-changelog.ts | 10 +- site/scripts/generator/render-command-page.ts | 4 +- .../generator/render-reference-lead.ts | 30 +- site/scripts/generator/render-skill-page.ts | 4 +- site/scripts/rehype-table-shell.ts | 51 ++ site/src/assets/logo.svg | 50 +- site/src/components/ArbiterIcon.astro | 2 +- site/src/components/ConceptNavigator.astro | 227 ++++++ site/src/components/PageContext.astro | 7 +- site/src/components/PageTitle.astro | 5 +- site/src/components/TrustRow.astro | 2 +- .../docs/{concepts.md => concepts.mdx} | 15 +- site/src/content/docs/concepts/gated-lanes.md | 7 +- .../docs/concepts/persona-and-context.md | 3 +- site/src/content/docs/enforcement.md | 7 +- .../content/docs/feature-forge/overview.md | 5 + .../getting-started/claude-code-and-codex.md | 2 +- .../docs/getting-started/compatibility.md | 17 +- .../content/docs/getting-started/install.md | 17 +- site/src/content/docs/getting-started/pi.md | 15 +- site/src/content/docs/glossary.md | 57 +- site/src/content/docs/guides/ca-sandbox.md | 4 + site/src/content/docs/guides/opt-in-a-repo.md | 7 +- .../content/docs/guides/overriding-a-gate.md | 9 +- site/src/content/docs/guides/uninstalling.md | 196 +++-- site/src/content/docs/hooks.md | 63 +- site/src/content/docs/index.mdx | 6 +- site/src/content/docs/learn/index.mdx | 6 +- site/src/content/docs/overview.md | 8 +- site/src/curated/commands/checkpoint.md | 5 +- site/src/curated/commands/pr.md | 5 +- site/src/curated/commands/review.md | 5 +- site/src/curated/commands/tribunal.md | 5 +- site/src/styles/callouts.css | 9 +- site/src/styles/design-system.css | 101 ++- site/src/styles/landing.css | 30 +- site/src/styles/theme.css | 79 +- site/test/astro-config.test.ts | 12 +- .../documentation-presentation.test.ts | 234 ++++++ site/test/content/readme-professional.test.ts | 50 ++ .../generator/feature-forge-content.test.ts | 2 +- site/test/generator/generate.test.ts | 9 + site/test/generator/render-agent-page.test.ts | 51 ++ site/test/generator/render-changelog.test.ts | 8 +- site/test/landing/landing-page.test.ts | 7 + site/test/rehype-table-shell.test.ts | 55 ++ 58 files changed, 1883 insertions(+), 835 deletions(-) delete mode 100644 docs/banner.svg delete mode 100644 docs/feature-forge.svg create mode 100644 docs/feature-forge.webp create mode 100644 docs/readme-hero.webp create mode 100644 docs/reports/docs-site/2026-07-30-page-audit.md create mode 100644 site/public/art/feature-forge.webp create mode 100644 site/scripts/rehype-table-shell.ts create mode 100644 site/src/components/ConceptNavigator.astro rename site/src/content/docs/{concepts.md => concepts.mdx} (69%) create mode 100644 site/test/content/documentation-presentation.test.ts create mode 100644 site/test/content/readme-professional.test.ts create mode 100644 site/test/rehype-table-shell.test.ts diff --git a/README.md b/README.md index dd7f4286..13e62c4b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@
    -codeArbiter: discipline, mechanically enforced +codeArbiter. Hard gates for agentic coding. **Shared enforcement and project-context parity across Claude Code, Codex CLI, and Pi.** -Every intent routes through a gated skill or reviewer agent. Nothing commits until the gates are green. Decisions go through SMARTS. The audit trail is append-only. +One repository-owned governance layer for tests, reviews, security checks, decisions, and durable +project context. You decide. codeArbiter enforces. Claude Code plugin Codex plugin @@ -15,123 +16,96 @@ Every intent routes through a gated skill or reviewer agent. Nothing commits unt agents license AGPL v3 -### [Read the documentation →](https://arbiterforge.github.io/codeArbiter/) +[Start learning](https://arbiterforge.github.io/codeArbiter/learn/) +  ·   +[Install](https://arbiterforge.github.io/codeArbiter/getting-started/install/) +  ·   +[Browse the reference](https://arbiterforge.github.io/codeArbiter/reference/) -Install it globally; it stays dormant until you opt a repo in. +Install it globally. It stays dormant until a repository explicitly opts in.
    ---- +> [!IMPORTANT] +> **License notice.** Since v2.6.0, codeArbiter is licensed under the +> [GNU AGPLv3](LICENSE), a change from its earlier MIT license. Copyright (C) 2026 SUaDtL, who +> reserves the right to dual-license under separate proprietary terms; commercial licenses are not +> offered at this time. See [License and contributions](#license-and-contributions). -> **License notice.** As of v2.6.0, codeArbiter is licensed under the [GNU AGPLv3](LICENSE), a change from its earlier MIT license. Copyright (C) 2026 SUaDtL, who reserves the right to dual-license under separate proprietary terms; commercial licenses are not offered at this time. See [Dual-Licensing & Contributions](#dual-licensing--contributions). +## Agentic coding, with a record -## What it is +codeArbiter is for teams and power users who let coding agents do consequential work and need more +than “the model said it passed.” Every request enters a named lane. The lane defines the evidence it +must produce, the reviewers it must dispatch, and the conditions that stop it. -codeArbiter is a governance layer for Claude Code, Codex CLI, and Pi. It lives in a repository of -four sibling plugins: the three governance hosts (`ca`, `ca-codex`, and `ca-pi`) plus the -`ca-sandbox` infrastructure plugin. The governance adapters are generated from one shared surface -and enforce one checked-in `.codearbiter/` project store. Instead of letting the model freelance, -you drive through host-native commands. Each one routes to the process that owns the work and clears -its gates before anything ships. +This repository contains four sibling plugins: -The Codex path was live-verified on **Codex CLI 0.144.1** with trusted SessionStart injection and a -real `[H-03]` PreToolUse block. See the -[Claude Code + Codex evidence](https://arbiterforge.github.io/codeArbiter/getting-started/claude-code-and-codex/) -for the dated proof, CI coverage, and intentional host differences. - -**Who it's for:** teams and power users who let agents write real code and need to prove what happened. - -It will not: - -- write feature code before a failing test exists, -- commit on a red suite or without the commit gate, -- resolve an open question by guessing, or -- silently reconcile a contradiction between your docs and your code. - -The gates are terse and non-negotiable. The thinking is not: it brainstorms a spec, works through a bug, and weighs a decision with you conversationally. When it enforces, it states the rule and holds the line. - -The gates scale to the change: a one-line docs fix takes the small lane or `/ca:chore`, not the full spec-to-PR march. The weight exists because the failure mode of an eager AI assistant is *plausible-but-wrong work that ships*, and the gates make that hard. - -## OpenAI Build Week submission - -codeArbiter predates OpenAI Build Week. The submission is the work added from July 13 through July -21, 2026: full Pi host support plus a corrective campaign over the existing governance core. The -new work is consolidated in [PR #313](https://github.com/arbiterForge/codeArbiter/pull/313). Its -verified implementation head is `ec64809c1b6c32f69e3d9787ab205f7bbd563d50`. - -The Pi adapter adds a rich global footer, trust-aware repository activation, an extensible -`allow | ask | deny` execution policy, read-only plan mode, session-only background jobs, bounded -child-agent dispatch, native compaction, diagnostics, and shared-state continuity. Background jobs -never survive Pi shutdown. Parent-only UI does not leak into JSON, RPC, print, or child modes. - -### How Codex and GPT-5.6 contributed - -Codex was the primary development and orchestration environment for the submission. GPT-5.6 was -used to design and implement the Pi adapter, compare its behavior with the established host, -investigate failures found during live testing, review older implementation paths, and consolidate -the resulting corrections. The work includes decisions and fixes across permissions, Windows -process-tree cleanup, provenance handling, audit integrity, generated-source drift, CI contracts, -and documentation. +- `ca` for Claude Code, `ca-codex` for Codex CLI, and `ca-pi` for Pi are the three governance hosts. +- `ca-sandbox` is the isolated local-Codespace infrastructure plugin. -The final candidate passed the six supported Pi platform cells for Pi 0.80.5 and 0.80.10 on Windows, -macOS, and Linux. It also passed Pi security analysis, CodeQL, the shared-core and generated-surface -contracts, and the repository's 46-gate final verifier. The sanitized evidence is available in -[`docs/reports/pi-support/promotion.md`](docs/reports/pi-support/promotion.md) and -[`docs/reports/pi-support/promotion.json`](docs/reports/pi-support/promotion.json). +The three governance hosts are generated from one shared surface and use one checked-in +`.codearbiter/` project store. A repository can move between hosts without losing its specs, plans, +ADRs, tasks, decisions, or audit history. -### Judge quick start +codeArbiter will not: -The submission supports Windows, macOS, and Linux. It requires Pi 0.80.10, Node.js 22.19 or newer, -Python 3, and Git. No account or sample data is required. +- write feature code before the required failing test exists; +- commit on a red suite or outside the commit gate; +- resolve a `[CONFIRM-NN]` question by guessing; +- silently reconcile a conflict between the rules, the docs, and the code; or +- push directly to the default branch or merge without the user. -Install the immutable candidate rather than a moving branch: +The gates scale to the work. A prose edit takes the small docs lane. A security-sensitive feature +takes the full spec, test-first, review, and PR path. -```text -pi install git:github.com/arbiterForge/codeArbiter@ec64809c1b6c32f69e3d9787ab205f7bbd563d50 -pi list -pi config -``` - -Open a disposable Git repository, inspect it, grant Pi project trust, and start a fresh Pi session. -Then run: - -```text -/ca-init -/ca-doctor -``` - -The rich footer appears globally. Repository-specific governance appears only in an enabled, -affirmatively trusted repository. The complete deterministic and trusted-live procedure is in the -[Pi parity-testing runbook](docs/pi-parity-testing.md). Judges can inspect the exact implementation, -review discussion, and hosted check results in [PR #313](https://github.com/arbiterForge/codeArbiter/pull/313) -without rebuilding the project from source. +The Codex path was live-verified on **Codex CLI 0.144.1** with trusted SessionStart injection and a +real `[H-03]` PreToolUse block. The dated +[Claude Code + Codex evidence](https://arbiterforge.github.io/codeArbiter/getting-started/claude-code-and-codex/) +separates continuously verified packaging from the live checks that require a model-backed turn. ## See it catch something -The first time codeArbiter blocks you, it can feel like friction, until you see what it caught. This exchange is a real one from this project's own history: a bug fix forced through test-first, then a PR held at review until an untested seam got covered. +The landing page includes a +[faithful replay of a shipped hook invocation](https://arbiterforge.github.io/codeArbiter/#direct-hook-proof). +It invokes the real `pre-bash.py` guard, records the source digest, proves the blocked command never +ran, and preserves the exact stderr and audit effect. It does not pretend that a direct hook replay +proves host discovery or trust. + +The operating loop looks like this: ```text you /ca:fix the statusline keeps running the old version after an update -arbiter Routing to tdd (bug variant): a regression test before any fix. - → writes a failing test, confirms it's red for the right reason - → minimal fix → suite green → coverage + lint gates clear +arbiter route to the regression-test-first fix lane + failing test -> minimal fix -> focused proof -> full verification you /ca:commit -arbiter commit-gate: ✓ permission ✓ branch ✓ tests ✓ secrets - ✓ behavioral proof ✓ clean diff → committed. +arbiter permission -> branch -> tests -> behavioral proof -> diff review + selective stage -> commit you /ca:pr -arbiter reviewer fleet over the diff: coverage-auditor flags an untested seam. - BLOCK. Here's the gap. → (you resolve, re-run) → PR opened. +arbiter reviewer fleet challenges the exact diff + BLOCK findings are resolved before the draft PR can advance ``` -Every step is a gate you watch clear. You stay in the driver's seat; the gates keep the work honest. +The user owns the decision. The repository keeps the evidence. + +## Get running + +Choose the adapter for the coding host you already use. The +[host chooser](https://arbiterforge.github.io/codeArbiter/getting-started/choose-your-host/) covers +stability, command syntax, trust, and platform differences. -## Install +| Host | Adapter | Command form | Status | +|---|---|---|---| +| Claude Code | `ca` | `/ca:feature` | Stable | +| Codex CLI | `ca-codex` | `$ca-feature` | Stable | +| Pi | `ca-pi` | `/ca-feature` | Feature Forge `preview` | -codeArbiter self-hosts its plugins from this repository. Install the host adapter you use; all three -governance hosts read the same `.codearbiter/` project state. +**Prerequisites:** Python 3 on `PATH` and `git config user.email` set. Pi also requires Node.js +22.19+. If Python is missing, Pi installs its final wrappers but blocks mutating calls and points to +`/ca-doctor`; Claude Code and Codex surface an interpreter breadcrumb instead of silently claiming +governance is active. ### Claude Code @@ -140,9 +114,10 @@ governance hosts read the same `.codearbiter/` project state. /plugin install ca@codearbiter ``` -Hooks, commands, agents, and statusline wiring load automatically; everything resolves under the `/ca:` namespace. +Approve the normal plugin trust prompt, open the target repository, and continue with +`/ca:init`. -### Codex +### Codex CLI The public GitHub-slug flow is **available now**. It was verified against release `v2.8.13` with `ca-codex 0.2.4`: @@ -152,10 +127,10 @@ codex plugin marketplace add arbiterForge/codeArbiter codex plugin add ca-codex@codearbiter ``` -Open `/hooks`, review and trust the `ca-codex` handlers, then start a fresh thread. Commands use the -`$ca-*` spelling. Run `$ca-init` to opt a repository in and `$ca-doctor` to prove the hooks are live. +Open `/hooks`, review and trust the `ca-codex` handlers, then start a fresh task. Run `$ca-init` and +`$ca-doctor`. -For development against an unpublished checkout, use the local-clone flow: +For development against an unpublished checkout: ```text git clone https://github.com/arbiterForge/codeArbiter @@ -166,410 +141,414 @@ codex plugin add ca-codex@codearbiter ### Pi -> **Feature Forge preview.** `ca-pi` is available for real use now, and you are -> welcome to install it, use it in your repositories, and report what you find. -> It has passed the documented automated and hosted promotion matrix, but it -> still needs broader real-world testing before codeArbiter claims 100% validation -> or stable status. +> [!NOTE] +> `ca-pi` is a Feature Forge `preview`. It is available for real use now, and you are welcome to +> install it, use it in repositories, and report what you find. Its automated and hosted promotion +> matrix is green; broader real-world evidence is still required before codeArbiter claims +> 100% validation or stable status. -Pi distribution is Git-only. Pin the independent `ca-pi` release tag, then inspect the installed -source and enabled resources: +Pi distribution is Git-only. List the independently versioned tags, choose one exact result, then +inspect the installed package, grant project trust, and start a fresh session: -```text +```sh +git ls-remote --tags --refs https://github.com/arbiterForge/codeArbiter.git "ca-pi-v*" pi install git:github.com/arbiterForge/codeArbiter@ca-pi-v pi list pi config ``` -Pi 0.80.5 and Pi 0.80.10 are the supported hosts for this release line. `ca-pi` also requires Node -22.19+ and Python 3. After inspecting the project, grant Pi project trust, start a fresh session, -then run `/ca-init` and `/ca-doctor`. Generated aliases use `/ca-*`; `/skill:ca-*` is the native -fallback. The [Pi parity runbook](./docs/pi-parity-testing.md) covers isolated installation, live -verification, uninstall, and the scope of the module-identity diagnostic. There is no npm release. - -Pi's generated human-readable skill catalog is `plugins/ca-pi/SKILLS.md`, outside the -loader-scanned `skills/` directory. Before the platform aggregate runs any fixture, it checks the -resolved tools workspace; a cold checkout exits with `missing_prerequisite` and directs you to -`npm --prefix plugins/ca-pi/tools ci --ignore-scripts` rather than installing dependencies itself. - -**Prerequisites:** Python 3 on `PATH`. Pi installs its final TypeScript wrappers before bridge -readiness, so a missing interpreter blocks mutating calls and points to `/ca-doctor`; Claude Code -and Codex host shims surface an interpreter breadcrumb instead of silently disabling governance. -Also set `git config user.email` (overrides and ADRs are attributed to -that identity). Full version matrix: [Compatibility](https://arbiterforge.github.io/codeArbiter/getting-started/compatibility/). -The optional /ca:statusline command writes the statusline entry into your -global `~/.claude/settings.json` (it backs up what was there and restores it on removal). +Replace `` with the numeric suffix from the chosen tag while retaining the full +`ca-pi-v...` tag in the install source. -
    -Install from a local clone (for hacking on it) +Pi 0.80.5 and Pi 0.80.10 are supported on this release line. Generated aliases use `/ca-*`; +`/skill:ca-*` is the host-native fallback. There is **no npm release**. The +[Pi runbook](./docs/pi-parity-testing.md) covers isolated install, trust, verification, shared-state +continuity, and uninstall. -
    +### Protect the first repository -```sh -git clone https://github.com/arbiterForge/codeArbiter -``` -```text -/plugin marketplace add ./codeArbiter -/plugin install ca@codearbiter -``` - -
    +Use a disposable Git repository for the first run. Add at least one meaningful source file, then +invoke the host-native forms below: -## Enable codeArbiter in a repo +| Outcome | Claude Code | Codex CLI | Pi | +|---|---|---|---| +| Scaffold project state | `/ca:init` | `$ca-init` | `/ca-init` | +| Prove enforcement | `/ca:doctor` | `$ca-doctor` | `/ca-doctor` | +| Read live state | `/ca:status` | `$ca-status` | `/ca-status` | -Installing a governance plugin does nothing until you opt a repo in. That silence is intentional. -Open the repo and run /ca:init in Claude Code, $ca-init in Codex, or -/ca-init in Pi. It scaffolds -`.codearbiter/` with `arbiter: enabled` -and routes you to the right populator for your situation: +`init` creates `.codearbiter/` and routes an existing codebase to `create-context` or a greenfield +project to `decompose`. `doctor` must report a healthy interpreter, current payload, and H-03 +live-fire probe before you treat the repository as governed. -| You have… | /ca:init routes to | What it does | -|---|---|---| -| an existing codebase | /ca:create-context | back-fills `.codearbiter/` from the source already there | -| a new project, no code yet | /ca:decompose | a layered interview that scaffolds `.codearbiter/` (it's thorough; expect a long, resumable Q&A) | +Follow the complete +[first-repository walkthrough](https://arbiterforge.github.io/codeArbiter/getting-started/quickstart/) +for expected output, proof, and recovery. -Once `.codearbiter/CONTEXT.md` carries the `` marker, you're in normal operation: the next session opens with the orchestrator active and the startup state presented. From there, everything flows through commands. +## The docs are the operating manual -## How it works +The site is designed as a continuous path from first contact to source-backed operation: -The `ca` plugin uses Claude Code's /ca:feature command namespace. The `ca-codex` sibling -exposes the generated surface as $ca-feature, $ca-commit, and -$ca-commands. Pi uses /ca-feature aliases with -/skill:ca-feature as its native fallback. All three adapters load the same policy core. +| Need | Start here | You finish able to... | +|---|---|---| +| Understand the promise | [What is codeArbiter](https://arbiterforge.github.io/codeArbiter/overview/) | explain command, route, dispatch, gate, and ship | +| Choose and install a host | [Start](https://arbiterforge.github.io/codeArbiter/getting-started/choose-your-host/) | install, trust, activate, and verify one adapter | +| Learn in order | [Learning path](https://arbiterforge.github.io/codeArbiter/learn/) | progress from foundation through the power-user capstone | +| Run daily work | [Workflows](https://arbiterforge.github.io/codeArbiter/guides/feature-lane/) | select and complete the smallest correct lane | +| Understand the model | [Concept map](https://arbiterforge.github.io/codeArbiter/concepts/) | trace routing, decisions, context, review, and audit evidence | +| Diagnose a problem | [Troubleshooting](https://arbiterforge.github.io/codeArbiter/guides/troubleshooting/) | follow a symptom to a deterministic recovery | +| Look up exact behavior | [Reference](https://arbiterforge.github.io/codeArbiter/reference/) | inspect generated commands, skills, agents, hooks, and configuration | +| Evaluate preview work | [Feature Forge](https://arbiterforge.github.io/codeArbiter/feature-forge/overview/) | opt in, verify the off switch, and understand the promotion bar | -Activation is **per-repo and explicit**. Claude Code and Codex use `SessionStart`; Pi uses its -`session_start` extension event. Each checks `.codearbiter/CONTEXT.md` for `arbiter: enabled` before -injecting the orchestrator and live startup state. Pi additionally requires an affirmative host -project-trust decision and a fresh session. Without the marker (or without required Pi trust), the -global install stays before the repository-aware boundary. +Command, skill, agent, and hook-gate reference pages are generated from shipped source on every site +build. Hand-authored guides teach the workflow; generated pages define the exact current surface. -The first session of each local day also opens with a read-only repo-hygiene briefing: branch drift against the remote, merged-but-unpruned branches, stale worktrees, and uncommitted or stashed work, all surfaced, never acted on. The full briefing fires **once per day**; later sessions that day stay quiet, with a single-line offer (`run /ca:standup`) only if something is actionable, and **nothing at all when the repo is clean**. The briefing only *reports*; /ca:standup is the separate command that performs the cleanups under per-action confirmation (ff-only pull on a clean tree, branch and worktree pruning, never the default branch). +## How governance works ```mermaid flowchart LR - A(["Host session startup"]) --> B{"CONTEXT.md:
    arbiter enabled?"} - B -->|yes| P{"Pi host?"} - B -->|no| D["dormant"] - P -->|no| C["inject ORCHESTRATOR.md
    + stage · tasks · questions"] - P -->|yes| T{"project trusted?"} - T -->|yes| C - T -->|no| U["inert:
    inspect and grant trust"] - C --> E["host-native commands route to
    gated workflows + reviewers"] - E --> F(["gates clear → it ships"]) + I["host-native command"] --> R["owning skill"] + R --> A["author or operator action"] + A --> V["tests + focused proof"] + V --> F["matched reviewer fleet"] + F --> G{"gate clear?"} + G -->|no| X["named finding or user decision"] + X --> A + G -->|yes| P["selective commit + draft PR"] ``` -On Claude Code, the same flag also gates the optional statusline. Codex has no statusline surface; -its SessionStart briefing presents the governance state instead. ca-pi installs its rich footer in -every interactive parent repository. The governance row appears only when the repository is enabled -and affirmatively trusted; rate-window telemetry is omitted rather than fabricated. +Activation is explicit and per repository. Claude Code and Codex check +`.codearbiter/CONTEXT.md` at SessionStart. Pi checks the same file through its `session_start` +extension event and also requires affirmative project trust. Without `arbiter: enabled`, the global +install is dormant: no repository persona, no repository gates, and no governance row. -Pi execute mode asks before governed mutations and external side effects. Plan mode is read-only -except for the current canonical spec, plan, and plan ledger. Background jobs are bounded and -session-only: they terminate at session switch or shutdown and are never restored from Pi session -entries. Unverified cleanup marks the manager unhealthy, blocks later launches, and points to -`/ca-doctor`. Footer, permission, plan, and background surfaces are parent-interactive only and do -not recurse into hardened children. +The project record lives beside the code: -Project state lives in **your** repo, not the plugin: a single `.codearbiter/` directory at the repo root, so stage, specs, plans, ADRs, the decision log, tribunal reports, and the overrides audit trail commit alongside your code and survive uninstalling the plugin. - - - - - - - -
    Lands in a consumer repoLives elsewhere
    - -Just `.codearbiter/`, nothing else - - - -The host plugin cache (`~/.claude/…` or `~/.codex/…`) +```text +.codearbiter/ +├── CONTEXT.md activation, vocabulary, maturity, current objective +├── tech-stack.md approved technologies and versions +├── coding-standards.md implementation and verification rules +├── security-controls.md security contract reviewers evaluate +├── open-tasks.md durable task board +├── open-questions.md unresolved [CONFIRM-NN] decisions +├── decisions/ numbered ADRs +├── specs/ and plans/ approved intent and execution +├── sprint-log.md SMARTS-scored autonomous decisions +├── checkpoints/ periodic reviewer sweeps +├── audits/ and reports/ assembled evidence +└── overrides.log append-only sanctioned bypass record +``` -
    +The [.codearbiter directory reference](https://arbiterforge.github.io/codeArbiter/codearbiter-directory/) +explains who creates, reads, and updates every artifact, plus its recovery rules. -Three features extend what the plugin tracks across a session. [Provenance and context drift](https://arbiterforge.github.io/codeArbiter/concepts/provenance-drift/): derived docs record their sources; stale derivations surface at `SessionStart` and the commit gate auto-heals them. [Just-in-time context injection](https://arbiterforge.github.io/codeArbiter/concepts/jit-context-injection/): on a read of a governed file, the controlling decision or spec is surfaced at the point of touch. [Board transitions land with the work](https://arbiterforge.github.io/codeArbiter/concepts/hardening-history/): `/ca:task` flips ride the work commit (ADR-0008), not a separate trailing chore. +### The hard gates -## The gates +- No feature code before `tdd` Phase 1 completes. +- No commit outside `commit-gate`, and never with a red suite. +- No `[CONFIRM-NN]` resolved by guessing. +- No silent reconciliation of rule conflicts; route to `/ca:conflict`. +- No raw secret stored in the repository, logs, images, or prompts. +- No direct write to the default branch and no force-push. +- No ADR outside `/ca:adr`, with explicit user attribution. +- Every `/ca:override`, `/ca:dev` entry/exit, and sprint auto-decision is durable and attributable. +- H-18 activation protection has no in-session override path; disabling a repository requires an + external editor and an explicit audit record. -The non-negotiables codeArbiter enforces in every enabled repo: +Security and audit-trail correctness outrank data integrity, maintainability, performance, and +velocity. A non-obvious tradeoff cites the level at which it was made. -- **No feature code before `tdd` Phase 1**: a failing test comes first. -- **No commit without `commit-gate`**, and never on a red suite. "It looks good" is not permission. -- **No `[CONFIRM-NN]` resolved by guessing**: the question is surfaced and work stops. -- **No silent reconciliation** of a conflict between persona, docs, and code; it routes to `/ca:conflict`. -- **No direct-to-`main`, no force-push**: all changes via branch/PR. -- **ADRs only via `/ca:adr`**, with explicit user attribution; an ADR with a `governs:` field pushes back at edit time on the files it constrains. -- **Every `/ca:override`, `/ca:dev` session, and small-lane triage call is logged** to append-only audit logs the hooks mechanically protect from rewrite. +### SMARTS: bounded autonomy, explicit reasoning -When rules pull apart, they resolve by a fixed hierarchy (security & audit-trail correctness first, then data integrity, maintainability, performance, velocity), and a non-obvious tradeoff cites the level it was made at. +SMARTS evaluates Scalable, Maintainable, Available, Reliable, Testable, and Securable properties. +Each option gets a concrete verdict and evidence, followed by a `strong`, `moderate`, or `tied` +recommendation. The user still decides. -## Commands +`/ca:sprint` uses the same lenses for bounded non-hard decisions and writes every call to +`.codearbiter/sprint-log.md` with a confidence flag. Security boundaries, irreversible operations, +gate bypasses, merges, and unresolved questions remain true stops. See +[SMARTS](https://arbiterforge.github.io/codeArbiter/concepts/smarts/) and +[autonomous sprints](https://arbiterforge.github.io/codeArbiter/guides/autonomous-sprints/). -Every intent flows through a command; direct off-channel instructions get redirected to the catalog. -Claude's `/ca:*` catalog is in [`plugins/ca/COMMANDS.md`](./plugins/ca/COMMANDS.md). Codex uses the -generated `$ca-*` catalog in [`plugins/ca-codex/COMMANDS.md`](./plugins/ca-codex/COMMANDS.md), and -Pi uses the generated alias catalog in [`plugins/ca-pi/COMMANDS.md`](./plugins/ca-pi/COMMANDS.md). -The generated counts are `ca: 40`, `ca-codex: 38`, and `ca-pi: 39`. Codex omits `statusline` and -`prune`; Pi omits only `statusline` and implements prune through native compaction. +## Core lanes -| Command | Purpose | -|---|---| -| /ca:feature "desc" | Spec-driven feature: brainstorm → plan → test-first build → commit → finish. **The only path to implementation.** | -| /ca:sprint "goal" | **Autonomous sprint.** One interactive spec gate, then plan-to-PR execution, every auto-decision SMARTS-scored and logged with a confidence flag for your morning review. Security, irreversible ops, and merges still stop for you. | -| /ca:fix "bug" | Regression-test-first defect fix. | -| /ca:commit | The only path to a commit; routes through the nine-gate `commit-gate`. | -| /ca:review | Dispatch the reviewer fleet over the diff; BLOCK on CRITICAL/HIGH. | -| /ca:adr "title" | Author a numbered, user-attributed Architecture Decision Record. | -| /ca:status | Stage, open tasks, unresolved `CONFIRM-NN`, overrides since checkpoint. | -| /ca:audit | One command, one packet: every commit, override, ADR, and autonomous decision in a window, with attribution: the document an auditor actually asks for. | -| /ca:metrics | Read-only trend glance: override rate, small-lane rate, and sprint low-confidence ratio, each with a direction arrow vs. the prior 20-commit window. | +| Intent | Claude Code | What the lane proves | +|---|---|---| +| New behavior | `/ca:feature "desc"` | approved spec and plan, test-first implementation, review, commit, PR | +| Autonomous delivery | `/ca:sprint "goal"` | one interactive design gate, persisted work, logged SMARTS decisions | +| Confirmed defect | `/ca:fix "bug"` | failing regression test before the minimal fix | +| Unknown cause | `/ca:debug "symptom"` | investigation and root-cause decision before any fix lane | +| Structural change | `/ca:refactor "surface"` | behavioral parity through unchanged pre-existing tests | +| Dependency | `/ca:add-dep "pkg"` | license, provenance, maintenance, CVE, and supply-chain review | +| Architecture decision | `/ca:adr "title"` | numbered, dated, user-attributed decision record | +| Commit | `/ca:commit` | the full commit gate and selective staging | +| Pull request | `/ca:pr` | cleared BLOCK findings and a draft PR, never a direct default-branch write | + +Claude Code's catalog is [`plugins/ca/COMMANDS.md`](./plugins/ca/COMMANDS.md). Codex uses the +generated [`plugins/ca-codex/COMMANDS.md`](./plugins/ca-codex/COMMANDS.md), and Pi uses the generated +[`plugins/ca-pi/COMMANDS.md`](./plugins/ca-pi/COMMANDS.md). Current generated counts are `ca: 40`, +`ca-codex: 38`, and `ca-pi: 39`. Codex omits `statusline` and `prune`; Pi omits `statusline` and uses +native compaction for pruning.
    -The full catalog: 40 commands - -
    +All 40 Claude Code commands -**Implementation** +### Implementation | Command | Purpose | |---|---| -| `/ca:feature "desc"` | Spec-driven feature: the only entry to implementation; a logged small lane skips ceremony for small changes | -| `/ca:sprint "goal"` | Autonomous sprint: one spec gate, then plan-to-PR with every auto-decision logged | +| `/ca:feature "desc"` | Spec-driven feature; the only entry to new implementation | +| `/ca:sprint "goal"` | Autonomous spec-to-PR sprint with SMARTS-scored decisions | | `/ca:fix "bug"` | Regression-test-first defect fix | -| `/ca:refactor "surface"` | Behavior-preserving restructure behind a parity-coverage gate | -| `/ca:debug "symptom"` | Investigate-then-decide root-cause analysis | -| `/ca:chore ` | Non-behavioral lane: docs edits, dependency bumps, reverts; type-scaled gates | -| `/ca:spike "question"` | Throwaway exploration on a `spike/*` branch; never merges, exits to a findings note or `/ca:feature` | +| `/ca:refactor "surface"` | Behavior-preserving restructure behind a parity gate | +| `/ca:debug "symptom"` | Investigate, identify root cause, then choose the owning lane | +| `/ca:chore ` | Type-scaled non-behavioral lane | +| `/ca:spike "question"` | Throwaway exploration that never merges | -**Commit & ship** +### Commit and ship | Command | Purpose | |---|---| -| `/ca:commit` | The only path to a commit; routes through `commit-gate` | -| `/ca:pr` | Open / finish a branch; no direct-to-default | -| `/ca:watch ` | Watch a PR's CI server-side: diagnose on red, notify and offer merge on green; never auto-merges | -| `/ca:review [path]` | Reviewer-fleet pass over the diff; BLOCK on CRITICAL/HIGH | -| `/ca:checkpoint` | Lean periodic multi-reviewer sweep | -| `/ca:tribunal [scope-path]` | Deep, rarely-run whole-codebase audit across eleven specialist lenses; one file per finding plus append-only run/triage logs, resumable from disk; files GitHub issues on approval; never a required gate | -| `/ca:release [--dry-run]` | SemVer bump + changelog + annotated tag | -| `/ca:add-dep "pkg"` | Vet a dependency (license, provenance, supply chain) | +| `/ca:commit` | The only path to a commit | +| `/ca:pr` | Clear review findings and open or finish a pull request | +| `/ca:watch ` | Watch hosted CI, diagnose red, offer merge on green | +| `/ca:review [path]` | Reviewer-fleet pass over the current diff | +| `/ca:checkpoint` | Lean periodic whole-codebase reviewer sweep | +| `/ca:tribunal [scope-path]` | Deep, resumable eleven-lens audit; never a routine gate | +| `/ca:release [--dry-run]` | Target-aware SemVer, changelog, and annotated tag | +| `/ca:add-dep "pkg"` | Vet license, provenance, maintenance, CVEs, and supply chain | -**Decisions** +### Decisions | Command | Purpose | |---|---| | `/ca:adr "title"` | Author a numbered, user-attributed ADR | -| `/ca:adr-status [--adr N]` | List/inspect ADR status and supersede chains | -| `/ca:reconcile ["scope"]` | Reconcile artifacts vs. scaffold via SMARTS | -| `/ca:conflict "description"` | Stop all work and surface a rule conflict | +| `/ca:adr-status [--adr N]` | Inspect ADR health and supersession chains | +| `/ca:reconcile ["scope"]` | Reconcile architectural artifacts through SMARTS | +| `/ca:conflict "description"` | Stop work and surface an unresolvable rule conflict | | `/ca:threat-model "scope"` | Optional lightweight STRIDE pass | -**Project & meta** +### Project and meta | Command | Purpose | |---|---| -| `/ca:decompose` | Greenfield: layered interview to populate `.codearbiter/` | -| `/ca:create-context` | Brownfield: back-fill `.codearbiter/` from source | -| `/ca:init` | Scaffold the `.codearbiter/` state store | -| `/ca:status` | Maturity, open tasks, unresolved `CONFIRM-NN`, overrides | -| `/ca:task` | Task-board writer: add a queued task, start one (mints a dotted ID, stamps the date), or mark one done. The only blessed write to `open-tasks.md` | -| `/ca:statusline` | Install/wire the codeArbiter statusline | -| `/ca:doctor` | Prove the install is enforcing: payload, cache staleness, live-fire hook probe | -| `/ca:preview` | Zero-onboarding read-only dry-run of the reviewer fleet on the current diff: predicts reviewers, runs the state-free secret scan, writes nothing | -| `/ca:context-check` | Optional manual drift audit: report stale provenance-tracked docs, then per stale doc offer re-scout, re-baseline, or defer; not the daily loop, `commit-gate` auto-heal owns routine maintenance | -| `/ca:standup` | Daily hygiene: review repo state, then ff-only pull / prune merged branches / remove stale worktrees / surface stashes, each under per-action confirmation | -| `/ca:cleanup` | Finish an already-merged branch: prove ancestry of the fetched default, classify leftover artifacts, `--ff-only` to the default branch, delete the merged local branch, every discard confirmed per item | -| `/ca:new-skill "gap"` | Author a new skill after the gap is proven uncovered | -| `/ca:btw "question"` | Lightweight Q&A; no state change | -| `/ca:override "reason"` | Sanctioned, logged single-identity gate bypass | -| `/ca:audit [range]` | Assemble the governance packet for a window into `.codearbiter/audits/`; read-only | -| `/ca:metrics [--window N]` | Read-only trend glance: override rate, small-lane rate, sprint low-confidence ratio, each with a direction arrow vs. the prior 20-commit window | -| `/ca:prune [status\|dry\|run\|audit\|on\|off]` | Trim transcript clutter to extend session lifetime; dry-run by default, gains land at resume/compaction | -| `/ca:commands` | Show the catalog | - -**Maintainer** +| `/ca:decompose` | Greenfield interview that populates `.codearbiter/` | +| `/ca:create-context` | Brownfield source scout and context backfill | +| `/ca:init` | Scaffold the shared project-state store | +| `/ca:status` | Show stage, branch, tasks, questions, and recent overrides | +| `/ca:task` | The only sanctioned writer for `open-tasks.md` | +| `/ca:statusline` | Install or remove the Claude Code statusline | +| `/ca:doctor` | Prove interpreter, payload, hooks, cache, and live-fire behavior | +| `/ca:preview` | Read-only prediction of reviewers plus a state-free secret scan | +| `/ca:context-check` | Manual provenance-drift audit | +| `/ca:standup` | Read repo hygiene, then confirm safe cleanup actions one by one | +| `/ca:cleanup` | Finish an already-merged branch with ancestry proof | +| `/ca:new-skill "gap"` | Author a new skill only after proving the gap | +| `/ca:btw "question"` | Lightweight project Q&A with no state change | +| `/ca:override "reason"` | Logged, attributed bypass for one immediate action when the gate permits it; never H-18 | +| `/ca:audit [range]` | Assemble a dated governance packet | +| `/ca:metrics [--window N]` | Read-only override, small-lane, and confidence trends | +| `/ca:prune [status\|dry\|run\|audit\|on\|off]` | Inspect or trim transcript bulk | +| `/ca:commands` | Show the public catalog | + +### Maintainer | Command | Purpose | |---|---| -| `/ca:dev ["note"]` | Suspend orchestration to edit codeArbiter itself; requires `CODEARBITER_DEV=1`, entry/exit logged to `overrides.log` | -| `/ca:arbiter` | Exit dev mode: restore orchestration, log the exit | +| `/ca:dev ["note"]` | Enter the env-gated, logged maintainer override | +| `/ca:arbiter` | Exit dev mode and restore orchestration |
    -## Decisions go through SMARTS +## Trust and host boundaries -When the arbiter hits an architectural fork, such as two `accepted` ADRs that disagree or a spec that says one thing while the scaffold does another, it does not pick for you and it does not hand you a naked "A or B?" Every option is scored through **SMARTS** (Scalable, Maintainable, Available, Reliable, Testable, Securable), a fixed six-lens evaluation, and the choice it presents carries that analysis with it. +The blocking enforcement core is local and Python-stdlib-only. It reads repository files and local +Git state. It does not send repository content to a codeArbiter account; there is no hosted +codeArbiter control plane. -Each lens gets one cell per option: a verdict (`Strong`, `Adequate`, `Weak`, or `Indifferent`) plus at most 20 words of justification citing a specific property or failure mode, never "industry standard." That is what lands in front of you, a table, not an opinion: +Two background reads are separate from blocking evaluation: -| Lens | Bundle the auth engine | Customer-provided | -|---|---|---| -| Scalable | Adequate. Sub-ms decisions sufficient at 50-user scale. | Adequate. Same ceiling, adds a network hop. | -| Maintainable | Strong. One package owns versioning and integration. | Weak. Two release cycles must coordinate. | -| Available | Strong. Available whenever the system is. | Weak. Depends on customer infrastructure. | -| Reliable | Strong. Failure contained in the deployment boundary. | Weak. Failure surface includes customer network. | -| Testable | Strong. Local test env is one package install. | Weak. Requires standing up two services. | -| Securable | Strong. Self-contained mandate satisfied. | Weak. Cross-service auditing is harder. | +- SessionStart may run a detached, read-only `git fetch` against the repository's configured remote + to report hygiene state. +- A once-daily, fail-silent update check reads the public GitHub Releases API. It sends no repository + content and never applies an update. -**Recommendation:** Bundle. Strength: **strong**. Securable and Available dominate cleanly, and no lens favors external enough to override. +The optional `/ca:sprint --farm` preview is a distinct opt-in network feature. It sends byte-capped, +secret-redacted task context to the OpenAI-compatible endpoint you configure and is inert without +the flag and provider key. -Every recommendation carries exactly one strength label (`strong`, `moderate`, or `tied`; there is no `weak`) and a `Precedent:` line citing the most similar prior decisions. +See [Enforcement and Security](https://arbiterforge.github.io/codeArbiter/enforcement/) and the +[Hooks Reference](https://arbiterforge.github.io/codeArbiter/hooks/) for the exact event, read, +write, block, and network contract. -**You still decide.** The arbiter recommends; it never records a decision you didn't explicitly make. "Use your best judgment" is declined, because the decision log is append-only and every entry is attributed to a person. +### Pi operational contract -**Autonomy with a paper trail.** /ca:sprint reuses the same six-lens scoring to decide "as the user" on every non-hard-gate point, logging each call to `.codearbiter/sprint-log.md` with a confidence flag (`high` for `strong`, `low` for `moderate` or `tied`) so you know exactly what to skim in the morning. Security boundaries, irreversible operations, gate bypasses, and an unresolved `[CONFIRM-NN]` still stop and wait for you. +`ca-pi` installs a rich footer in interactive parent sessions. The governance row appears only when +the repository is enabled and affirmatively trusted; rate-window telemetry is omitted rather than +fabricated. Execute mode asks before governed mutations and external side effects. Plan mode is +read-only except for the current canonical spec, plan, and plan ledger. -More detail and the full lens definitions: [SMARTS](https://arbiterforge.github.io/codeArbiter/concepts/smarts/) and [Autonomous sprints](https://arbiterforge.github.io/codeArbiter/guides/autonomous-sprints/). +Background jobs are bounded and session-only, and are never restored from Pi session entries. +Unverified cleanup marks the manager unhealthy, blocks later launches, and points to `/ca-doctor`. +Footer, permission, plan, and background surfaces are parent-interactive only and never recurse into +hardened children. -## Claude Code statusline +The human-readable generated catalog is `plugins/ca-pi/SKILLS.md`. On a cold checkout, the platform +aggregate reports `missing_prerequisite` and directs the operator to +`npm --prefix plugins/ca-pi/tools ci --ignore-scripts`; it never installs dependencies on the +verification path. -codeArbiter ships a token-aware statusline. Wire it in with /ca:statusline: +## Feature Forge -
    codeArbiter statusline
    +
    +A dark machined gate under controlled amber heat: preview work accumulating evidence before promotion. +
    -The folder, git/diff, rate limits, token usage, cost, and context segments render in every repo; the arbiter row (stage · tasks · open questions · overrides-since-checkpoint) lights up only in an enabled repo. Token counts come from the session transcript and the **cost is Claude Code's own `cost.total_cost_usd`** (what you actually pay); the context bar shifts toward red as you near compaction, the model pill carries the active model **and** its effort level, and session age sits beside the compaction headroom. +Preview features are built, tested, and shipping, but they remain opt-in and dormant until +real-world evidence earns stable status. A preview label never weakens a hard gate and never hides +spending or trust requirements. -Remove it any time with /ca:statusline; it backs up and restores whatever statusline you had before. +| Feature | Opt-in | Promotion evidence still needed | +|---|---|---| +| `ca-pi` | install a pinned `ca-pi-v*` Git tag | diverse real repositories, providers, terminals, and workflows | +| Live transcript pruning | `CODEARBITER_PRUNE=dry` | real-session context savings and false-positive data | +| Pluggable execution farm | `/ca:sprint --farm` | per-task pass rates, cost, and gate-escape evidence | +| `ca-sandbox` | install the sibling plugin | real untrusted repositories and interactive `--with-claude` use | -## Staying up to date +The source-backed +[live Forge catalog](https://arbiterforge.github.io/codeArbiter/feature-forge/whats-in-the-forge/) +names every opt-in, off switch, dependency, trust boundary, and promotion signal. -codeArbiter self-hosts a **third-party** marketplace, and Claude Code's native plugin auto-update is -enabled by default only for official Anthropic marketplaces: it's **off by default** for a -third-party one like this. The official update mechanism is still the native one; you just have to -turn it on: +The farm preview uses one checked-in `farm.js` backend across supported hosts. A Pi-native embedded +farm worker is a future spike, not a shipping dependency or second engine. npm packaging for +`ca-pi` is also a future spike; pinned Git tags remain the only distribution path. -```text -/plugin marketplace update codearbiter -``` +## Operator details -Run that whenever you want the latest release, or check whether your marketplace auto-updates are -enabled via `/plugin`. Release notes: [Changelog](https://arbiterforge.github.io/codeArbiter/changelog/). +
    +Claude Code statusline -Because that's opt-in, codeArbiter also checks for you: at session start (and in the statusline), it -surfaces a one-line notice when a newer release is published: +Run `/ca:statusline` to install the optional token-aware statusline. -```text -codeArbiter: update available 2.8.11 -> 2.10.0 (run /plugin marketplace update codearbiter) -``` +
    +Annotated codeArbiter statusline +
    -That check is one of exactly two background network touches the plugin makes (see -[What's inside](#whats-inside) for the other, a local `git fetch`): a best-effort, **once-a-day**, -fail-silent, unauthenticated HTTPS GET to the GitHub Releases API, no repo data sent, cached to a -small user-global file. It never blocks session start; it refreshes off to the side, so a slow or -unreachable network never delays your session, and it stays silent if the check fails or you're -already current. It only ever *tells* you; it never applies an update itself. +Folder, Git state, rate limits, token use, cost, context headroom, model, effort, and session age +render globally. The governance row activates only in an enabled repository. Running +`/ca:statusline` again restores the previous statusline. -## Configuration +
    -Every optional behavior is **off by default** and opt-in through an environment variable. codeArbiter never enables one on your behalf. Set them in your shell profile (or per session) to turn them on. +
    +Updates and configuration -| Variable | Default | Effect | -|---|---|---| -| `CODEARBITER_BABYSIT` | `off` | When `on`, /ca:pr auto-attaches a CI watcher to the PR it opens (same as running /ca:watch by hand). Ad-hoc /ca:watch works regardless. | -| `CODEARBITER_BABYSIT_ONRED` | `propose` | The watcher's depth on a red check: `propose` (name the cause, suggest a fix, touch nothing) or `branch` (additionally stage the fix on an unmergeable `spike/fix-*`). | +Update the Claude marketplace explicitly: -Every flag is shipped off, never auto-enabled, and dormant in a repo without `arbiter: enabled`. Preview features carry their own opt-ins; see [Feature Forge](#feature-forge) below. +```text +/plugin marketplace update codearbiter +``` -## Feature Forge +The operator-facing environment-variable catalog, defaults, accepted values, safety boundaries, +and verification steps live in the +[Configuration Reference](https://arbiterforge.github.io/codeArbiter/reference/configuration/). +Release notes are in the [Changelog](https://arbiterforge.github.io/codeArbiter/changelog/). -
    The Feature Forge. Preview features: built, tested, shipping, not yet blessed
    +
    -Some features are built, tested, and shipping in the box, but not yet *blessed*. They live in the **Feature Forge**: off by default, fully dormant until you opt in, and labeled `preview` until real-world data earns them a promotion to a stable release. Nothing here touches your repo or your gates unless you turn it on. A preview graduates when real-world evidence says it's ready; each feature below names how to send that evidence back. Full detail: [What's in the Forge](https://arbiterforge.github.io/codeArbiter/feature-forge/whats-in-the-forge/). +
    +Turn it off or uninstall it -| Feature | Opt-in | Status | How to help it graduate | -|---|---|---|---| -| ca-pi (Pi governance adapter) | install a pinned `ca-pi-v*` Git tag | `preview` | use it in real repositories and report host or workflow mismatches | -| Live transcript pruning | `CODEARBITER_PRUNE=dry` | `preview` | run `dry`, send the log | -| Pluggable execution farm | /ca:sprint --farm | `preview` | run it on a real sprint, report results | -| ca-sandbox (local Codespace) | install the `ca-sandbox` plugin | `preview` | explore real repos in it; run `--with-claude` and report | +A repository without `.codearbiter/CONTEXT.md` carrying `arbiter: enabled` is dormant. Remove `ca` +with Claude Code's plugin manager, run `codex plugin remove ca-codex@codearbiter` for Codex, or use +`pi remove` with the pinned Git source for Pi. The repository-owned `.codearbiter/` record survives, +so another supported host can keep using it. -**ca-pi (Pi governance adapter).** The Pi adapter carries the shared governance core, rich footer, -permission and plan modes, bounded background work, native compaction, and hardened child dispatch. -Its automated and hosted promotion matrix is green, but the diversity of real repositories, -providers, terminals, and workflows still needs more coverage. It is usable now and feedback is -welcome; the Feature Forge label means codeArbiter is not yet claiming 100% validation or stable -status. Install a pinned Git tag using the [Pi runbook](./docs/pi-parity-testing.md), then report any -host or workflow mismatch you encounter. +Follow [Uninstall and Disable](https://arbiterforge.github.io/codeArbiter/guides/uninstalling/) for +host-specific removal, pinning, and verification. -**Live transcript pruning.** Long sessions bloat the transcript until Claude Code compacts early and you lose working headroom; `CODEARBITER_PRUNE=dry` computes every prune it would make and logs the evidence without touching your transcript. The evidence separates model-visible context savings from file-only sidecar cleanup, and only the context figure informs the `dry → on` benefit decision. It's preview because that go/no-go needs real-session evidence first. Details and tuning knobs: [What's in the Forge](https://arbiterforge.github.io/codeArbiter/feature-forge/whats-in-the-forge/). +
    -#### Pluggable execution farm +## Repository map -**What it does.** /ca:sprint --farm runs the implementation step through a `Worker` seam in isolated git worktrees under the same hard gates, instead of a premium subagent. The cheap HTTP-chat worker ships today; the seam is built to admit **premium and agentic** workers behind the same gates (roadmap, not yet built). The worker prompt is enriched with the failing-test source and in-scope files, byte-capped and secret-redacted before transmission. Claude still writes the spec, failing tests, and plan, and **every green task still routes through the full spec-compliance + quality + fresh-verification chain**: a worker can pass the gates, never redefine them. +```text +.claude-plugin/marketplace.json Claude marketplace +.agents/plugins/marketplace.json Codex marketplace +core/ canonical shared policy and generated surface +plugins/ca/ Claude Code governance host +plugins/ca-codex/ Codex governance host +plugins/ca-pi/ Pi governance host (Feature Forge preview) +plugins/ca-sandbox/ isolated local-Codespace plugin +site/ Astro/Starlight documentation product +docs/ architecture, parity runbooks, reports, and media +.codearbiter/ this repository's own governed project state +``` -**Opt-in.** /ca:sprint --farm (needs `FARM_API_KEY`). +`plugins/ca/` contains the always-on orchestrator, public commands, gated skills, dispatched agents, +hooks, and farm tooling. The [architecture docs](./docs/architecture.md), [parity ledger](./docs/parity.md), +and [full hook contract](./docs/hooks.md) are the maintainer entry points. -| Variable | Default | Purpose | -|---|---|---| -| `FARM_API_KEY` | _(required)_ | OpenAI-compatible provider key; never committed, never in audit files. | -| `FARM_MODEL` | _(unset)_ | Skip selection; otherwise the model is auto-selected by measured canary at dispatch. | -| `FARM_ENRICH_MAX_BYTES` | `131072` | Cap on test-source + in-scope context injected into the worker prompt (redacted for secrets). | -| `FARM_CONCURRENCY` | `4` | Max concurrent task workers. | -| `FARM_SAMPLES` | `1` | Parallel candidate draws per task, each in its own scratch worktree; the first to pass the gate is accepted. `FARM_SAMPLES=1` is byte-for-byte the single-candidate path. Total in-flight workers never exceed `FARM_CONCURRENCY`. | -| `FARM_TEMPERATURE` | `0` | Sampling temperature; auto-bumped to `0.7` when `FARM_SAMPLES>1` so samples diversify. Set explicitly to override. | -| `FARM_MAX_TOKENS` | _(unset)_ | Token ceiling per worker call; unset defers to the provider default. | +
    +OpenAI Build Week submission record -**Best-of-N sampling.** Because the gate is a deterministic pass/fail oracle, `FARM_SAMPLES` candidates are drawn in parallel and the first to pass is accepted; the N-fold token cost is recorded in `farm-report.json`. +codeArbiter predates OpenAI Build Week. The submitted work, added July 13 through July 21, 2026, +delivered the Pi host plus a corrective campaign over the shared governance core. It is consolidated +in [PR #313](https://github.com/arbiterForge/codeArbiter/pull/313) at verified implementation head +`ec64809c1b6c32f69e3d9787ab205f7bbd563d50`. -Full config (endpoint, retries, circuit breaker, mutation guard, sovereignty note) is in /ca:sprint and the farm setup doc. It's preview because it is not yet validated on real runs; the promotion bar is the open question `CONFIRM-05`. **Help promote it:** run a real /ca:sprint --farm and report back the per-task pass rates and any gate escapes you see. +Codex was the primary development and orchestration environment. GPT-5.6 was used to design and +implement the Pi adapter, investigate live failures, review older paths, and consolidate corrections +across permissions, Windows process-tree cleanup, provenance, audit integrity, generated-source +drift, CI contracts, and documentation. -Pi keeps `--farm` at the same `preview` level and calls the one checked-in `farm.js` backend through -its trusted parent extension. A Pi-native embedded farm worker is a future spike, not a shipping -dependency or a second engine. npm packaging for `ca-pi` is also a future spike; today it installs -from pinned Git tags only. +The final candidate passed the six Pi platform cells for Pi 0.80.5 and 0.80.10 on Windows, macOS, +and Linux, plus security analysis, CodeQL, shared-core contracts, generated-surface contracts, and +the repository's final verifier. Sanitized evidence: -**ca-sandbox (local Codespace).** A locally-hosted GitHub-Codespace equivalent, shipped as a sibling plugin per ADR-0007: pull a repo you're curious about, including untrusted code, into an ephemeral, isolated Docker container. Your host filesystem is never mounted in (no bind mounts, no docker socket, never `--privileged`), and getting work back out is a host-initiated `cp` only. It ships with a full automated suite green, but the `--with-claude` path (running Claude Code inside the box) is verified only against a dummy token, not yet a real interactive session, so it stays preview until real-world runs earn it a promotion. **Help promote it:** explore real repos in it and report how `--with-claude` behaves in a real session. Install: the `ca-sandbox` plugin from the marketplace, then `/ca-sandbox:sandbox create `. Details: [`plugins/ca-sandbox/README.md`](./plugins/ca-sandbox/README.md). +- [`docs/reports/pi-support/promotion.md`](docs/reports/pi-support/promotion.md) +- [`docs/reports/pi-support/promotion.json`](docs/reports/pi-support/promotion.json) +- [`docs/pi-parity-testing.md`](docs/pi-parity-testing.md) -## What's inside +Judge quick start for the immutable candidate: ```text -.claude-plugin/marketplace.json Claude marketplace -.agents/plugins/marketplace.json Codex marketplace -plugins/ca/ the governance plugin (CLAUDE_PLUGIN_ROOT) -├── .claude-plugin/plugin.json -├── README.md plugin-directory summary (this file is the long form) -├── ORCHESTRATOR.md always-on persona, injected by the SessionStart hook -├── COMMANDS.md command catalog (+ user-facing glossary) -├── SPRINT.md /ca:sprint mode body — the autonomous-sprint procedure -├── commands/ (40) skills/ (23) agents/ (28) -├── includes/ routing-table · reference-map · redirect · farm setup (loaded on demand) -├── hooks/ session-start (activation linchpin) · pre/post gates · statusline → docs/hooks.md -└── tools/ farm dispatcher (farm.js + TypeScript source and tests) -plugins/ca-sandbox/ the local-Codespace plugin (Feature Forge, preview) -plugins/ca-codex/ Codex sibling: generated skills + shared hook core -plugins/ca-pi/ Pi sibling (Feature Forge preview): generated policy + TypeScript adapter + built extensions +pi install git:github.com/arbiterForge/codeArbiter@ec64809c1b6c32f69e3d9787ab205f7bbd563d50 +pi list +pi config ``` -**Skills** encode gated processes: `tdd`, `commit-gate`, `decision-variance`/SMARTS, `debug`, `refactor`, and the dynamic brainstorm → plan → execute workflow layer. **Agents** are the dispatched reviewers and authors: security, auth/crypto, dependency, migration, coverage, and architecture-drift reviewers, the design-quality reviewer, plus the backend/frontend/infra authors and the scout/grader/triage plumbing. - -**Hooks** are how the plugin stays active in your repo, and they run code on your machine, so they're documented in full: [`docs/hooks.md`](./docs/hooks.md) (also mirrored at [/hooks/](https://arbiterforge.github.io/codeArbiter/hooks/)) covers every hook, exactly what it reads and writes, and names the only two things any hook sends over a network: a detached local `git fetch` against your own remote, and a once-a-day fail-silent read of the GitHub Releases API (see [Staying up to date](#staying-up-to-date)). Neither blocks a hook, and no repo data leaves your machine either way. +Inspect the repository, grant project trust, start a fresh Pi session, then run `/ca-init` and +`/ca-doctor`. -## Turning it off - -codeArbiter is dormant by default: a repo without `.codearbiter/CONTEXT.md` → `arbiter: enabled` -never sees the orchestrator persona and never routes through a gate. Remove `ca` with Claude Code's -plugin manager, run `codex plugin remove ca-codex@codearbiter` for Codex, or use `pi remove` with -the pinned Git source for Pi. Either way, `.codearbiter/` survives, so another governance host can -keep using the same specs, ADRs, and audit trail. -Full walkthrough: [Uninstalling](https://arbiterforge.github.io/codeArbiter/guides/uninstalling/). +
    ## Project history -codeArbiter v2 is a ground-up rebuild: from a ~13,600-line `.agents/` + vendoring framework into a native Claude Code plugin. The full story is in [`CHANGELOG.md`](./CHANGELOG.md) and the [site changelog](https://arbiterforge.github.io/codeArbiter/changelog/). The v1 framework is preserved in this repository's early commit history for reference. - -## License +codeArbiter v2 is a ground-up rebuild of the earlier vendored `.agents/` framework as a native, +multi-host plugin system. The complete record is in [`CHANGELOG.md`](./CHANGELOG.md) and the +[site changelog](https://arbiterforge.github.io/codeArbiter/changelog/). The v1 implementation +remains available in early repository history. -codeArbiter is licensed under the [GNU Affero General Public License v3.0](./LICENSE) (AGPLv3). You may use, study, modify, and redistribute it under those terms. Because AGPLv3 covers network use (section 13), running a modified version as a hosted service obligates you to offer that version's complete source under the same license. +## License and contributions -The AGPLv3 transition applies from v2.6.0 forward. Earlier releases, through the last MIT-tagged commit, remain available under the MIT license they shipped with. +codeArbiter is licensed under the +[GNU Affero General Public License v3.0](./LICENSE) (AGPLv3). You may use, study, modify, and +redistribute it under those terms. AGPLv3 section 13 also applies when a modified version is run as a +network service. -## Dual-Licensing & Contributions +The AGPLv3 transition applies from v2.6.0 forward. Earlier releases remain under the license they +shipped with. **Open source.** codeArbiter is available under AGPLv3 for open-source use, free of charge. -**Commercial licensing.** The copyright holder (SUaDtL) retains sole ownership and reserves the right to offer the project under separate proprietary terms. Commercial licenses are not being offered at this time. If you have a use case that AGPLv3 does not fit, you may send an inquiry through GitHub (open an issue or reach the repository owner), and it will be considered if and when a commercial-licensing path is established. +**Commercial licensing.** SUaDtL retains the copyright and reserves the right to offer the project +under separate proprietary terms. Commercial licenses are not being offered at this time. Inquiries +may be made through GitHub if AGPLv3 does not fit a use case. -**Contributions.** Future community contributions require a Contributor License Agreement granting the copyright holder the right to relicense the contribution under both AGPLv3 and proprietary terms, which is what keeps the dual-licensing model intact. See [CLA.md](./CLA.md). That CLA is a template pending legal review and is not yet in force. +**Contributions.** Future community contributions require a Contributor License Agreement that +permits relicensing under AGPLv3 and proprietary terms. See [`CLA.md`](./CLA.md). The CLA is a +template pending legal review and is not yet in force. -
    Built for Claude Code, Codex CLI, and Pi.
    +
    +Built for Claude Code, Codex CLI, and Pi. +
    diff --git a/docs/architecture.md b/docs/architecture.md index 59225518..d07ff2fd 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -19,7 +19,7 @@ capabilities, and tool classes without copying governance policy. | Host | Adapter entry | Public command form | Runtime boundary | |---|---|---|---| | Claude Code (`ca`) | `hooks/hooks.json` | `/ca:` | native hook events and Claude agents | -| Codex CLI (`ca-codex`) | `.codex-plugin/plugin.json` + generated hooks | `$ca-` | compatible hook events; unsupported role surfaces run inline | +| Codex CLI (`ca-codex`) | `.codex-plugin/plugin.json` + generated hooks | `$ca-` | compatible hook events; host-provided agent threads load shared charters, with a bounded older-host inline fallback | | Pi (`ca-pi`) | `extensions/codearbiter.js` | `/ca-` with `/skill:ca-` fallback | TypeScript lifecycle/tool wrappers call the bounded Python bridge; roles use hardened child Pi processes | Pi's parent extension stays dormant until the repository is enabled and Pi diff --git a/docs/banner.svg b/docs/banner.svg deleted file mode 100644 index 46bf4188..00000000 --- a/docs/banner.svg +++ /dev/null @@ -1,125 +0,0 @@ - - codeArbiter - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - codeArbiter - - DISCIPLINE, MECHANICALLY ENFORCED - - - - - - PR - - - - - - - - - - - - - - - - - - - SPEC - - - - - - - - - - - - - - - - - - - - TEST-FIRST - - - - - - - - - - - - - COMMIT-GATE - - - - - - - overrides.log · append-only - diff --git a/docs/feature-forge.svg b/docs/feature-forge.svg deleted file mode 100644 index 1274ef37..00000000 --- a/docs/feature-forge.svg +++ /dev/null @@ -1,76 +0,0 @@ - - The Feature Forge - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - the feature forge - - BUILT · TESTED · SHIPPING · NOT YET BLESSED - - - - - - - - - - - - - - - - - - - - - - - - - FORGE - - - preview channel · opt-in · your data promotes it - diff --git a/docs/feature-forge.webp b/docs/feature-forge.webp new file mode 100644 index 0000000000000000000000000000000000000000..2b4302b0ab640afe09e6432c6ecfca59d1c0f4bf GIT binary patch literal 68474 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≫ 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 UZMQF!v{R=HKmY*ascirN0BCbXJOBUy literal 0 HcmV?d00001 diff --git a/docs/parity.md b/docs/parity.md index 1d85b730..5375aa4a 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -31,7 +31,7 @@ presenting the deliberately nonblocking unsupported-latest canary as supported. |---|---|---|---|---| | Public entries | 39 `/ca:*` commands | 37 `$ca-*` entry skills | 38 `/ca-*` aliases with `/skill:ca-*` fallback | `plugins/*/COMMANDS.md`, `plugins/ca-pi/SKILLS.md` | | Orchestrator routines | 22 generated skills | 22 generated routines | 22 generated routines | `python tools/build-surface.py --check` | -| Role charters | 28 plugin agents | inline until Codex agent packaging lands | 28 generated roles used by hardened child dispatch | `core/surface/agents/`, `plugins/ca-pi/generated/roles.json` | +| Role charters | 28 plugin agents | host-provided agent threads load the shared charters; inline is an older-host fallback | 28 generated roles used by hardened child dispatch | `core/surface/agents/`, `plugins/ca-pi/generated/roles.json` | | Shared Python | stdlib-only core | byte-identical vendored core | byte-identical vendored core behind bounded bridge | `python tools/sync-core.py --check` | | Project store | `.codearbiter/` | same store | same store with `HOST: pi` attribution | `.github/scripts/test_pi_shared_store.py` | @@ -50,8 +50,8 @@ and `ca-pi: 38`. | Git backstop | shared `.git/hooks` installer | same | same through Pi bridge | | Status | complete Claude statusline | startup state only | rich footer globally; governance row only when enabled and affirmatively trusted; rate windows omitted | | Prune/compaction | shared policy plus Claude transcript codec | transcript engine unavailable; audit warning remains | shared policy plus Pi native compaction; no active-session rewrite | -| Role dispatch | Claude subagents | inline review/author fallback | fresh Pi RPC children: single, chain, parallel | -| Process cleanup | host-managed subagents | host-managed inline work | bounded cancellation/timeout plus verified whole-tree cleanup; unhealthy latch on failure | +| Role dispatch | Claude subagents | host-provided agent threads with retained receipts; bounded inline fallback on older hosts | fresh Pi RPC children: single, chain, parallel | +| Process cleanup | host-managed subagents | host-managed agent threads (or current-thread lifecycle for an inline fallback) | bounded cancellation/timeout plus verified whole-tree cleanup; unhealthy latch on failure | | Doctor | interpreter, payload, hooks, live H-03 probe | trusted hook/origin diagnostics | package/origin/trust/collision/core/child/wrapper plus footer/background health | ## Pi live surface classifications @@ -99,7 +99,7 @@ Every exception has a status and a source-visible evidence pointer. | Codex native Read event | HOST-IMPOSSIBLE | Codex exposes no equivalent read hook; governed notices still run after writes. | `plugins/ca-codex/includes/codex-host-notes.md` | | Codex transcript compaction | HOST-IMPOSSIBLE | Claude transcript JSONL is not a Codex session format. | `plugins/ca-codex/includes/codex-host-notes.md` | | Codex statusline | HOST-IMPOSSIBLE | Codex exposes no plugin statusline surface. | `plugins/ca-codex/includes/codex-host-notes.md` | -| Codex packaged agents | DEGRADED | Roles run inline until a supported packaging surface lands. | `plugins/ca-codex/includes/codex-host-notes.md` | +| Codex packaged agents | DEGRADED | The plugin does not vendor custom agent definitions; current hosts still dispatch host-provided threads loaded with the shared charter, while older hosts may fall back inline. Context creation blocks if isolated scouts are unavailable. | `plugins/ca-codex/includes/codex-host-notes.md` | | Pi rate-window telemetry | HOST-IMPOSSIBLE | Pi exposes no supported provider rate-window source, so the rich footer omits it rather than fabricating data. | `plugins/ca-pi/tools/src/footer-state.ts` | | Pi active-dispatch doctor self-test | DEGRADED | Public 0.80.5/0.80.10 APIs cannot submit the deterministic wrapper probe through active dispatch. | `plugins/ca-pi/tools/src/doctor.ts` | | Pi farm route | PREVIEW | Uses the shared backend but awaits real-run promotion under CONFIRM-05. | `plugins/ca-pi/tools/src/farm.ts` | diff --git a/docs/readme-hero.webp b/docs/readme-hero.webp new file mode 100644 index 0000000000000000000000000000000000000000..11cba30ab5dc08f8bb682e613353e95bd293cd9d GIT binary patch literal 55404 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 UZMQF!v{R=HKmY*ascirN0BCbXJOBUy literal 0 HcmV?d00001 diff --git a/site/scripts/generator/generate.ts b/site/scripts/generator/generate.ts index 388c19fb..150ba15b 100644 --- a/site/scripts/generator/generate.ts +++ b/site/scripts/generator/generate.ts @@ -10,6 +10,7 @@ import { renderSkillPage } from "./render-skill-page"; import { buildIndex } from "./build-index"; import { getCommandForgeStatus } from "./forge-status"; import { loadCurated } from "./load-curated"; +import { publicReferenceDescription } from "./render-reference-lead"; import { commandHostAvailability, loadHostCommandCatalogs, @@ -207,7 +208,7 @@ export function generate( slug: slugs[i], title: name, markdown: renderPage(source.type, input), - description: doc.fields.description, + description: publicReferenceDescription(doc.fields.description ?? ""), model: doc.fields.model, forgeStatus, }; @@ -243,13 +244,16 @@ export function generate( }; // The entities come from the Claude Code payload, while each command page // derives its host availability from all three shipped COMMANDS.md catalogs. - // Agent dispatch remains a Claude-specific catalog boundary. + // Agent identities remain a Claude-specific catalog boundary even though + // current Codex hosts can execute the same charter in host-provided threads. const hostNote = "Commands, skills, and agents below are generated from the `ca` (Claude Code) plugin " + "payload. Each command page checks the shipped Claude Code, Codex, and Pi catalogs and marks " + "adapters where that command is not shipped. The Agents catalog below describes Claude " + - "Code's isolated `Task`-tool dispatch — Codex executes the same reviewer and author roles " + - "inline in the current thread instead, until packaged agent dispatch lands for that host. " + + "Code's packaged agent charters. Current Codex releases load those charters into host-provided " + + "agent threads and retain thread receipts; older hosts may fall back to inline execution except " + + "where isolated scouts are mandatory. Pi dispatches hardened children through the parent-only " + + "`codearbiter_dispatch` tool. " + "See [Compatibility → Host Differences](/getting-started/compatibility/#host-differences) " + "for the full per-surface breakdown across all three hosts."; const indexBody = sidebar @@ -276,7 +280,7 @@ export function generate( : "Focused author and reviewer roles a skill may dispatch."; return `${group.items.length}${plural}${purpose}`; }).join("\n"); - const indexContent = `---\ntitle: Reference\ndescription: Source-backed command, skill, and agent catalogs with host syntax, operating context, gates, relationships, and exact shipped source.\n---\n\nThis section is generated from the plugin's own frontmatter and regenerates on every build, so it can never drift from the source. See how the three catalogs cooperate in [How a Request Flows](/overview/#how-a-request-flows): a command routes to an owning skill, which may dispatch specialist agents.\n\n

    \n${catalogCards}\n
    \n\n
    \nUse the catalog from left to right.\n
      \n
    1. Choose the public command that matches the outcome you need.
    2. \n
    3. Follow its owning skill to understand phases, stops, and durable artifacts.
    4. \n
    5. Open an agent only to inspect a dispatched role's tools and constraints; agents are not a second command surface.
    6. \n
    \n

    Every entity page begins with host-native syntax or dispatch context, then curated operating guidance, gates, related routes, and the exact source used to generate it.

    \n
    \n\n${hostNote}\n\n${indexBody}\n`; + const indexContent = `---\ntitle: Reference\ndescription: Source-backed command, skill, and agent catalogs with host syntax, operating context, gates, relationships, and exact shipped source.\n---\n\nEntity identities, frontmatter, host availability, and exact source embeds regenerate from the shipped payload on every build. Curated operating guidance is hand-reviewed and contract-tested, but it can still lag a source change; when the two disagree, the exact source embed is authoritative. See how the three catalogs cooperate in [How a Request Flows](/overview/#how-a-request-flows): a command routes to an owning skill, which may dispatch specialist agents.\n\n
    \n${catalogCards}\n
    \n\n
    \nUse the catalog from left to right.\n
      \n
    1. Choose the public command that matches the outcome you need.
    2. \n
    3. Follow its owning skill to understand phases, stops, and durable artifacts.
    4. \n
    5. Open an agent only to inspect a dispatched role's tools and constraints; agents are not a second command surface.
    6. \n
    \n

    Every entity page begins with host-native syntax or dispatch context, then curated operating guidance, gates, related routes, and the exact source used to generate it.

    \n
    \n\n${hostNote}\n\n${indexBody}\n`; mkdirSync(outDir, { recursive: true }); mkdirSync(dirname(resolvedSidebarPath), { recursive: true }); diff --git a/site/scripts/generator/render-agent-page.ts b/site/scripts/generator/render-agent-page.ts index 83192333..2661e994 100644 --- a/site/scripts/generator/render-agent-page.ts +++ b/site/scripts/generator/render-agent-page.ts @@ -5,7 +5,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; -import { renderReferenceLead } from "./render-reference-lead"; +import { publicReferenceDescription, renderReferenceLead } from "./render-reference-lead"; /** * Render an agent reference page as Starlight-compatible markdown. @@ -21,7 +21,7 @@ import { renderReferenceLead } from "./render-reference-lead"; */ export function renderAgentPage(input: PageInput): string { const { curated, relatedLinks } = input; - const description = input.description ?? ""; + const description = publicReferenceDescription(input.description ?? ""); const descriptionLine = yamlDescriptionLine(description); const frontMatterFields = descriptionLine ? `title: ${input.name} agent\n${descriptionLine}` diff --git a/site/scripts/generator/render-changelog.ts b/site/scripts/generator/render-changelog.ts index 180f7a92..27f37d5e 100644 --- a/site/scripts/generator/render-changelog.ts +++ b/site/scripts/generator/render-changelog.ts @@ -11,7 +11,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; const DESCRIPTION = - "Every released version of codeArbiter, generated from the repository's own CHANGELOG.md."; + "Release history for the core ca plugin, generated from the repository's root CHANGELOG.md, with links to each independently versioned sibling."; const REPO_URL = "https://github.com/arbiterForge/codeArbiter"; @@ -53,9 +53,13 @@ export function renderChangelog(changelogSource: string): string { return ( frontmatterLines.join("\n") + "\n\n" + - "Every released version of codeArbiter. See the " + + "This timeline covers the core `ca` plugin. See the " + "[GitHub Releases page](https://github.com/arbiterForge/codeArbiter/releases) " + - "for the same history with release assets.\n\n" + + "for the same history with release assets. The host and sandbox adapters are versioned " + + "independently; read the [Codex changelog](https://github.com/arbiterForge/codeArbiter/blob/main/plugins/ca-codex/CHANGELOG.md), " + + "[Pi changelog](https://github.com/arbiterForge/codeArbiter/blob/main/plugins/ca-pi/CHANGELOG.md), and " + + "[sandbox changelog](https://github.com/arbiterForge/codeArbiter/blob/main/plugins/ca-sandbox/CHANGELOG.md) " + + "for their release lines.\n\n" + body + "\n" ); diff --git a/site/scripts/generator/render-command-page.ts b/site/scripts/generator/render-command-page.ts index b4101f74..a804dd35 100644 --- a/site/scripts/generator/render-command-page.ts +++ b/site/scripts/generator/render-command-page.ts @@ -4,7 +4,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; -import { renderReferenceLead } from "./render-reference-lead"; +import { publicReferenceDescription, renderReferenceLead } from "./render-reference-lead"; /** * Render a command reference page as Starlight-compatible markdown. @@ -21,7 +21,7 @@ import { renderReferenceLead } from "./render-reference-lead"; */ export function renderCommandPage(input: PageInput): string { const { name, description, forgeStatus, curated, relatedLinks } = input; - const desc = description ?? ""; + const desc = publicReferenceDescription(description ?? ""); let decoration = ""; if (forgeStatus?.kind === "preview-command") { diff --git a/site/scripts/generator/render-reference-lead.ts b/site/scripts/generator/render-reference-lead.ts index d524697e..14d01e36 100644 --- a/site/scripts/generator/render-reference-lead.ts +++ b/site/scripts/generator/render-reference-lead.ts @@ -10,6 +10,32 @@ function escapeHtml(value: string): string { .replaceAll('"', """); } +/** Translate Claude-specific environment placeholders in reader-facing copy. + * The verbatim source embed remains untouched and authoritative. */ +export function publicReferenceDescription(value: string): string { + return value + .replaceAll("${CLAUDE_PROJECT_DIR}/.codearbiter/", "the repository's .codearbiter/") + .replaceAll("${CLAUDE_PROJECT_DIR}/", "the repository root/") + .replaceAll("${CLAUDE_PROJECT_DIR}", "the repository root") + .replaceAll("${CLAUDE_PLUGIN_ROOT}/", "the installed plugin root/") + .replaceAll("${CLAUDE_PLUGIN_ROOT}", "the installed plugin root"); +} + +function compactReferenceSummary(value: string): string { + const abbreviations = new Set(["vs.", "e.g.", "i.e.", "etc.", "mr.", "mrs.", "ms.", "dr."]); + let sentenceCount = 0; + for (let index = 0; index < value.length - 1; index += 1) { + if (value[index] === "." && /\s/.test(value[index + 1] ?? "")) { + const prefix = value.slice(0, index + 1); + const token = prefix.match(/(?:^|\s)(\S+)$/)?.[1]?.toLowerCase() ?? ""; + if (abbreviations.has(token)) continue; + sentenceCount += 1; + if (sentenceCount === 2) return value.slice(0, index + 1); + } + } + return value; +} + const HOST_LABEL: Readonly> = { claude: "Claude Code", codex: "Codex", @@ -58,7 +84,7 @@ function usageFor( return [ 'How it is used', - "

    An owning skill dispatches this specialist. You invoke the command that owns the lane, not the agent directly.

    ", + "

    Invoke the owning command. Claude Code: an isolated plugin agent. Codex: a host-provided agent thread loaded with this charter, with inline execution only as an older-host fallback. Pi: a hardened child through parent-only codearbiter_dispatch.

    ", ].join("\n"); } @@ -74,7 +100,7 @@ export function renderReferenceLead( commandHosts?: CommandHostAvailability, ): string { const label = kind === "command" ? "Command" : kind === "skill" ? "Routed skill" : "Specialist agent"; - const summary = escapeHtml(description); + const summary = escapeHtml(compactReferenceSummary(publicReferenceDescription(description))); return [ `
    `, diff --git a/site/scripts/generator/render-skill-page.ts b/site/scripts/generator/render-skill-page.ts index c4736e8c..1348fc54 100644 --- a/site/scripts/generator/render-skill-page.ts +++ b/site/scripts/generator/render-skill-page.ts @@ -3,7 +3,7 @@ import { yamlDescriptionLine } from "./yaml-quote"; import { renderSourceEmbed } from "./render-source-embed"; import { renderGatesTable } from "./render-gates-table"; import { renderRelatedLinks } from "./render-related-links"; -import { renderReferenceLead } from "./render-reference-lead"; +import { publicReferenceDescription, renderReferenceLead } from "./render-reference-lead"; /** * Render a skill reference page as Starlight-compatible markdown. @@ -18,7 +18,7 @@ import { renderReferenceLead } from "./render-reference-lead"; */ export function renderSkillPage(input: PageInput): string { const { curated, relatedLinks } = input; - const description = input.description ?? ""; + const description = publicReferenceDescription(input.description ?? ""); const descriptionLine = yamlDescriptionLine(description); const frontMatterFields = descriptionLine ? `title: ${input.name} skill\n${descriptionLine}` diff --git a/site/scripts/rehype-table-shell.ts b/site/scripts/rehype-table-shell.ts new file mode 100644 index 00000000..f76a8a14 --- /dev/null +++ b/site/scripts/rehype-table-shell.ts @@ -0,0 +1,51 @@ +/** rehype-table-shell.ts — preserve table semantics while containing overflow. + * + * `overflow-x: auto` only works when a table is changed to `display: block`, + * which breaks the table formatting context and produces the partial-width + * headers and dead strips seen on the Concept Map and Checkpoints pages. + * + * This dependency-free rehype pass wraps every rendered Markdown table in a + * shared scroll shell. The table remains `display: table`; the shell owns + * overflow, focus-independent touch scrolling, border, and radius. + */ + +interface HastNode { + type?: string; + tagName?: string; + properties?: Record; + children?: HastNode[]; +} + +function classNames(node: HastNode): string[] { + const value = node.properties?.className; + if (Array.isArray(value)) return value.filter((item): item is string => typeof item === "string"); + return typeof value === "string" ? value.split(/\s+/) : []; +} + +function wrapTables(node: HastNode): void { + if (!Array.isArray(node.children)) return; + + for (let index = 0; index < node.children.length; index += 1) { + const child = node.children[index]; + const alreadyWrapped = classNames(node).includes("ca-table-shell"); + + if (child.type === "element" && child.tagName === "table" && !alreadyWrapped) { + node.children[index] = { + type: "element", + tagName: "div", + properties: { className: ["ca-table-shell"] }, + children: [child], + }; + continue; + } + + wrapTables(child); + } +} + +/** Rehype plugin factory used by Astro's unified Markdown processor. */ +export function rehypeTableShell() { + return () => (tree: HastNode) => { + wrapTables(tree); + }; +} diff --git a/site/src/assets/logo.svg b/site/src/assets/logo.svg index be892b6c..75edd3c4 100644 --- a/site/src/assets/logo.svg +++ b/site/src/assets/logo.svg @@ -1,36 +1,30 @@ - + codeArbiter - - - - - - - + + + + + + + + + - - - - - - - - - - - + - - - - - - - codeArbiter + + codeArbiter diff --git a/site/src/components/ArbiterIcon.astro b/site/src/components/ArbiterIcon.astro index 837394cf..3f63d4d7 100644 --- a/site/src/components/ArbiterIcon.astro +++ b/site/src/components/ArbiterIcon.astro @@ -25,7 +25,7 @@ interface Props { const { name, label, size = "1.5rem" } = Astro.props; const paths: Record = { start: - '', + '', workflow: '', operate: diff --git a/site/src/components/ConceptNavigator.astro b/site/src/components/ConceptNavigator.astro new file mode 100644 index 00000000..4dbcecca --- /dev/null +++ b/site/src/components/ConceptNavigator.astro @@ -0,0 +1,227 @@ +--- +import ArbiterIcon, { type ArbiterIconName } from "./ArbiterIcon.astro"; + +interface ConceptDestination { + href: string; + title: string; + question: string; + label: string; + icon: ArbiterIconName; +} + +const destinations: ConceptDestination[] = [ + { + href: "concepts/gated-lanes/", + title: "The gated-lane model", + question: "Why must each kind of work follow a different path?", + label: "Work routing", + icon: "gate", + }, + { + href: "concepts/smarts/", + title: "SMARTS", + question: "How does an autonomous sprint choose without asking every time?", + label: "Autonomy", + icon: "sprint", + }, + { + href: "enforcement/", + title: "Enforcement & Security", + question: "Which protections are deterministic and which are advisory?", + label: "Protection", + icon: "operate", + }, + { + href: "concepts/adrs/", + title: "ADRs and the decision log", + question: "How is a long-lived architecture choice recorded?", + label: "Decisions", + icon: "decision", + }, + { + href: "concepts/provenance-drift/", + title: "Provenance and context drift", + question: "How do repository facts stay current as source changes?", + label: "Repository truth", + icon: "repository", + }, + { + href: "concepts/jit-context-injection/", + title: "Just-in-time context injection", + question: "How does the right rule reach an agent at file-read time?", + label: "Context", + icon: "reference", + }, + { + href: "concepts/persona-and-context/", + title: "The persona-register split", + question: "Why are authors, reviewers, and routing kept separate?", + label: "Separation", + icon: "understand", + }, + { + href: "concepts/checkpoints/", + title: "Checkpoints", + question: "What catches drift outside one feature lane?", + label: "Review cadence", + icon: "audit", + }, + { + href: "concepts/auditability/", + title: "Auditability", + question: "What evidence can be reconstructed later?", + label: "Evidence", + icon: "audit", + }, + { + href: "concepts/hardening-history/", + title: "Selected hardening notes", + question: "Why does a particular hardening control exist?", + label: "Design history", + icon: "fix", + }, +]; + +const base = import.meta.env.BASE_URL.endsWith("/") + ? import.meta.env.BASE_URL + : `${import.meta.env.BASE_URL}/`; +--- + + + + + + + {destination.label} + {destination.title} + {destination.question} + + + + )) + } + + + diff --git a/site/src/components/PageContext.astro b/site/src/components/PageContext.astro index cfc75d4a..f07f57d6 100644 --- a/site/src/components/PageContext.astro +++ b/site/src/components/PageContext.astro @@ -10,6 +10,11 @@ interface Props { } const { level, time, outcome, prerequisites = [], proof } = Astro.props; +const timeLabel = time === "Lookup" + ? "Reference lookup" + : time === "Symptom-driven" + ? time + : `Read / walkthrough / ${time}`; const iconByLevel: Record = { Foundation: "start", Practitioner: "workflow", @@ -26,7 +31,7 @@ const iconByLevel: Record = {
    {level} - {time} + {timeLabel}

    Outcome: {outcome}

    { diff --git a/site/src/components/PageTitle.astro b/site/src/components/PageTitle.astro index 82fe034a..d4e5b0d1 100644 --- a/site/src/components/PageTitle.astro +++ b/site/src/components/PageTitle.astro @@ -10,8 +10,9 @@ import PageContext from './PageContext.astro'; const { id, entry } = Astro.locals.starlightRoute; const isHome = id === '' || id === 'index'; +const isGeneratedEntity = /^reference\/(?:commands|skills|agents)\//.test(id); const explicitJourney = entry.data.journey; -const inferred = id.startsWith('reference/') || id === 'reference' || id === 'hooks' || id === 'glossary' +const inferred = id.startsWith('reference/') || id === 'reference' || id === 'hooks' || id === 'glossary' || id === 'changelog' ? { level: 'Reference' as const, time: 'Lookup', outcome: entry.data.description ?? `the source-backed details for ${entry.data.title}` } : id.startsWith('getting-started/') ? { level: 'Foundation' as const, time: '10–15 min', outcome: entry.data.description ?? `a working understanding of ${entry.data.title}` } @@ -29,7 +30,7 @@ const journey = explicitJourney ?? inferred; !isHome && ( <> - + {!isGeneratedEntity && } ) } diff --git a/site/src/components/TrustRow.astro b/site/src/components/TrustRow.astro index ee2bbee5..24b9891c 100644 --- a/site/src/components/TrustRow.astro +++ b/site/src/components/TrustRow.astro @@ -53,7 +53,7 @@ const tiles = [ }
  • stdlib-only - every hook is stdlib-only Python, no third-party dependencies + the shared Python guard core has no third-party runtime dependency
  • diff --git a/site/src/content/docs/concepts.md b/site/src/content/docs/concepts.mdx similarity index 69% rename from site/src/content/docs/concepts.md rename to site/src/content/docs/concepts.mdx index faa38453..1a4c7b21 100644 --- a/site/src/content/docs/concepts.md +++ b/site/src/content/docs/concepts.mdx @@ -10,6 +10,8 @@ journey: proof: "You can trace one real change from its entry lane to the evidence retained after shipment." --- +import ConceptNavigator from "../../components/ConceptNavigator.astro"; + codeArbiter is easiest to understand as a chain: 1. **A request enters a lane.** The lane defines the work, its required evidence, and its stops. @@ -24,18 +26,7 @@ codeArbiter is easiest to understand as a chain: ## Start with the problem you are trying to understand -| Question | Read | -|---|---| -| Why must each kind of work follow a different path? | [The gated-lane model](/concepts/gated-lanes/) | -| How does an autonomous sprint choose without asking every time? | [SMARTS](/concepts/smarts/) | -| Which protections are deterministic and which are advisory? | [Enforcement & Security](/enforcement/) | -| How is a long-lived architecture choice recorded? | [ADRs and the decision log](/concepts/adrs/) | -| How do repository facts stay current as source changes? | [Provenance and context drift](/concepts/provenance-drift/) | -| How does the right rule reach an agent at file-read time? | [Just-in-time context injection](/concepts/jit-context-injection/) | -| Why are authors, reviewers, and routing kept separate? | [The persona-register split](/concepts/persona-and-context/) | -| What catches drift outside one feature lane? | [Checkpoints](/concepts/checkpoints/) | -| What evidence can be reconstructed later? | [Auditability](/concepts/auditability/) | -| Why does a particular hardening control exist? | [Selected hardening notes](/concepts/hardening-history/) | + ## A concrete example diff --git a/site/src/content/docs/concepts/gated-lanes.md b/site/src/content/docs/concepts/gated-lanes.md index bfe316dc..0a4a8027 100644 --- a/site/src/content/docs/concepts/gated-lanes.md +++ b/site/src/content/docs/concepts/gated-lanes.md @@ -77,9 +77,10 @@ Every change lands through a PR, via [finishing-a-development-branch](/reference/skills/finishing-a-development-branch/). Direct merge to the default branch is forbidden. -The one sanctioned way around any of this is [`/ca:override`](/reference/commands/override/): -a deliberate bypass that appends one line to `overrides.log` and proceeds. It is logged, not -silent. +Where a gate permits bypass, the one sanctioned path is +[`/ca:override`](/reference/commands/override/): a deliberate, immediate-action exception that +appends one line to `overrides.log`. It is logged, not silent. H-18 activation protection is the +important exception: it has no in-session override path. ## Check your understanding diff --git a/site/src/content/docs/concepts/persona-and-context.md b/site/src/content/docs/concepts/persona-and-context.md index 9040b20f..2e059248 100644 --- a/site/src/content/docs/concepts/persona-and-context.md +++ b/site/src/content/docs/concepts/persona-and-context.md @@ -63,7 +63,8 @@ may dispatch and the condition that activates it. Then open the generated agent role's tools, model tier, constraints, and exact source. Claude Code can dispatch packaged agents through its native task tool. Codex currently executes the -same reviewer and author responsibilities inline in the active thread; Pi uses its supervised child +same reviewer and author charters in host-provided agent threads, with an inline fallback only on +older hosts where isolation is not mandatory; Pi uses its supervised child path where supported. That host difference changes isolation mechanics, not ownership or the gate that consumes the result. The [compatibility matrix](/getting-started/compatibility/#host-differences) records the current boundary. diff --git a/site/src/content/docs/enforcement.md b/site/src/content/docs/enforcement.md index 2735f24d..98a426a2 100644 --- a/site/src/content/docs/enforcement.md +++ b/site/src/content/docs/enforcement.md @@ -40,7 +40,10 @@ codeArbiter is dormant until a repository opts in. Every enforcement hook calls ## Blocking Commit-Time Gates -These run in `pre-bash.py` on `PreToolUse(Bash|PowerShell)` (plus the Write/Edit guards for the audit trail and ADRs). Each blocks the tool call outright. Ambiguity resolves **closed**: a spelling that cannot be told apart from a destructive one is blocked, and `/ca:override` is the sanctioned escape hatch. +These originate in the shared Python guard core and reach each host through its registered hooks, +adapter, or wrappers. Each blocks the tool call outright. Ambiguity resolves **closed**: a spelling +that cannot be told apart from a destructive one is blocked. `/ca:override` is the sanctioned +escape hatch only for gates that permit bypass; H-18 deliberately does not. | Gate | Enforces | |------|----------| @@ -51,7 +54,7 @@ These run in `pre-bash.py` on `PreToolUse(Bash|PowerShell)` (plus the Write/Edit | **H-09b / H-10b** | Crypto and secret commit gate. A commit that introduces a crypto/TLS or secret line is blocked unless the crypto-compliance / secret-handling gate has recorded a pass for **those exact lines**. | | **H-11** | ADRs are authored only via `/ca:adr`. Both the shell flank (redirects, `cp`, `rm`, `sed -i` into `decisions/`) and the Write/Edit flank are guarded; the skill drops a fresh authoring marker first. | | **H-14** | Migration review. A commit staging a database migration is blocked unless a migration-review pass is recorded for that file's current content. | -| **H-18 / H-19** | Repository activation and gate-pass markers cannot be disabled or forged through shell, Write, Edit, or patch operations. | +| **H-18 / H-19** | Repository activation cannot be disabled through governed shell, Write, Edit, or patch operations. Common gate-pass marker forge paths are blocked as cooperative attestation; a determined same-user process remains outside that guarantee. | | **H-20** | `--no-verify` and its supported short forms cannot bypass the repository's pre-commit or pre-push backstop. | | **H-21** | A write or edit envelope the host adapter cannot decompose into guardable per-file operations is refused instead of being allowed through uninspected. Retry as a plain supported patch or split the operation. | diff --git a/site/src/content/docs/feature-forge/overview.md b/site/src/content/docs/feature-forge/overview.md index 2b0deedb..9594e9ce 100644 --- a/site/src/content/docs/feature-forge/overview.md +++ b/site/src/content/docs/feature-forge/overview.md @@ -20,6 +20,11 @@ anyone's behavior until they turn it on. Once real-world evidence shows it holds promoted to **stable** and becomes on by default. The version says the whole payload moved; the forge says which individual features have earned trust. +
    + +
    Preview is not unfinished. It is shipped work still earning the right to become the default.
    +
    + That is the two-axis model. Read them together and a release is legible: SemVer governs the whole payload, the Feature Forge governs each feature's maturity. diff --git a/site/src/content/docs/getting-started/claude-code-and-codex.md b/site/src/content/docs/getting-started/claude-code-and-codex.md index 7b903256..3d1aee60 100644 --- a/site/src/content/docs/getting-started/claude-code-and-codex.md +++ b/site/src/content/docs/getting-started/claude-code-and-codex.md @@ -138,7 +138,7 @@ absence. That closes the public-installation gate with the same GitHub-slug comm | Statusline | Available | No Codex statusline surface; startup state carries governance status | | Transcript pruning | Claude transcript-pruning engine available | No transcript pruning; the host-neutral audit-staleness warning remains | | Governed-file Read hook | Available on Claude's Read tool | No Codex Read hook; reads happen through shell, while write-time notices remain | -| Reviewer roles | Plugin agents can be dispatched | Roles execute inline until Codex agent packaging reaches its later milestone | +| Reviewer roles | Packaged plugin agents can be dispatched | Host-provided agent threads load the role charter and return a retained thread receipt; older hosts may fall back inline, except context creation requires isolated scouts | These differences are host capabilities and packaging choices. They do not create a second project context, weaken the blocking shell/write gates, or split the audit trail. diff --git a/site/src/content/docs/getting-started/compatibility.md b/site/src/content/docs/getting-started/compatibility.md index d9ca5cae..ecb81648 100644 --- a/site/src/content/docs/getting-started/compatibility.md +++ b/site/src/content/docs/getting-started/compatibility.md @@ -40,19 +40,22 @@ and the [Pi install page](/getting-started/pi/) for the `ca-pi` install flow. | Plan mode | Host plan workflow | Read-only collaboration mode | Plan mode is read-only except for the current canonical spec, plan, and plan ledger | | Background jobs | Host managed | Host managed | Bounded session-only jobs, never restored from Pi session entries; unverified cleanup blocks later launches with `/ca-doctor` direction | | `/ca:sprint --farm` | `preview`, shared `farm.js` backend | `preview`, shared `farm.js` backend | `preview`, same shared `farm.js` backend through the trusted parent extension; no Pi-native farm engine | -| Subagent/child dispatch | Plugin agents dispatched directly | Roles run inline (packaging pending) | Fresh child Pi processes via the parent-only `codearbiter_dispatch` EXEC tool; single/chain/parallel modes share bounded depth, concurrency, timeout, cancellation, and process-tree cleanup | +| Subagent/child dispatch | Plugin agents dispatched directly | Current hosts provide agent threads: codeArbiter loads the role charter and retains the thread receipt; older hosts may fall back inline, but context creation blocks without isolated scouts | Fresh child Pi processes via the parent-only `codearbiter_dispatch` EXEC tool; single/chain/parallel modes share bounded depth, concurrency, timeout, cancellation, and process-tree cleanup | | Transcript pruning / compaction | Claude transcript-pruning engine | No transcript pruning; host-neutral staleness warning | Native Pi compaction event; codeArbiter does not rewrite Pi session JSONL | | Project state | Shared `.codearbiter/` store | Shared `.codearbiter/` store | Shared `.codearbiter/` store | The full exception ledger with status and evidence for every host delta lives in [`docs/parity.md`](https://github.com/arbiterForge/codeArbiter/blob/main/docs/parity.md). -The Pi platform aggregate has a cold prerequisite: if the tools workspace lacks -its resolved Vitest binary, it returns `missing_prerequisite` before fixtures -start and directs the operator to -`npm --prefix plugins/ca-pi/tools ci --ignore-scripts`. It never installs on the -verification path. Footer, permission UI, plan UI, and background jobs are -parent-interactive only and absent from JSON, RPC, print, and hardened children. +:::note[Maintainer verification only] +When running Pi's repository-level platform aggregate, a tools workspace without its resolved +Vitest binary returns `missing_prerequisite` before fixtures start. Maintainers can prepare that +workspace with `npm --prefix plugins/ca-pi/tools ci --ignore-scripts`; the verification path never +installs dependencies itself. This setup is not required to install or use codeArbiter. +::: + +Footer, permission UI, plan UI, and background jobs are parent-interactive only and absent from +JSON, RPC, print, and hardened children. ## Prerequisites Checklist diff --git a/site/src/content/docs/getting-started/install.md b/site/src/content/docs/getting-started/install.md index f00c528b..885ae784 100644 --- a/site/src/content/docs/getting-started/install.md +++ b/site/src/content/docs/getting-started/install.md @@ -29,12 +29,21 @@ Confirm both before installing: failure, and Pi blocks mutating calls with an interpreter breadcrumb. None of those states is active governance. Verify before installation with: + macOS / Linux: + ```sh python3 --version || python --version ``` - Either succeeding is enough: hooks are registered under both names, falling back to whichever - resolves. + Windows PowerShell: + + ```powershell + if (Get-Command python -ErrorAction SilentlyContinue) { python --version } elseif (Get-Command python3 -ErrorAction SilentlyContinue) { python3 --version } else { throw "Python 3 was not found on PATH" } + ``` + + Either interpreter name is sufficient. Host registration is not identical: Claude Code carries + interpreter-compatibility commands in `hooks.json`, Codex has OS-specific `command` and + `commandWindows` entries, and Pi validates the interpreter before its bridge admits mutations. - **`git config user.email` set**: overrides and ADRs are attributed to this identity. Verify with: @@ -86,8 +95,8 @@ Pi is a third governance host, `ca-pi`, distributed Git-only (no npm release) wi line and prerequisites. The complete adapter is a Feature Forge `preview`: it is available and welcomed for real use, with broader testing still required before stable status or a claim of 100% validation. It is not covered here; see [Install for Pi](/getting-started/pi/) for the full flow, -including `pi install git:github.com/arbiterForge/codeArbiter@ca-pi-v` and the project-trust -step Pi requires before it activates. + including mechanical tag discovery, an exact pinned `ca-pi-v` install, and the + project-trust step Pi requires before it activates. ## 2. Scaffold and Activate the Repo diff --git a/site/src/content/docs/getting-started/pi.md b/site/src/content/docs/getting-started/pi.md index f85d18f3..8907276c 100644 --- a/site/src/content/docs/getting-started/pi.md +++ b/site/src/content/docs/getting-started/pi.md @@ -6,7 +6,7 @@ journey: time: "12 minutes" outcome: "Install a pinned ca-pi preview, trust the project, opt in a repository, and verify a real gate." prerequisites: - - "Pi 0.80.10 or newer" + - "Pi 0.80.5 or Pi 0.80.10" - "Node.js 22.19 or newer" proof: "Pi reports the pinned extension and a disposable broad-stage probe is blocked by H-03." --- @@ -31,7 +31,14 @@ Confirm all before installing: ## 1. Install -Pi distribution is Git-only. Pin the independent `ca-pi` release tag, then inspect the installed +Pi distribution is Git-only. First list the repository's published Pi tags; do not guess a version +or substitute the core plugin's release number: + +```sh +git ls-remote --tags --refs https://github.com/arbiterForge/codeArbiter.git "ca-pi-v*" +``` + +Choose an exact tag from that output, pin it in the install source, then inspect the installed source and enabled resources: ```text @@ -40,7 +47,9 @@ pi list pi config ``` -`pi list` and `pi config` let you verify the installed source before trusting it. See +Replace `` with only the numeric suffix from the chosen tag. For example, the tag +`ca-pi-v0.1.32` maps to `@ca-pi-v0.1.32`; keep the full tag in the source. `pi list` and +`pi config` let you verify the installed source before trusting it. See [Trust and Security](#trust-and-security) below. ## 2. Grant Project Trust diff --git a/site/src/content/docs/glossary.md b/site/src/content/docs/glossary.md index 12fc97b9..a6bddf72 100644 --- a/site/src/content/docs/glossary.md +++ b/site/src/content/docs/glossary.md @@ -24,6 +24,14 @@ A non-blocking reminder that surfaces right after a write (e.g. a crypto pattern change) so the blocking gate at commit time is not a surprise. Advisories never stop a tool call. See [Enforcement & Security](/enforcement/#advisory-non-blocking-reminders). +## Agent + +A focused author or reviewer role dispatched by a [skill](#skill). An agent is not a public +command and not a second orchestrator; it receives only the tools and context its role needs. +Claude Code dispatches packaged plugin agents. Current Codex releases load the equivalent charter +into host-provided agent threads, with inline execution only as an older-host fallback; Pi uses +hardened child dispatch. See the [Agents reference](/reference/#agents). + ## Arbiter (enabled flag) The `arbiter: enabled` line in `.codearbiter/CONTEXT.md`'s frontmatter: the single activation @@ -33,9 +41,10 @@ without it loads nothing and blocks nothing. See ## Blocking gate -A gate whose failure stops the tool call outright: the call never happens, and the only -sanctioned way past it is fixing the underlying issue or a logged `/ca:override`. Contrast with -an [advisory](#advisory). See [Enforcement & Security](/enforcement/#blocking-commit-time-gates). +A gate whose failure stops the tool call outright: the call never happens. Most blocks are resolved +by fixing the issue or, when the gate permits it, a logged `/ca:override`; H-18 activation +protection deliberately has no in-session override path. Contrast with an [advisory](#advisory). +See [Enforcement & Security](/enforcement/#blocking-commit-time-gates). ## Board @@ -49,6 +58,12 @@ A periodic, read-only sweep of the whole codebase by the reviewer fleet, consoli dated report under `.codearbiter/checkpoints/`. Catches drift between feature work without blocking any single change. See [Checkpoints](/concepts/checkpoints/). +## Command + +A public entry point the user invokes for an outcome, using the current host's syntax: +`/ca:name` in Claude Code, `$ca-name` in Codex, or `/ca-name` in Pi. The orchestrator routes that +command to its owning skill. See the [Commands reference](/reference/#commands). + ## CONFIRM-NN The placeholder for an unresolved question only the user can answer, numbered and recorded in @@ -85,6 +100,12 @@ A sanctioned path through the system, with gates scaled to the work's risk: impl commit & ship, decisions, or project & meta. Not a "workflow" and not a "track." See [The Gated-Lane Model](/concepts/gated-lanes/). +## Layer + +A level in the greenfield decomposition interview. “Layer” describes how `/ca:decompose` structures +discovery; it is not a project maturity stage, skill phase, or enforcement gate. See the +[`decompose` skill reference](/reference/skills/decompose/). + ## Marker A small file under `.codearbiter/.markers/` that records a gate's pass state, for example @@ -106,9 +127,10 @@ freelances. See [The Persona-Register Split](/concepts/persona-and-context/). ## Override -The sanctioned, logged bypass of a gate, invoked as `/ca:override "reason"`. It appends one -permanent line to `.codearbiter/overrides.log` before proceeding. Never call this a "workaround" -or a "skip." See [Override a Gate Safely](/guides/overriding-a-gate/). +The sanctioned, logged bypass for a gate that allows bypass, invoked as `/ca:override "reason"`. +It appends one permanent line to `.codearbiter/overrides.log` before the immediate action proceeds. +H-18 is intentionally non-overridable from the governed session. Never call an override a +“workaround” or a “skip.” See [Override a Gate Safely](/guides/overriding-a-gate/). ## Persona @@ -116,6 +138,12 @@ A named voice codeArbiter speaks with: the terse orchestrator, or a focused auth agent, each scoped to its own job and context footprint. See [The Persona-Register Split](/concepts/persona-and-context/). +## Phase + +One named step inside a skill, with an entry purpose and often a gate at its exit. A phase is not a +project [stage](#stage) or a decomposition [layer](#layer). See +[The Gated-Lane Model](/concepts/gated-lanes/). + ## Provenance The per-doc evidence trail that backs a derived `.codearbiter/` document (which source lines @@ -128,6 +156,18 @@ The named framework an autonomous `/ca:sprint` uses to decide "as the user" on n points, scoring each auto-decision and logging it with a confidence flag. See [SMARTS](/concepts/smarts/). +## Severity + +A finding classification—CRITICAL, HIGH, MEDIUM, or LOW—that expresses impact. Severity does not +itself say whether a phase must STOP or BLOCK; gate action is a separate decision. See +[Checkpoints](/concepts/checkpoints/#the-funnel). + +## Skill + +An orchestrator routine with named phases. The orchestrator routes to a skill from a command, and +the skill may dispatch agents; users do not invoke internal skill files as an alternate command +surface. See the [Skills reference](/reference/#skills). + ## Spike A time-boxed exploration on a disposable `spike/*` branch that answers one named question. It @@ -143,8 +183,9 @@ remain true stops even under autonomy. See ## Stage -A single project-maturity number in `.codearbiter/CONTEXT.md`'s frontmatter. It scales how -strict a gate behaves for this project and is surfaced by `/ca:status` and the statusline. See +A single project-maturity number in `.codearbiter/CONTEXT.md`'s frontmatter. It records where the +project is in its maturity progression and is surfaced by `/ca:status` and the statusline; it is not +a skill phase, decomposition layer, gate action, or finding severity. See [The `.codearbiter/` Directory Reference](/codearbiter-directory/#contextmd). ## Statusline diff --git a/site/src/content/docs/guides/ca-sandbox.md b/site/src/content/docs/guides/ca-sandbox.md index 7838b8d8..d7679399 100644 --- a/site/src/content/docs/guides/ca-sandbox.md +++ b/site/src/content/docs/guides/ca-sandbox.md @@ -54,7 +54,11 @@ The isolation holds by construction, not by trust: ## Install +Register the self-hosted marketplace first if `codearbiter` is not already listed, then install the +sandbox sibling: + ```text +/plugin marketplace add arbiterForge/codeArbiter /plugin install ca-sandbox@codearbiter ``` diff --git a/site/src/content/docs/guides/opt-in-a-repo.md b/site/src/content/docs/guides/opt-in-a-repo.md index 4f316d9f..4ab4155d 100644 --- a/site/src/content/docs/guides/opt-in-a-repo.md +++ b/site/src/content/docs/guides/opt-in-a-repo.md @@ -12,9 +12,10 @@ journey: proof: "A fresh session shows the startup briefing, and doctor completes its harmless live-fire probe." --- -The repository opt-in is shared by Claude Code and Codex. Run `/ca:init` in Claude Code or -`$ca-init` in Codex; both create or observe the same `.codearbiter/` directory. See the -[Claude Code + Codex evidence](/getting-started/claude-code-and-codex/) for mixed-host operation. +The repository opt-in is shared by Claude Code, Codex, and Pi. Run `/ca:init` in Claude Code, +`$ca-init` in Codex, or `/ca-init` in Pi; all three create or observe the same `.codearbiter/` +directory. See the [Claude Code + Codex evidence](/getting-started/claude-code-and-codex/) for a +worked mixed-host example, and [Pi](/getting-started/pi/) for Pi's parent-process trust boundary. The plugin installs once, globally. Enabling enforcement is a per-repo step you run once inside each repository you want covered. Complete the [plugin install](/getting-started/install/) before starting here. diff --git a/site/src/content/docs/guides/overriding-a-gate.md b/site/src/content/docs/guides/overriding-a-gate.md index 47edbeb3..946117e2 100644 --- a/site/src/content/docs/guides/overriding-a-gate.md +++ b/site/src/content/docs/guides/overriding-a-gate.md @@ -1,6 +1,6 @@ --- title: "Override a Gate Safely" -description: "Bypass a blocked gate with /ca:override, the only sanctioned path: one audit line is appended to overrides.log, your identity comes from git config user.email, and the bypass is permanent in the trail." +description: "Bypass an overridable gate with /ca:override: one audit line is appended to overrides.log, your identity comes from git config user.email, and the immediate exception is permanent in the trail." journey: level: "Power user" time: "9 minutes" @@ -11,7 +11,11 @@ journey: proof: "The immediate action proceeds and exactly one attributable line appears in overrides.log." --- -A gate blocked your command and the action is genuinely justified. Use `/ca:override "reason"` to bypass it. That is the only sanctioned path, since any other bypass leaves no record in the audit trail. +An overridable gate blocked your command and the action is genuinely justified. Use +`/ca:override "reason"` to bypass the immediate action. That is the only sanctioned bypass path, +because an alternate bypass leaves no record in the audit trail. H-18 activation protection is not +overridable from inside the governed session; [Uninstall & Disable](/guides/uninstalling/) documents +its external-edit boundary. Before running it, identify which kind of gate you are facing. The two paths are different. @@ -33,6 +37,7 @@ Hard gates exist because the cost of the mistake they prevent is higher than the Do not use `/ca:override` when: +- H-18 blocked an attempt to disable repository activation. It has no in-session override path. - The gate caught a real problem. Fix the problem instead: replace the banned primitive, correct the failing test, remove the secret. - Routine work passed all gates. The command is not needed and running it creates a permanent log entry for no reason. - Two sources conflict rather than one blocking the other. Use `/ca:conflict` instead. diff --git a/site/src/content/docs/guides/uninstalling.md b/site/src/content/docs/guides/uninstalling.md index 5ae33eed..d286429a 100644 --- a/site/src/content/docs/guides/uninstalling.md +++ b/site/src/content/docs/guides/uninstalling.md @@ -7,13 +7,13 @@ journey: outcome: "Disable one repository or remove a host plugin without losing project records or another tool's git hooks." prerequisites: - "Know whether you want repository dormancy, one-host removal, or complete removal" - proof: "A fresh session is dormant or the host plugin is absent, retained state is intentional, and only codeArbiter-owned hook shims were removed." + proof: "A fresh session is dormant or the host plugin is absent, retained state is intentional, and git-hook registry entries or shared shims were removed without breaking another installed host." --- codeArbiter's pitch is enforcement, so it documents the exit. This page covers three levels: turning it off in one repository, removing it globally, and what to check before uninstalling mid-feature. -Claude Code and Codex share `.codearbiter/`, so removing either plugin leaves project context and -audit history available to the other host. +Claude Code, Codex, and Pi share `.codearbiter/`, so removing one host plugin leaves project context +and audit history available to the others. ## Choose the exit you actually want @@ -40,17 +40,21 @@ redirect, `Write`, or `Edit` that would change `arbiter: enabled` to `arbiter: d corrupt the frontmatter) through `.codearbiter/CONTEXT.md`. The gates cannot silence themselves from inside the repo they govern. Reads (`cat`, `grep`) still pass through untouched. -Unlike the crypto/secret and migration gates (H-09b/H-10b, H-14), H-18 has no marker-based bypass that -`/ca:override` can unlock. Its block calls in `pre-bash.py`, `pre-write.py`, and `pre-edit.py` are -unconditional: there is no override flag the hook code checks. Running `/ca:override` first does not -make the block go away; the hook still fires on the next Bash/Write/Edit call that touches -`CONTEXT.md`. That's deliberate: the block exists precisely so a session cannot talk itself past it. - -The honest, sanctioned path is to make the edit through a channel H-18 doesn't intercept, because it -only subscribes to Claude Code's own `PreToolUse` hook on the Bash, Write, and Edit tools: - -1. Close or step outside the active Claude Code session for that repo (or use a different terminal / - editor entirely: a plain text editor, `vim`, VS Code, a shell outside Claude Code's Bash tool). +Unlike the crypto/secret and migration gates (H-09b/H-10b, H-14), H-18 has no marker-based bypass +that `/ca:override` can unlock. The shared guard's activation block is unconditional, whether the +host reaches it through Claude's Python hooks, Codex's adapter, or Pi's wrappers. Running an +override first does not make the next governed mutation to `CONTEXT.md` pass. That is deliberate: +the block exists precisely so a session cannot talk itself past it. + +The honest, sanctioned path is to make the edit through a channel H-18 does not intercept. Across +Claude Code, Codex, and Pi, the protected boundary is the same: Claude Code guards Bash, Write, and +Edit; Codex guards shell and patch/write adapters; Pi guards its final +mutator wrappers. All three protect the same activation boundary, but none can police an external +editor or process outside the active host's tool path: + +1. Close or step outside the active Claude Code, Codex, or Pi session for that repo. Use a different + terminal or editor entirely: a plain text editor, `vim`, VS Code, or a shell not mediated by the + active agent host. 2. Edit `.codearbiter/CONTEXT.md` directly and change the frontmatter line to: ```yaml @@ -64,13 +68,12 @@ only subscribes to Claude Code's own `PreToolUse` hook on the Bash, Write, and E before, because the log entry and the edit are independent; only the edit itself needs to happen outside the tool-mediated path). This keeps `.codearbiter/overrides.log` an honest record of the change even though the hook itself never granted permission. -4. Verify: open a new Claude Code session in the repo. No orchestrator persona loads, the statusline's - arbiter row (stage · tasks · questions · overrides) does not render, and any tool call proceeds - without a gate check. +4. Verify: open a fresh session in the host you use. No orchestrator persona loads, host-specific + project-state UI stays dormant, and a read-only status check does not report active governance. There is no hidden bypass inside a session, and this page will not pretend there is one. H-18 guards -exactly the tool flanks Claude Code mediates, and an edit outside those tools was never something it -could stop. +the mutating tool flanks each host mediates; an edit outside those host-managed tools was never +something it could stop. Re-enabling is the same edit in reverse (`arbiter: disabled` → `arbiter: enabled`), no override required. H-18 only blocks disabling the switch, not re-enabling it. @@ -83,28 +86,83 @@ welcome while broader testing continues before stable status. `ca-pi` is distributed Git-only, versioned independently as `ca-pi-v` tags (not tied to the `ca`/`ca-codex` release cadence). There is no npm release and no auto-update. -**Upgrade or pin a version:** re-run `pi install` with the new pinned tag: +List the published tags, then compare them with the exact source shown by `pi list`: + +```sh +git ls-remote --tags --refs https://github.com/arbiterForge/codeArbiter.git "ca-pi-v*" +pi list +``` + +**Upgrade or pin a version:** choose an exact tag from the first command and re-run `pi install` +with that full tag: ```text pi install git:github.com/arbiterForge/codeArbiter@ca-pi-v ``` -**Uninstall:** +**Uninstall:** copy the exact installed Git source from `pi list` so the removal target matches: ```text pi remove git:github.com/arbiterForge/codeArbiter@ca-pi-v ``` -(`pi uninstall` is the equivalent alias.) Confirm removal with `pi list`. +Replace `` with the installed tag's numeric suffix; `pi uninstall` is the equivalent +alias. Confirm removal with `pi list`. Uninstalling `ca-pi` does not touch `.codearbiter/` in any repository: that state survives, the same as for Claude Code and Codex, and another governance host can pick it up. ## Full Uninstall -Removing codeArbiter entirely has four independent pieces. Do them in this order. +Removing codeArbiter entirely has five pieces. Order matters because the first two use files inside +the installed payload. + +### 1. Remove the Claude Code Statusline + +If you wired the statusline with /ca:statusline, remove it **before** uninstalling the +plugin (the command needs the plugin's `wire-statusline.py` to run): + +```text +/ca:statusline uninstall +``` + +This restores whatever `statusLine.command` was in `~/.claude/settings.json` before you wired +codeArbiter in (or removes the key entirely if there was none), and restores any prior `spinnerVerbs` +the same way. See [Set Up the Statusline](/guides/the-statusline/#remove-the-statusline) for the full +behavior. + +If you already uninstalled the plugin first, `/ca:statusline uninstall` is no longer available. Edit +`~/.claude/settings.json` by hand and remove the `statusLine` entry that points at +`hooks/statusline.py` under the plugin's cache path. + +### 2. Remove Each Host's Git-Backstop Registration -### 1. Uninstall the Plugin +An activated host may install one shared POSIX-shell shim at `pre-commit` and `pre-push` (under the +repository's effective Git hooks directory). The shim reads each +`/codearbiter-hooksd/.path` entry, runs **every live registered enforcer**, +and allows Git to continue only when every one allows. That closes shell-indirection gaps without +making one host's plugin cache path the shared hook's owner. + +Before uninstalling a host package, run that host's own `_githooks.py uninstall` against every +repository where it was active. This removes only that host's registry entry and its owned +trusted-identity record. It deliberately leaves the shared shims because another installed host may +still depend on them. + +From a codeArbiter checkout, run the command for each host you are removing. Replace +`C:\path\to\repo` with the target repository: + +```powershell +python plugins/ca/hooks/_githooks.py uninstall C:\path\to\repo +python plugins/ca-codex/hooks/_githooks.py uninstall C:\path\to\repo +python plugins/ca-pi/hooks/_githooks.py uninstall C:\path\to\repo +``` + +Run only the rows for hosts that were installed. The commands are safe to repeat: a host with no +entry reports no change. Run `git rev-parse --git-common-dir` inside the target repository and append +`codearbiter-hooksd` to locate the shared registry. The common directory is intentional: linked +worktrees share the same host registrations. + +### 3. Uninstall the Host Packages Claude Code: @@ -118,22 +176,45 @@ Codex: codex plugin remove ca-codex@codearbiter ``` -Pi (Git-only; see [Pi](#pi) above for pinning and version-specific removal): +Pi (Git-only; use `pi list` to copy the installed source and the tag lookup in [Pi](#pi) above): ```sh pi remove git:github.com/arbiterForge/codeArbiter@ca-pi-v ``` -This removes the plugin payload (hooks, commands, agents, skills) from Claude Code's plugin cache. -Hooks, commands, and the statusline wiring stop loading from the next session onward. `claude plugin -update` is **not** sufficient for a full removal. It can leave a stale cached payload behind when the -marketplace version string hasn't changed; `uninstall` is the clean path. +The effect is host-specific: + +- **Claude Code removes** the plugin payload (hooks, commands, agents, and skills) from its plugin + cache. Hooks, commands, and statusline wiring stop loading from the next session onward. + `claude plugin update` is **not** sufficient for a full removal: it can leave a stale cached payload + behind when the marketplace version string has not changed, so `uninstall` is the clean path. +- **Codex removes** the `ca-codex@codearbiter` plugin registration and stops discovering its packaged + skills and hooks in subsequent sessions. +- **Pi removes** the selected Git package source from Pi's installed packages. New sessions stop + loading the codeArbiter extension, skills, prompts, and theme from that source. The marketplace entry (`codearbiter`) itself was added at install time with `/plugin marketplace add arbiterForge/codeArbiter`. If you want that gone too, you can manage it through the `/plugin` command's interactive marketplace management screen in any Claude Code session. -### 2. Decide What Happens to `.codearbiter/` +### 4. Remove the Shared Git Shims After the Last Host + +Skip this step when any codeArbiter governance host remains installed. For a complete uninstall, +first resolve the directory Git actually reads: + +1. Run `git config --get core.hooksPath`. +2. If it prints a path, use that directory. Resolve a relative value from the repository root + reported by `git rev-parse --show-toplevel`. +3. If it prints nothing, run `git rev-parse --git-path hooks` and use that directory. + +Inspect `pre-commit` and `pre-push` inside that directory. Delete a hook only when its first lines +contain the exact sentinel `# codeArbiter-managed git hook (#161)`. A hook without that sentinel +belongs to another tool and must remain. After both sentinel shims are gone, remove +`/codearbiter-hooksd` only if you have already removed every installed host's +registry entry. An empty registry fails closed, so do not leave the shared shim pointing at an empty +directory after a complete uninstall. + +### 5. Decide What Happens to `.codearbiter/` Uninstalling the plugin does **not** touch `.codearbiter/` in any repository. That directory is your repo's state (specs, plans, ADRs, the decision log, tribunal reports, and the append-only overrides @@ -154,54 +235,6 @@ Windows PowerShell the scoped equivalent is `Remove-Item -LiteralPath .codearbit the verified repository root. This deletion is not required for dormancy or plugin removal and is not recoverable unless Git or your archive contains the files. -### 3. Remove the Statusline - -If you wired the statusline with /ca:statusline, remove it **before** uninstalling the -plugin (the command needs the plugin's `wire-statusline.py` to run): - -```text -/ca:statusline uninstall -``` - -This restores whatever `statusLine.command` was in `~/.claude/settings.json` before you wired -codeArbiter in (or removes the key entirely if there was none), and restores any prior `spinnerVerbs` -the same way. See [Set Up the Statusline](/guides/the-statusline/#remove-the-statusline) for the full -behavior. - -If you already uninstalled the plugin first, `/ca:statusline uninstall` is no longer available. Edit -`~/.claude/settings.json` by hand and remove the `statusLine` entry that points at -`hooks/statusline.py` under the plugin's cache path. - -### 4. Remove the `.git/hooks` Backstop - -Every session, codeArbiter installs a small POSIX-shell shim into each repo's `.git/hooks/pre-commit` -and `.git/hooks/pre-push` (or wherever `core.hooksPath` points), which calls `git-enforce.py` at the -git operation itself. This closes the gap where a shell command constructed to dodge the `PreToolUse` -Bash hook's literal-string match (`g=git; c=commit; $g $c`) would otherwise slip past enforcement -entirely. - -These shims are managed only in repositories you opened with the plugin active, and only removed -automatically the next time codeArbiter runs there, which won't happen once the plugin is -uninstalled. Remove them by hand, per repository: - -```sh -python plugins/ca/hooks/_githooks.py uninstall . -``` - -(Run this from a checkout that still has the plugin's hook files present: for example, before you -uninstall the plugin, or from a fresh clone of the codeArbiter repo pointed at your target repo's path -as the second argument.) `_githooks.py uninstall` removes only shims that carry codeArbiter's sentinel -comment; a pre-existing hook from another tool (husky, pre-commit-framework) is left untouched. - -If that script isn't available, remove the files directly. They only exist if codeArbiter installed -them (check for the sentinel comment `# codeArbiter-managed git hook (#161)` at the top): - -```sh -rm .git/hooks/pre-commit .git/hooks/pre-push -``` - -Only do this for a hook file that carries that sentinel; a hook without it belongs to something else. - ## Mid-Feature Uninstall If you uninstall while a `/ca:feature` or `/ca:sprint` run is in progress, here's what survives and @@ -217,8 +250,8 @@ what to check first. **Does not survive, because it's session/tool state, not repo state:** -- Any in-flight gate context held only in the current Claude Code session (a half-finished TDD Phase, - an open `/ca:commit` gate walk). Nothing was committed to `.codearbiter/` for that step, so there's +- Any in-flight gate context held only in the current host session (a half-finished TDD phase or an + open commit-gate walk). Nothing was committed to `.codearbiter/` for that step, so there is nothing to lose except needing to redo that step by hand or with a different tool. **Before you uninstall mid-feature, check:** @@ -238,7 +271,8 @@ Start a fresh session after the chosen exit: does not report an active arbiter; - for one-host removal, confirm that host no longer lists the plugin and that another installed host can still read the retained `.codearbiter/` state; -- for full removal, confirm the Claude statusline no longer points into the plugin cache and inspect +- for full removal, confirm Claude's optional statusline no longer points into the plugin cache, + Codex no longer lists or trusts the adapter, Pi no longer lists the Git source, and inspect `pre-commit` and `pre-push` before removing only files with the codeArbiter sentinel. The proof is a fresh-session observation plus an intentional decision about retained repository diff --git a/site/src/content/docs/hooks.md b/site/src/content/docs/hooks.md index 350a4ecb..02e9a454 100644 --- a/site/src/content/docs/hooks.md +++ b/site/src/content/docs/hooks.md @@ -1,6 +1,6 @@ --- -title: Hooks Reference -description: "A complete per-hook reference for codeArbiter: every registered hook across the three governance hosts, the gates it enforces, its fail posture, and the non-event scripts behind the commands." +title: Hooks & Host Adapters +description: "The shared Python hook core in detail, plus the Claude Code, Codex, and Pi registration surfaces that carry its decisions." journey: level: "Reference" time: "10 minutes" @@ -30,13 +30,25 @@ The complete `ca-pi` adapter currently carries Feature Forge `preview` status. Its documented matrix is green and real use is welcome, while broader testing continues before stable status or a claim of 100% validation. -codeArbiter enforces its gates as Claude Code hooks under `plugins/ca/hooks/`. Every hook is stdlib-only Python, registered in `hooks.json`, and runs only in an arbiter-enabled repo (see [Enforcement & Security](/enforcement) for the activation contract and the fail-loud posture). A blocking hook exits 2; an advisory hook prints a reminder and exits 0. +The shared guard core lives under `plugins/ca/hooks/`, but the three hosts register it differently. +Claude Code consumes Python hook exits directly. Codex uses an adapter that translates the same +decision into its structured hook result. Pi wraps its native tools and sends normalized events to +the bridge. Every surface checks the same repository activation state; see +[Enforcement & Security](/enforcement/) for the activation contract and fail posture. -Every hook is registered **twice** in `hooks.json`: once under `python3`, and once under a `python3 -c "" || python` fallback. On a stock Windows box that has only the `python` interpreter, the gates still fire. The two entries each receive their own stdin, so a real block is never swallowed by the fallback. +For the exact message text emitted at each shared guard call site, see the generated +[Hook Gates reference](/reference/hooks-gates/). That page is generated from the Python +`block()`/`remind()` calls. The host tables below explain how each host reaches those guards. -For the exact, word-for-word message text a hook prints for every gate ID (generated directly from each `block()`/`remind()` call site, so it can never drift from the narrative summaries below), see the [Hook Gates reference](/reference/hooks-gates/). +## Registered enforcement surfaces -## Registered Hooks +### Claude Code registrations + +`plugins/ca/hooks/hooks.json` registers the shared scripts at Claude Code lifecycle and tool +boundaries. Each logical registration includes a direct `python` command and a compatibility +command that falls back to `python3` when `python` is not a usable Python 3 interpreter. They are +compatibility entries for the same script, not separate gate definitions. Run `/ca:doctor` to prove +the interpreter and live hook path on the machine actually in use. | Event | Matcher | Script | |-------|---------|--------| @@ -49,6 +61,39 @@ For the exact, word-for-word message text a hook prints for every gate ID (gener | `UserPromptSubmit` | (any) | `prune-transcript.py` | | `PreCompact` | (any) | `prune-transcript.py` | +### Codex adapter registrations + +`plugins/ca-codex/hooks/hooks.json` uses OS-specific `command` and `commandWindows` entries. Shell +and write calls enter `pre-tool-adapter.py`, which normalizes the event, calls the shared guard, and +returns a Codex-native deny result when blocked. + +| Event | Matcher | Script | +|---|---|---| +| `SessionStart` | (any) | `session-start.py` | +| `PreToolUse` | `Bash\|shell_command\|exec_command\|unified_exec` | `pre-tool-adapter.py` | +| `PreToolUse` | `apply_patch\|Write\|Edit` | `pre-tool-adapter.py` | +| `PostToolUse` | `apply_patch\|Write\|Edit` | `post-write-edit.py` | +| `UserPromptSubmit` | (any) | `prune-transcript.py` | + +Codex does not register the Claude-only Read injection, `PreCompact`, or statusline surfaces. +The [host evidence page](/getting-started/claude-code-and-codex/) records that boundary. + +### Pi wrapper events + +Pi has no `hooks.json`. Its parent extension installs final wrappers around native and +codeArbiter-owned mutating tools, then uses Pi lifecycle events for the rest: + +| Pi surface | What enters the shared core | +|---|---| +| Wrapped `bash`, `write`, `edit`, and governed custom tools | Normalized `tool_call` before execution | +| `read` and `tool_result` | Advisory context/result handling; reads do not become blocking Claude hooks | +| `session_start` / `before_agent_start` | Trust, activation, bridge readiness, and injected orchestration context | +| `session_before_compact` / `session_compact` | Native compaction validation and governance-state preservation | +| `session_shutdown` | Cleanup, background-job verification, and native-footer restoration | + +Pi registers repository-aware dispatch only in an affirmatively trusted, enabled project. Hardened +children do not receive the parent-only footer, background-job, or nested-dispatch surfaces. + --- ### session-start.py @@ -82,7 +127,11 @@ For the exact, word-for-word message text a hook prints for every gate ID (gener - **H-11:** ADRs only via `/ca:adr`. Shell redirects/verbs into `.codearbiter/decisions/` are blocked; reads pass. - **H-14:** migration review. A commit staging a migration is blocked unless `migration-gate-passed` covers that file's content digest. - **H-18:** the activation switch is protected. A shell write that would flip `.codearbiter/CONTEXT.md` off (`arbiter: disabled` or broken frontmatter) is blocked, so the gates cannot be silenced from inside the repo they govern. - - **H-19:** gate-pass markers are unforgeable by hand. Shell redirects/verbs naming `security-gate-passed`/`migration-gate-passed` are blocked; only the sanctioned recorder scripts may write them. + - **H-19:** gate-pass markers are cooperative attestation on the governed path. Common shell, + Write, Edit, and patch forge attempts naming `security-gate-passed` or + `migration-gate-passed` are blocked, while sanctioned recorder scripts bind a pass to content + digests. This adds friction and an audit record; it does not make a public digest unforgeable to + a determined same-user process. See the [marker trust boundary](/codearbiter-directory/#markers). - **H-20:** no `--no-verify` bypass. A literal `--no-verify`/`-n` on `git commit` (including bundled and attached-value short-flag clusters like `-nm`, mirroring git's own parsing) and a literal `--no-verify` on `git push` are blocked, because that flag skips the `.git/hooks` git-enforce backstop. - **Why:** This is the load-bearing commit-time gate. The branch and force-push rules keep the default branch PR-only; the crypto/secret/migration gates keep dangerous content out of the committed artifact. - **Fail posture:** Blocking (exit 2). Ambiguity resolves **closed**: a spelling indistinguishable from a destructive one is blocked. H-09b/H-10b and H-14 fail **closed** when git cannot read the diff or file list (a `None` sentinel, distinct from an empty diff), and a crash inside the guard itself blocks rather than allows (H-00). `/ca:override` is the sanctioned escape hatch. diff --git a/site/src/content/docs/index.mdx b/site/src/content/docs/index.mdx index 914bfa04..1d17186c 100644 --- a/site/src/content/docs/index.mdx +++ b/site/src/content/docs/index.mdx @@ -13,9 +13,9 @@ import TrustRow from '../../components/TrustRow.astro'; import ArbiterIcon from '../../components/ArbiterIcon.astro'; import heroArt from '../../assets/hero-gates.webp'; -
    +
    -
    +

    Governed autonomy for serious repositories

    Hard gates for agentic coding.

    @@ -79,7 +79,7 @@ import heroArt from '../../assets/hero-gates.webp';

    Stops at the tool boundary

    -

    Host hooks run before shell and write calls. A blocking verdict prevents the call; it does not depend on the model remembering a prompt.

    +

    Host enforcement adapters run before shell and write calls. A blocking verdict prevents the call; it does not depend on the model remembering a prompt.

    Inspect enforcement
    diff --git a/site/src/content/docs/learn/index.mdx b/site/src/content/docs/learn/index.mdx index 6a4038db..806c47bd 100644 --- a/site/src/content/docs/learn/index.mdx +++ b/site/src/content/docs/learn/index.mdx @@ -3,7 +3,7 @@ title: The codeArbiter Learning Path description: "A progressive, hands-on route from first install to confident operation, diagnosis, customization, audit, and release." journey: level: Foundation - time: 60–90 min total + time: 3–4 hours total outcome: "a working repository, a mental model of every major lane, and a capstone checklist that proves you can operate codeArbiter without guesswork." prerequisites: - Git installed @@ -13,7 +13,7 @@ journey: import ArbiterIcon from '../../../components/ArbiterIcon.astro'; -export const base = `${import.meta.env.BASE_URL}/`; +export const base = `${import.meta.env.BASE_URL.replace(/\/$/, "")}/`; You do not need to understand codeArbiter’s internals before using it. Follow this path in order, perform the short exercise in each stage, and keep the proof it asks you to produce. The reference @@ -124,7 +124,7 @@ Use a disposable repository. Do not perform this exercise in active production w
  • Use status to identify the stage, current branch, one task, and one open question.
  • Explain which lane owns a new feature, a confirmed bug, a risky dependency, and a structural refactor.
  • Locate the hook-gate entry for H-03 and the source file that emitted your block.
  • -
  • Find the configuration reference for migration, CI, and deployment scope.
  • +
  • Find the security controls and commit-gate references that govern migration, CI, and deployment scope.
  • Describe what an override records and what authority it does not grant.
  • Trace one sprint decision from the log to its SMARTS evidence and confidence flag.
  • Explain why a green local test is not proof that a pull request is safe to merge.
  • diff --git a/site/src/content/docs/overview.md b/site/src/content/docs/overview.md index 4c6abf52..da4fb6df 100644 --- a/site/src/content/docs/overview.md +++ b/site/src/content/docs/overview.md @@ -35,9 +35,11 @@ your behalf. command surface differs in spelling, not ownership or policy. 2. **Route.** The orchestrator hands the command to the workflow that owns that lane. `/ca:fix` and `$ca-fix` both reach the same test-first obligations. -3. **Dispatch.** The owning skill calls the agents the change actually demands: an author - to write code test-first, plus a reviewer fleet matched to the diff (security, - auth/crypto, dependencies, migrations, coverage). +3. **Execute the roles.** The owning skill selects the author and reviewer roles the change actually + demands. Claude Code dispatches plugin agents. Current Codex releases load the same charters into + host-provided agent threads and retain their thread receipts; older hosts may use an inline + fallback unless the workflow requires isolation. Pi launches hardened child processes through + its trusted parent. The policy stays shared even though each host's mechanism differs. 4. **Gate.** Nothing advances until its gates are green. A failing test, a CRITICAL security finding, an unresolved decision: each is a real stop. 5. **Ship.** Code reaches version control only through the commit gate, and the default diff --git a/site/src/curated/commands/checkpoint.md b/site/src/curated/commands/checkpoint.md index b4bc272b..c06db0d6 100644 --- a/site/src/curated/commands/checkpoint.md +++ b/site/src/curated/commands/checkpoint.md @@ -17,8 +17,9 @@ re-zeros the `over:N` overrides-since-checkpoint counter the statusline shows, b current `overrides.log` line count to `.codearbiter/last-checkpoint`. This is a report, not a promotion gate — it surfaces findings and enforces no sign-off. -On Codex, the reviewer fleet runs inline in the current thread rather than through isolated -`Task`-tool dispatch — see [Claude Code + Codex → Intentional host +On current Codex hosts, codeArbiter loads each reviewer charter into a host-provided agent thread and +retains its thread receipt. An older host may run a review role inline rather than skip it — see +[Claude Code + Codex → Intentional host differences](/getting-started/claude-code-and-codex/#intentional-host-differences). ## Usage diff --git a/site/src/curated/commands/pr.md b/site/src/curated/commands/pr.md index 96ac16d5..d45ec1a8 100644 --- a/site/src/curated/commands/pr.md +++ b/site/src/curated/commands/pr.md @@ -25,8 +25,9 @@ draft cold until it's addressed. Once everything clears, the PR itself gets writ summary, a test plan, and links to any decision record it touches — then, if you've opted into CI babysitting, a watcher attaches to follow the checks through to green. -On Codex, the dispatched reviewers execute inline in the current thread rather than through -isolated `Task`-tool dispatch — see [Claude Code + Codex → Intentional host +On current Codex hosts, codeArbiter loads each reviewer charter into a host-provided agent thread and +retains its thread receipt. An older host may run a review role inline rather than skip it — see +[Claude Code + Codex → Intentional host differences](/getting-started/claude-code-and-codex/#intentional-host-differences). ## Usage diff --git a/site/src/curated/commands/review.md b/site/src/curated/commands/review.md index 27624f78..95d59390 100644 --- a/site/src/curated/commands/review.md +++ b/site/src/curated/commands/review.md @@ -24,8 +24,9 @@ Findings are surfaced by severity (CRITICAL/HIGH/MEDIUM/LOW), file:line, remedia security findings — the specific control in `.codearbiter/security-controls.md` they map to. Raw per-reviewer output is never consumed directly; only the triaged, aggregated verdict is. -On Codex, these reviewer roles execute inline in the current thread rather than through isolated -`Task`-tool dispatch — see [Claude Code + Codex → Intentional host +On current Codex hosts, codeArbiter loads each reviewer charter into a host-provided agent thread and +retains its thread receipt. An older host may run a review role inline rather than skip it — see +[Claude Code + Codex → Intentional host differences](/getting-started/claude-code-and-codex/#intentional-host-differences). ## Usage diff --git a/site/src/curated/commands/tribunal.md b/site/src/curated/commands/tribunal.md index a3e3cd29..fe0c7acc 100644 --- a/site/src/curated/commands/tribunal.md +++ b/site/src/curated/commands/tribunal.md @@ -42,8 +42,9 @@ On a large repo, two optional read-only mappers ([`map-structure`](/reference/ag and [`map-deps`](/reference/agents/map-deps/)) build the codebase inventory ahead of the lenses, so that mapping work stays off the orchestrator's own context. -On Codex, every lens runs inline in the current thread rather than through isolated `Task`-tool -dispatch — see [Claude Code + Codex → Intentional host +On current Codex hosts, codeArbiter loads each lens charter into a host-provided agent thread and +retains its thread receipt. An older host may run a lens inline rather than silently omit it — see +[Claude Code + Codex → Intentional host differences](/getting-started/claude-code-and-codex/#intentional-host-differences). A triage pass independently recalibrates every lens's own severity call rather than trusting it diff --git a/site/src/styles/callouts.css b/site/src/styles/callouts.css index fde7b63e..e8203eaf 100644 --- a/site/src/styles/callouts.css +++ b/site/src/styles/callouts.css @@ -20,16 +20,11 @@ background: color-mix(in srgb, var(--ca-callout-preview-border) 14%, transparent); } -/* Diagrams embedded mid-prose get room to breathe and never overflow the column. */ +/* Diagrams embedded mid-prose get room to breathe. The shared theme owns media + sizing so labeled diagrams may remain intrinsically legible on phones. */ .sl-markdown-content .ca-diagram { margin-block: 1.75rem; } -.ca-diagram img, -.ca-diagram svg { - display: block; - width: 100%; - height: auto; -} .ca-diagram figcaption { margin-top: 0.6rem; text-align: center; diff --git a/site/src/styles/design-system.css b/site/src/styles/design-system.css index cf417d05..0f65aff5 100644 --- a/site/src/styles/design-system.css +++ b/site/src/styles/design-system.css @@ -123,10 +123,24 @@ header.header { } .site-title img { - width: 10rem; + width: 11.75rem; height: auto; } +@media (max-width: 34rem) { + .site-title { + width: 2.3rem; + flex: 0 0 2.3rem; + overflow: hidden; + } + + .site-title img { + width: 11.75rem; + max-width: none; + height: 2.3rem; + } +} + .sidebar-pane { border-inline-end-color: rgb(58 76 96 / 55%); background: @@ -178,24 +192,70 @@ header.header { text-underline-offset: 0.2em; } +.ca-table-shell { + max-width: 100%; + margin-block: var(--ca-space-5); + overflow-x: auto; + overscroll-behavior-inline: contain; + border: 1px solid var(--ca-line); + border-radius: var(--ca-radius-lg); + background: + linear-gradient(180deg, rgb(240 185 47 / 3%), transparent 8rem), + var(--ca-bg-raised); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 3%), + 0 18px 44px -38px rgb(0 0 0 / 95%); + scrollbar-color: var(--ca-line-strong) transparent; + scrollbar-width: thin; +} + .sl-markdown-content table { display: table; width: 100%; - border: 1px solid var(--ca-line); - border-radius: var(--ca-radius-md); - overflow: hidden; + min-width: 34rem; + margin: 0; + overflow: visible; + border: 0; + border-collapse: collapse; + border-radius: 0; font-size: var(--ca-text-sm); } +.sl-markdown-content :is(th, td) { + padding: 0.78rem 0.9rem; + border-block-end: 1px solid rgb(58 76 96 / 52%); + vertical-align: top; +} + .sl-markdown-content th { - color: var(--ca-ink); - background: var(--ca-bg-soft); + color: var(--ca-brand-bright); + background: rgb(17 24 33 / 94%); + font: 700 0.68rem/1.35 var(--ca-font-mono); + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.sl-markdown-content tbody tr { + transition: background-color 140ms ease; } -.sl-markdown-content tr:nth-child(2n) { +.sl-markdown-content tbody tr:nth-child(2n) { background: rgb(255 255 255 / 1.5%); } +.sl-markdown-content tbody tr:hover { + background: rgb(240 185 47 / 4.5%); +} + +.sl-markdown-content tr:last-child > :is(th, td) { + border-block-end: 0; +} + +.sl-markdown-content td:first-child { + color: var(--ca-ink); + font-weight: 560; +} + details.ca-source { max-width: 100%; overflow: hidden; @@ -736,11 +796,9 @@ details.ca-source pre { line-height: 1.55; } -.sl-markdown-content table { - display: block; - max-width: 100%; - overflow-x: auto; - overscroll-behavior-inline: contain; +.sl-markdown-content > .ca-reference-lead + h2, +.sl-markdown-content > .ca-reference-lead + ul + h2 { + margin-block-start: var(--ca-space-6); } @media (max-width: 42rem) { @@ -762,14 +820,31 @@ details.ca-source pre { .ca-reference-lead { grid-template-columns: minmax(0, 1fr); + gap: var(--ca-space-3); + padding: var(--ca-space-4); + } + + .ca-reference-lead__kind { + margin-block-end: var(--ca-space-2); + } + + .ca-reference-lead__summary { + font-size: var(--ca-text-md); + line-height: 1.5; } .ca-reference-lead__usage { - padding-block-start: var(--ca-space-4); + padding-block-start: var(--ca-space-3); padding-inline-start: 0; border-block-start: 1px solid var(--ca-line); border-inline-start: 0; } + + .ca-reference-lead__usage p { + max-width: none; + font-size: var(--ca-text-xs); + line-height: 1.45; + } } @media (prefers-reduced-motion: reduce) { diff --git a/site/src/styles/landing.css b/site/src/styles/landing.css index 1b38182b..b799505c 100644 --- a/site/src/styles/landing.css +++ b/site/src/styles/landing.css @@ -24,11 +24,27 @@ .ca-landing { --ca-landing-width: 76rem; + position: relative; + isolation: isolate; width: 100%; overflow: clip; } +.ca-landing::before { + position: fixed; + z-index: 0; + inset: 4rem 0 0; + background: + linear-gradient(90deg, rgb(9 13 18 / 96%) 0%, rgb(9 13 18 / 66%) 45%, rgb(9 13 18 / 82%) 100%), + linear-gradient(180deg, rgb(9 13 18 / 18%) 0%, rgb(9 13 18 / 72%) 52%, rgb(9 13 18 / 94%) 100%), + var(--ca-hero-art) center top / cover no-repeat; + pointer-events: none; + content: ""; +} + .ca-landing section { + position: relative; + z-index: 1; scroll-margin-top: 5rem; } @@ -48,20 +64,6 @@ min-width: 0; } -.ca-splash::before { - position: absolute; - z-index: -1; - inset: 0 -25vw; - background: - linear-gradient(90deg, var(--ca-bg) 0%, rgb(9 13 18 / 92%) 35%, rgb(9 13 18 / 50%) 66%, var(--ca-bg) 100%), - linear-gradient(180deg, rgb(9 13 18 / 8%) 58%, var(--ca-bg) 100%), - var(--ca-hero-art) center / cover no-repeat; - opacity: 0.62; - mask-image: linear-gradient(180deg, #000 0%, #000 75%, transparent 100%); - pointer-events: none; - content: ""; -} - .ca-splash__copy h1 { max-width: 9ch; margin: var(--ca-space-4) 0 var(--ca-space-5); diff --git a/site/src/styles/theme.css b/site/src/styles/theme.css index bc3dbd14..91a3515e 100644 --- a/site/src/styles/theme.css +++ b/site/src/styles/theme.css @@ -1,7 +1,7 @@ /* codeArbiter docs — brand theme. - Palette lifted from docs/banner.svg: "gate gold" (#e3b341 / #d29922) on a - deep slate (#0b0f14). The accent variables retint Starlight site-wide; the - rest is scoped to the splash landing page. + Gate gold (#e3b341 / #d29922) on deep slate (#0b0f14) anchors the shared + rendered-art and interface system. Accent variables retint Starlight + site-wide; landing-specific composition stays in landing.css. DESIGN SYSTEM CONTRACT — downstream authors build on these tokens and classes; never edit this file for per-page content. @@ -315,6 +315,79 @@ letter-spacing: 0.04em; } +@media (max-width: 48rem) { + /* Labeled process diagrams are reference material, not thumbnails. Keep a + readable drawing scale and let the bordered figure provide the scroll + viewport instead of shrinking labels into illegibility. */ + .ca-diagram { + align-items: flex-start; + overscroll-behavior-inline: contain; + scrollbar-color: var(--ca-gold-dim) transparent; + } + + .ca-diagram > svg, + .ca-diagram > img { + width: 42rem; + min-width: 42rem; + max-width: none; + } + + .ca-diagram > .ca-statusline-map { + min-width: 54rem; + } + + .ca-diagram figcaption { + position: sticky; + inset-inline-start: 0; + width: min(100%, calc(100vw - 4.5rem)); + text-align: start; + } +} + +/* Decorative rendered art establishes a section's emotional register; it never + substitutes for the precise diagrams that explain an operating contract. */ +.ca-art-banner { + position: relative; + margin-block: 1.75rem 2.25rem; + border: 1px solid color-mix(in srgb, var(--ca-gold) 24%, var(--ca-border)); + border-radius: 0.8rem; + background: var(--ca-slate); + box-shadow: + 0 1.5rem 3rem rgb(0 0 0 / 24%), + inset 0 0 0 1px rgb(255 255 255 / 2%); + overflow: hidden; + isolation: isolate; +} + +.ca-art-banner::after { + position: absolute; + inset: 0; + z-index: 1; + background: linear-gradient(180deg, transparent 45%, rgb(4 8 12 / 82%) 100%); + content: ""; + pointer-events: none; +} + +.ca-art-banner > img { + display: block; + width: 100%; + aspect-ratio: 3 / 1; + object-fit: cover; +} + +.ca-art-banner > figcaption { + position: absolute; + inset-inline: clamp(1rem, 3vw, 1.5rem); + inset-block-end: clamp(0.85rem, 2vw, 1.25rem); + z-index: 2; + max-width: 38rem; + margin: 0; + color: var(--ca-text); + font: 600 clamp(0.78rem, 1.35vw, 0.92rem) / 1.45 var(--ca-font-sans); + letter-spacing: 0.01em; + text-shadow: 0 1px 1rem rgb(0 0 0 / 90%); +} + /* The statusline guide annotates the real PNG without modifying or redrawing it. */ .ca-statusline-map { position: relative; diff --git a/site/test/astro-config.test.ts b/site/test/astro-config.test.ts index 90585d79..ca023022 100644 --- a/site/test/astro-config.test.ts +++ b/site/test/astro-config.test.ts @@ -29,11 +29,11 @@ describe("astro.config markdown wiring", () => { expect(config.markdown?.remarkRehype).toBeUndefined(); }); - it("wires the base-path plugin onto an explicit unified processor", () => { + it("wires both local rehype plugins onto an explicit unified processor", () => { const processor = config.markdown?.processor; expect(processor).toBeDefined(); expect(isUnifiedProcessor(processor)).toBe(true); - expect(processor.options.rehypePlugins).toHaveLength(1); + expect(processor.options.rehypePlugins).toHaveLength(2); }); it("base-prefixes a root-absolute markdown link through the configured processor", async () => { @@ -50,4 +50,12 @@ describe("astro.config markdown wiring", () => { expect(code).toContain(`href="https://github.com/arbiterForge/codeArbiter"`); }); + + it("wraps rendered markdown tables through the configured processor", async () => { + const renderer = await config.markdown.processor.createRenderer({}); + const { code } = await renderer.render("| Reviewer | Checks |\n|---|---|\n| security | gates |"); + + expect(code).toContain('
    '); + expect(code).toContain(""); + }); }); diff --git a/site/test/content/documentation-presentation.test.ts b/site/test/content/documentation-presentation.test.ts new file mode 100644 index 00000000..cbc6162a --- /dev/null +++ b/site/test/content/documentation-presentation.test.ts @@ -0,0 +1,234 @@ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; + +const siteRoot = process.cwd(); +const docsRoot = join(siteRoot, "src", "content", "docs"); +const styles = readFileSync(join(siteRoot, "src", "styles", "design-system.css"), "utf8"); +const themeStyles = readFileSync(join(siteRoot, "src", "styles", "theme.css"), "utf8"); +const calloutStyles = readFileSync(join(siteRoot, "src", "styles", "callouts.css"), "utf8"); +const config = readFileSync(join(siteRoot, "astro.config.mjs"), "utf8"); + +function readIfPresent(path: string): string { + return existsSync(path) ? readFileSync(path, "utf8") : ""; +} + +function finalTableDisplay(source: string): string | undefined { + const rules = [...source.matchAll(/\.sl-markdown-content table\s*\{([^}]+)\}/g)]; + return rules + .flatMap((rule) => [...rule[1].matchAll(/display:\s*([^;]+);/g)]) + .at(-1)?.[1] + .trim(); +} + +describe("documentation presentation regressions", () => { + it("OBL-PRES-01 renders the concept index as navigation rather than a raw table", () => { + const markdownPath = existsSync(join(docsRoot, "concepts.mdx")) + ? join(docsRoot, "concepts.mdx") + : join(docsRoot, "concepts.md"); + const source = readFileSync(markdownPath, "utf8"); + const component = readIfPresent(join(siteRoot, "src", "components", "ConceptNavigator.astro")); + + expect(source).toContain(" { + expect(finalTableDisplay(styles)).toBe("table"); + expect(styles).toContain(".ca-table-shell"); + expect(styles).toMatch(/\.ca-table-shell\s*\{[^}]*overflow-x:\s*auto;/s); + expect(config).toContain("rehypeTableShell"); + }); + + it("OBL-PRES-03 preserves Checkpoints as a real reviewer data grid", () => { + const checkpoints = readFileSync(join(docsRoot, "concepts", "checkpoints.md"), "utf8"); + const fleetTable = checkpoints.match(/\| Reviewer \| Checks \|[\s\S]+?(?=\n\n## The funnel)/)?.[0] ?? ""; + + expect(fleetTable).toContain("| Reviewer | Checks |"); + expect(fleetTable.match(/reference\/agents\//g)).toHaveLength(6); + }); + + it("OBL-PRES-04 gives Start a trailhead icon instead of a pause-shaped glyph", () => { + const icons = readFileSync(join(siteRoot, "src", "components", "ArbiterIcon.astro"), "utf8"); + + expect(icons).not.toContain("M4 18.5V5.5h4.5v13H4"); + expect(icons).toContain("M6 21V4.5"); + }); + + it("OBL-PRES-05 uses a crisp, ownable header lockup instead of a glow-filtered utility mark", () => { + const logo = readFileSync(join(siteRoot, "src", "assets", "logo.svg"), "utf8"); + + expect(logo).toContain('viewBox="0 0 188 36"'); + expect(logo).toContain('id="mark-gold"'); + expect(logo).not.toContain(" { + const learningPath = readFileSync(join(docsRoot, "learn", "index.mdx"), "utf8"); + + expect(learningPath).toContain('import.meta.env.BASE_URL.replace(/\\/$/, "")'); + expect(learningPath).not.toContain("`${import.meta.env.BASE_URL}/`"); + }); + + it("OBL-PRES-07 uses rendered art for Feature Forge atmosphere and retains SVG for its model", () => { + const forge = readFileSync(join(docsRoot, "feature-forge", "overview.md"), "utf8"); + const artwork = join(siteRoot, "public", "art", "feature-forge.webp"); + + expect(forge).toContain('class="ca-art-banner'); + expect(forge).toContain('/codeArbiter/art/feature-forge.webp'); + expect(forge).toContain('/codeArbiter/diagrams/two-axis-model.svg'); + expect(existsSync(artwork)).toBe(true); + expect(themeStyles).toContain(".ca-art-banner"); + }); + + it("OBL-PRES-08 gives the landing page a real skip-link target", () => { + const landing = readFileSync(join(docsRoot, "index.mdx"), "utf8"); + + expect(landing).toMatch(/id=["']_top["']/); + expect(landing).toMatch(/tabindex=["']-1["']/); + }); + + it("OBL-PRES-09 keeps labeled diagrams readable through horizontal exploration on phones", () => { + expect(themeStyles).toMatch( + /@media\s*\(max-width:\s*48rem\)[\s\S]*?\.ca-diagram\s*>\s*(?:svg|img)[\s\S]*?max-width:\s*none;/, + ); + expect(themeStyles).toMatch( + /@media\s*\(max-width:\s*48rem\)[\s\S]*?\.ca-statusline-map\s*\{[^}]*min-width:/, + ); + expect(calloutStyles).not.toMatch( + /\.ca-diagram\s+(?:img|svg)[\s\S]*?width:\s*100%;/, + ); + }); + + it("OBL-PRES-10 labels lookup, recovery, and walkthrough page orientation honestly", () => { + const pageContext = readFileSync( + join(siteRoot, "src", "components", "PageContext.astro"), + "utf8", + ); + + expect(pageContext).toContain('time === "Lookup"'); + expect(pageContext).toContain('"Reference lookup"'); + expect(pageContext).toContain('time === "Symptom-driven"'); + expect(pageContext).toContain("Read / walkthrough / ${time}"); + }); + + it("OBL-CONTENT-01 states the honest learning-path duration and governing scope", () => { + const learningPath = readFileSync(join(docsRoot, "learn", "index.mdx"), "utf8"); + + expect(learningPath).toContain("3–4 hours total"); + expect(learningPath).not.toContain( + "configuration reference for migration, CI, and deployment scope", + ); + expect(learningPath).toContain("security controls"); + }); + + it("OBL-CONTENT-02 gives Pi exact supported hosts and a mechanical tag lookup", () => { + const pi = readFileSync(join(docsRoot, "getting-started", "pi.md"), "utf8"); + + expect(pi).toContain("Pi 0.80.5 or Pi 0.80.10"); + expect(pi).not.toContain("Pi 0.80.10 or newer"); + expect(pi).toContain("git ls-remote --tags --refs"); + expect(pi).toContain('"ca-pi-v*"'); + }); + + it("OBL-CONTENT-03 documents hook registration and marker trust honestly across hosts", () => { + const hooks = readFileSync(join(docsRoot, "hooks.md"), "utf8"); + + expect(hooks).not.toContain("Every hook is registered **twice**"); + expect(hooks).not.toContain("unforgeable by hand"); + expect(hooks).toContain("Claude Code registrations"); + expect(hooks).toContain("Codex adapter registrations"); + expect(hooks).toContain("Pi wrapper events"); + expect(hooks).toContain("cooperative attestation"); + }); + + it("OBL-CONTENT-04 defines every terminology-locked term and the H-18 exception", () => { + const glossary = readFileSync(join(docsRoot, "glossary.md"), "utf8"); + + for (const term of ["Agent", "Command", "Layer", "Phase", "Severity", "Skill"]) { + expect(glossary).toMatch(new RegExp(`^## ${term}$`, "m")); + } + expect(glossary).toContain("H-18"); + expect(glossary).toContain("project-maturity"); + expect(glossary).not.toContain("It scales how strict a gate behaves"); + }); + + it("OBL-CONTENT-05 keeps uninstall and sandbox setup actionable on every host", () => { + const uninstall = readFileSync(join(docsRoot, "guides", "uninstalling.md"), "utf8"); + const sandbox = readFileSync(join(docsRoot, "guides", "ca-sandbox.md"), "utf8"); + + expect(uninstall).toContain("Claude Code, Codex, and Pi"); + expect(uninstall).toContain("git ls-remote --tags --refs"); + expect(uninstall).toContain("Claude Code removes"); + expect(uninstall).toContain("Codex removes"); + expect(uninstall).toContain("Pi removes"); + expect(uninstall).not.toContain("This removes the plugin payload"); + expect(uninstall).toContain("runs **every live registered enforcer**"); + expect(uninstall).toContain("removes only that host's registry"); + expect(uninstall).toContain("deliberately leaves the shared shims"); + expect(uninstall).toContain("git rev-parse --git-common-dir"); + expect(uninstall).toContain("git config --get core.hooksPath"); + expect(uninstall.indexOf("### 1. Remove the Claude Code Statusline")).toBeLessThan( + uninstall.indexOf("### 3. Uninstall the Host Packages"), + ); + expect(uninstall.indexOf("### 2. Remove Each Host's Git-Backstop Registration")).toBeLessThan( + uninstall.indexOf("### 3. Uninstall the Host Packages"), + ); + expect(sandbox).toContain("/plugin marketplace add arbiterForge/codeArbiter"); + }); + + it("OBL-CONTENT-06 avoids duplicate narration for decorative Feature Forge art", () => { + const forge = readFileSync(join(docsRoot, "feature-forge", "overview.md"), "utf8"); + + expect(forge).toMatch(/feature-forge\.webp"\s+alt=""/); + }); + + it("OBL-CONTENT-07 presents repository opt-in as a three-host contract", () => { + const optIn = readFileSync(join(docsRoot, "guides", "opt-in-a-repo.md"), "utf8"); + + expect(optIn).toMatch(/shared by Claude Code, Codex, and Pi/); + expect(optIn).toContain("/ca-init"); + }); + + it("OBL-CONTENT-08 explains host-specific role execution without overgeneralizing dispatch", () => { + const overview = readFileSync(join(docsRoot, "overview.md"), "utf8"); + + expect(overview).not.toContain("The owning skill calls the agents"); + expect(overview).toContain("Claude Code dispatches plugin agents"); + expect(overview).toContain("host-provided agent threads"); + expect(overview).toContain("inline"); + expect(overview).toMatch(/Pi launches\s+hardened child processes/); + }); + + it("OBL-CONTENT-09 labels platform-aggregate setup as maintainer-only verification", () => { + const compatibility = readFileSync( + join(docsRoot, "getting-started", "compatibility.md"), + "utf8", + ); + + expect(compatibility).toContain("Maintainer verification only"); + expect(compatibility).toContain("npm --prefix plugins/ca-pi/tools ci --ignore-scripts"); + }); + + it("OBL-REF-01 suppresses inferred journey cards on generated entity pages", () => { + const pageTitle = readFileSync(join(siteRoot, "src", "components", "PageTitle.astro"), "utf8"); + + expect(pageTitle).toContain("isGeneratedEntity"); + expect(pageTitle).toMatch(/!isGeneratedEntity\s*&&\s* { + expect(styles).toMatch( + /@media \(max-width: 42rem\)[\s\S]*?\.ca-reference-lead\s*\{[^}]*padding:\s*var\(--ca-space-4\);/, + ); + expect(styles).toMatch( + /@media \(max-width: 42rem\)[\s\S]*?\.ca-reference-lead__usage p\s*\{[^}]*max-width:\s*none;/, + ); + expect(styles).toContain(".ca-reference-lead + ul + h2"); + }); +}); diff --git a/site/test/content/readme-professional.test.ts b/site/test/content/readme-professional.test.ts new file mode 100644 index 00000000..5425369b --- /dev/null +++ b/site/test/content/readme-professional.test.ts @@ -0,0 +1,50 @@ +import { existsSync, readFileSync, statSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +const repoRoot = resolve(process.cwd(), ".."); +const readme = readFileSync(join(repoRoot, "README.md"), "utf8"); + +describe("professional repository README", () => { + it("leads with the rendered product artwork instead of the legacy SVG banner", () => { + const hero = join(repoRoot, "docs", "readme-hero.webp"); + + expect(readme).toContain('src="docs/readme-hero.webp"'); + expect(readme).not.toContain('src="docs/banner.svg"'); + expect(existsSync(hero)).toBe(true); + expect(statSync(hero).size).toBeGreaterThan(50_000); + }); + + it("routes a newcomer into the same novice-to-power-user journey as the site", () => { + for (const href of [ + "getting-started/choose-your-host/", + "getting-started/install/", + "getting-started/quickstart/", + "learn/", + "concepts/", + "reference/", + ]) { + expect(readme).toContain(href); + } + + expect(readme.indexOf("## Get running")).toBeLessThan(readme.indexOf("## How governance works")); + }); + + it("uses rendered Feature Forge atmosphere while linking the source-backed live catalog", () => { + expect(readme).toContain('src="docs/feature-forge.webp"'); + expect(readme).not.toContain('src="docs/feature-forge.svg"'); + expect(readme).toContain("feature-forge/whats-in-the-forge/"); + }); + + it("keeps detailed catalogs collapsible so the README remains an adoption surface", () => { + expect(readme).toContain("All 40 Claude Code commands"); + expect(readme).toContain("The docs are the operating manual"); + }); + + it("keeps Pi tag selection and override authority honest at the repository entry point", () => { + expect(readme).toContain("git ls-remote --tags --refs"); + expect(readme).toContain('"ca-pi-v*"'); + expect(readme).toContain("H-18"); + expect(readme).not.toContain("bypass of one gate or hard rule"); + }); +}); diff --git a/site/test/generator/feature-forge-content.test.ts b/site/test/generator/feature-forge-content.test.ts index 52036928..55c44861 100644 --- a/site/test/generator/feature-forge-content.test.ts +++ b/site/test/generator/feature-forge-content.test.ts @@ -28,7 +28,7 @@ const FORGE = join(DOCS, "feature-forge"); const overview = readFileSync(join(FORGE, "overview.md"), "utf8"); const whatsIn = readFileSync(join(FORGE, "whats-in-the-forge.mdx"), "utf8"); const usingPreview = readFileSync(join(FORGE, "using-preview-features.md"), "utf8"); -const concepts = readFileSync(join(DOCS, "concepts.md"), "utf8"); +const concepts = readFileSync(join(DOCS, "concepts.mdx"), "utf8"); describe("Feature Forge section (its own top-level group)", () => { it("overview is titled 'What Is the Feature Forge'", () => { diff --git a/site/test/generator/generate.test.ts b/site/test/generator/generate.test.ts index 2e7c80e4..73136ff9 100644 --- a/site/test/generator/generate.test.ts +++ b/site/test/generator/generate.test.ts @@ -196,6 +196,15 @@ describe("generate", () => { expect(indexContent).toContain("agents are not a second command surface"); }); + it("distinguishes generated source facts from hand-reviewed operating guidance", () => { + generate(pluginDir, outDir); + const indexContent = readFileSync(join(outDir, "index.md"), "utf8"); + + expect(indexContent).not.toContain("it can never drift from the source"); + expect(indexContent).toContain("Curated operating guidance is hand-reviewed"); + expect(indexContent).toContain("exact source embed is authoritative"); + }); + it("renders one markdown table per non-empty collection, headers matching the collection", () => { generate(pluginDir, outDir); const indexContent = readFileSync(join(outDir, "index.md"), "utf8"); diff --git a/site/test/generator/render-agent-page.test.ts b/site/test/generator/render-agent-page.test.ts index c94bdbd8..f7d3c70a 100644 --- a/site/test/generator/render-agent-page.test.ts +++ b/site/test/generator/render-agent-page.test.ts @@ -29,6 +29,57 @@ describe("renderAgentPage", () => { expect(md).toContain("`Read`, `Grep`"); }); + it("explains direct-page dispatch semantics for Claude Code, Codex, and Pi", () => { + const md = renderAgentPage( + input({ + name: "myagent", + description: "reviews a thing", + model: "sonnet", + tools: "Read, Grep", + }), + ); + + expect(md).toContain("Claude Code"); + expect(md).toContain("Codex"); + expect(md).toContain("Pi"); + expect(md).toContain("host-provided agent thread"); + expect(md).toContain("older-host fallback"); + expect(md).toContain("codearbiter_dispatch"); + }); + + it("translates Claude-only path variables in reader-facing summaries but keeps exact source", () => { + const md = renderAgentPage( + input({ + name: "myagent", + description: "Reads ${CLAUDE_PROJECT_DIR}/.codearbiter/tech-stack.md.", + tools: "Read", + }), + ); + const beforeSource = md.slice(0, md.indexOf("## Source")); + const source = md.slice(md.indexOf("## Source")); + + expect(beforeSource).toContain("the repository's .codearbiter/tech-stack.md"); + expect(beforeSource).not.toContain("${CLAUDE_PROJECT_DIR}"); + expect(source).toContain("Body."); + }); + + it("keeps the orientation lead concise while retaining the full metadata description", () => { + const md = renderAgentPage( + input({ + name: "myagent", + description: "Owns the architecture lens. Reviews structural drift. Writes one finding file.", + tools: "Read", + }), + ); + const lead = md.match(/
    \n<\/div>/)?.[0] ?? ""; + + expect(md).toContain( + 'description: "Owns the architecture lens. Reviews structural drift. Writes one finding file."', + ); + expect(lead).toContain("Owns the architecture lens. Reviews structural drift."); + expect(lead).not.toContain("Writes one finding file."); + }); + it("shows the default tier when model is missing", () => { const md = renderAgentPage( input({ name: "nomodel", description: "no model here", tools: "Read" }), diff --git a/site/test/generator/render-changelog.test.ts b/site/test/generator/render-changelog.test.ts index b4c91706..8308f58c 100644 --- a/site/test/generator/render-changelog.test.ts +++ b/site/test/generator/render-changelog.test.ts @@ -43,9 +43,15 @@ describe("renderChangelog", () => { expect(out).toContain("Cut SessionStart blocking work (#194)."); }); - it("links the GitHub releases page", () => { + it("labels the timeline as the core ca plugin and links every sibling changelog", () => { const out = renderChangelog(SOURCE); + + expect(out).toContain("core `ca` plugin"); expect(out).toContain("https://github.com/arbiterForge/codeArbiter/releases"); + expect(out).toContain("plugins/ca-codex/CHANGELOG.md"); + expect(out).toContain("plugins/ca-pi/CHANGELOG.md"); + expect(out).toContain("plugins/ca-sandbox/CHANGELOG.md"); + expect(out).not.toContain("Every released version of codeArbiter"); }); it("is idempotent: rendering the same source twice yields byte-identical output", () => { diff --git a/site/test/landing/landing-page.test.ts b/site/test/landing/landing-page.test.ts index 8d22a669..f146cabd 100644 --- a/site/test/landing/landing-page.test.ts +++ b/site/test/landing/landing-page.test.ts @@ -24,6 +24,13 @@ describe("first-class product splash", () => { expect(landingCss).toContain(":root:has(.ca-landing) .main-pane"); }); + it("keeps the atmospheric hero artwork fixed behind the scrolling landing narrative", () => { + expect(indexMdx).toMatch(/class="ca-landing"[^>]*--ca-hero-art/); + expect(indexMdx).not.toMatch(/class="ca-splash"[^>]*--ca-hero-art/); + expect(landingCss).toMatch(/\.ca-landing::before\s*\{[^}]*position:\s*fixed;/s); + expect(landingCss).not.toContain(".ca-splash::before"); + }); + it("leads with the reader outcome and supported hosts", () => { expect(indexMdx).toContain("Hard gates for agentic coding."); expect(indexMdx).toContain("You decide. codeArbiter enforces."); diff --git a/site/test/rehype-table-shell.test.ts b/site/test/rehype-table-shell.test.ts new file mode 100644 index 00000000..ed906d11 --- /dev/null +++ b/site/test/rehype-table-shell.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from "vitest"; +import { rehypeTableShell } from "../scripts/rehype-table-shell"; + +type Node = { + type?: string; + tagName?: string; + properties?: Record; + children?: Node[]; +}; + +function run(tree: Node): Node { + const transformer = rehypeTableShell()(); + transformer(tree); + return tree; +} + +function table(): Node { + return { type: "element", tagName: "table", properties: {}, children: [] }; +} + +describe("rehypeTableShell", () => { + it("wraps a rendered table without changing the table element", () => { + const original = table(); + const tree = run({ type: "root", children: [original] }); + const shell = tree.children?.[0]; + + expect(shell?.tagName).toBe("div"); + expect(shell?.properties?.className).toEqual(["ca-table-shell"]); + expect(shell?.children?.[0]).toBe(original); + }); + + it("finds tables nested inside other rendered content", () => { + const tree = run({ + type: "root", + children: [{ type: "element", tagName: "section", properties: {}, children: [table()] }], + }); + + expect(tree.children?.[0].children?.[0].properties?.className).toEqual(["ca-table-shell"]); + }); + + it("is idempotent when a table is already inside the shared shell", () => { + const tree: Node = { + type: "root", + children: [{ + type: "element", + tagName: "div", + properties: { className: ["ca-table-shell"] }, + children: [table()], + }], + }; + + run(tree); + expect(tree.children?.[0].children?.[0].tagName).toBe("table"); + }); +}); From 1105dec2ff3b632a60d76a30a28e9e3d18166d42 Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 15:08:10 -0400 Subject: [PATCH 08/21] docs: specify first-class site completion --- .codearbiter/overrides.log | 1 + ...0-docs-navigation-brand-diagrams-design.md | 313 ++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-30-docs-navigation-brand-diagrams-design.md diff --git a/.codearbiter/overrides.log b/.codearbiter/overrides.log index b25b0948..e511da81 100644 --- a/.codearbiter/overrides.log +++ b/.codearbiter/overrides.log @@ -26,3 +26,4 @@ 2026-07-30T07:24:09.5507998-04:00 | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: isolated docs-site worktree [2026-07-30T09:25:46.2296648-04:00] | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: resume docs-site work after upstream replay removed local marker [2026-07-30T09:43:51.6342839-04:00] | BY: SUaDtL@users.noreply.github.com | DEV: exit +2026-07-30T14:43:53.4651428-04:00 | BY: SUaDtL@users.noreply.github.com | DEV: enter | NOTE: continue isolated docs-site brand and design-system work diff --git a/docs/superpowers/specs/2026-07-30-docs-navigation-brand-diagrams-design.md b/docs/superpowers/specs/2026-07-30-docs-navigation-brand-diagrams-design.md new file mode 100644 index 00000000..cccd1aa6 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-docs-navigation-brand-diagrams-design.md @@ -0,0 +1,313 @@ +# First-Class Documentation Site: Navigation, Brand, Diagrams, and Learning Quality + +**Date:** 2026-07-30 + +**Status:** Approved design, awaiting written-spec review + +**Branch:** `codex/docs-first-class-site` +**Deployment target:** GitHub Pages under `/codeArbiter/` + +## Intent + +The documentation site must take a reader from no prior codeArbiter knowledge to confident power-user operation. It must also present codeArbiter with the visual and editorial finish expected of a serious open-source infrastructure project. + +This phase builds on the existing first-class landing page and learning path. It closes the remaining gaps in navigation, brand execution, diagram quality, page-level teaching coverage, generated reference usefulness, and README consistency. + +## Goals + +1. Give returning visitors immediate access to the complete documentation navigation from the splash page. +2. Restore the historic gate/commit-line mark as the core codeArbiter identity and finish it as a coherent logo system. +3. Redraw every operational SVG under one professional diagram system. +4. Audit every authored and generated page for task-level usefulness. +5. Keep every demonstration honest: runtime media must come from real product behavior, not an invented terminal sequence. +6. Bring the repository README into visual and editorial alignment after the site is complete. +7. Preserve the existing base-path-safe build, source-backed generator, accessibility, and verification guarantees. + +## Non-goals + +- No paid services, stock subscriptions, or externally hosted commercial assets. +- No change to codeArbiter's product behavior or governance semantics. +- No replacement documentation framework. +- No decorative rasterization of operational diagrams that need precise labels, accessibility, or future edits. +- No fabricated GIF, video, screenshot, or terminal transcript presented as product evidence. +- No merge to the default branch. Work remains on the isolated branch and draft pull request. + +## Approved Design Decisions + +### 1. Splash-page documentation rail + +The splash page receives a persistent collapsed rail on the left edge below the site header. + +- Desktop collapsed width: `38px`. +- Narrow-screen collapsed width: `31px`. +- The collapsed rail contains a gold right-pointing triangle and vertical `OPEN DOCS` label. +- Activating it opens an overlay drawer containing the real Starlight sidebar tree. +- The logo remains an ordinary home link. It does not double as a menu control. +- The rail appears only on the splash page. Normal documentation pages retain the existing desktop sidebar and mobile menu behavior. + +The component must reuse `Astro.locals.starlightRoute.sidebar` through the existing `SidebarSublist` rendering path. It must not maintain a second navigation tree. + +The open drawer uses: + +- a desktop width of `292px`; +- a mobile width of `min(292px, calc(100vw - 31px))`; +- a backdrop; +- document scroll lock; +- Escape-to-close; +- focus trapping; +- focus restoration to the opener; +- `aria-expanded` and `aria-controls`; +- close-on-backdrop and close-button behavior. + +If the generated sidebar is unavailable, the rail must still offer a base-path-safe link to **What Is codeArbiter**. A no-script fallback must expose the same direct link. + +### 2. Historic gate brand system + +The historical mark recovered from the repository is the fixed brand foundation. Its meaning is: + +1. a hollow node represents an unproven change; +2. the horizontal segment represents the commit/work line; +3. twin posts and a beam form the governed gate; +4. the filled gold node represents a proven decision after the gate. + +The mark already combines code and arbitration through one construction. It must not be replaced with an A monogram, gavel, courthouse, scales, shield, or literal code/legal mash-up. + +The silhouette and element arrangement remain stable. One approved semantic adjustment is made: + +- replace the small filled downward pivot triangle above the beam with an open gold terminal-prompt chevron pointing downward into the gate. + +The production redraw may correct optical alignment, intervals, terminal shapes, stroke hierarchy, and small-size overshoot. It may use one restrained gold highlight in the full-size color mark, but must remove the fuzzy glow from small variants. + +The delivered brand system includes: + +- full horizontal wordmark; +- standalone icon; +- monochrome mark; +- favicon; +- reduced-detail `16px` master; +- light-on-dark color master used by the site. + +The wordmark remains `codeArbiter`, with `code` in the light neutral and `Arbiter` in the gold accent. Logo and wordmark must read as one cohesive brand unit. + +### 3. Fixed landing atmosphere + +The landing page's generated gate artwork is atmosphere, not operational evidence. It remains visually behind the page while the foreground content scrolls over it. + +Implementation must avoid the previously observed vertical seam/disturbance. The artwork must: + +- cover the intended viewport without a central edge or repeated tile; +- remain fixed on desktop; +- use a mobile-safe fixed-layer or non-janky fallback where `background-attachment: fixed` is unreliable; +- preserve text contrast; +- respect reduced-motion preferences; +- never obscure interactive content. + +### 4. Diagram system and complete redraw + +SVG remains the correct medium for operational explanations because it provides precise geometry, sharp labels, accessibility, responsive behavior, and maintainable source. + +Every current SVG is reviewed and redrawn as needed. No file survives merely because it already exists: + +1. `activation-states.svg` +2. `commit-gate-phases.svg` +3. `core-fanout.svg` +4. `four-tier-map.svg` +5. `gate-model.svg` +6. `lane-add-dep.svg` +7. `lane-adr.svg` +8. `lane-feature.svg` +9. `lane-flow.svg` +10. `lane-opt-in.svg` +11. `lane-release.svg` +12. `lane-sprint.svg` +13. `provenance-drift-flow.svg` +14. `sandbox-boundary.svg` +15. `two-axis-model.svg` + +`statusline.png` is runtime UI evidence rather than an operational SVG. It is audited separately for currency and provenance. + +#### Diagram visual contract + +- One shared near-black canvas, neutral text palette, and semantic accent palette. +- Gold indicates an allowed, selected, or governed path. +- Red is reserved for a real hard stop or security block. +- Slate indicates neutral structure and advisory context. +- A single type family is used throughout, drawing from the site's local Manrope and JetBrains Mono assets. +- Prose labels are no smaller than `14px` at the diagram's normal desktop rendering size. +- Monospace annotations are no smaller than `12px` at normal rendering size. +- Primary strokes use a shared scale; secondary dividers and leaders use a distinct shared scale. +- Nodes, cards, arrows, margins, and gutters align to an explicit spacing grid. +- Repeated elements use identical dimensions unless semantic weight requires a documented difference. +- Arrowheads, radii, labels, and status badges remain consistent across the family. +- Captions complement the visual; they do not repeat labels already visible in the SVG. +- Empty space must be intentional and balanced. Disconnected badges, unexplained dead zones, accidental asymmetry, and arbitrary scale changes fail review. +- Every diagram must remain understandable at its normal content-column width and usable through horizontal overflow on small screens when shrinking would make labels unreadable. +- Every image reference keeps an accurate alt description. Complex figures may add an adjacent prose explanation when alt text alone would become unwieldy. + +`gate-model.svg` is the calibration diagram for the system. Its soft and hard gate halves must use matched geometry, explicit continuation/stop outcomes, and an integrated hard-stop statement rather than a disconnected badge. + +### 5. Page-level teaching contract + +Every documentation page is audited from the perspective of a reader encountering that part of codeArbiter for the first time. + +An authored concept or guide page must make the following discoverable where applicable: + +- what the mechanism or workflow is; +- when the reader should use it; +- the exact invocation or configuration; +- prerequisites and hard gates; +- what happens step by step; +- what success looks like; +- how to verify the result; +- common failure modes and recovery; +- the next relevant page or workflow. + +The structure can vary when a page is conceptual rather than procedural, but no page may rely on unexplained internal vocabulary. Terms link to the glossary or their owning concept page. + +The review includes every authored page under `site/src/content/docs/`, including the previously reported weak/broken **The Gated-Lane Model** and **Checkpoints** destinations. + +### 6. Generated reference contract + +Generated command, skill, agent, and hook pages remain source-backed. Hand editing generated output is prohibited. + +When a generated page does not teach the reader how to use or interpret that surface, the generator or its source-extraction model must be improved. Generated pages should expose, where the source supports it: + +- purpose; +- invocation; +- arguments; +- lane or ownership; +- phases and gates; +- state read and written; +- expected output; +- failure/stop conditions; +- related commands and concepts; +- provenance back to the source file and revision. + +The generator must preserve deterministic output and existing slug/base-path behavior. + +### 7. Real demonstrations only + +Operational media is divided into two honest categories: + +- **Conceptual art/diagram:** explicitly explanatory and not presented as a runtime capture. +- **Runtime evidence:** captured from a real invocation against the shipped code. + +A GIF or training video is added only when the real workflow can be reproduced and recorded. Runtime media must carry enough adjacent context to identify: + +- the command or interaction performed; +- the shipped code path exercised; +- capture date or source revision; +- the expected result; +- any limitation in what the recording proves. + +The existing hook proof remains acceptable only if its checked-in proof data and media continue to match the shipped hook behavior. Invented terminal playback may be used only as a clearly labeled conceptual illustration, never as evidence. + +### 8. README synchronization + +The repository README is revised only after the site design and content audit stabilize. + +It must: + +- present the same value proposition and historic gate identity; +- provide a concise install-to-first-proof path; +- distinguish Claude Code, Codex, and Pi accurately; +- link to the learning path and generated reference; +- explain hard gates, repository-owned state, and auditability without duplicating entire docs pages; +- avoid claims not proven by the current repository; +- use repository-safe artwork that renders correctly on GitHub; +- reflect the final navigation names and current command syntax. + +The README is an entry point, not a second documentation site. + +## Component Boundaries + +### `SplashDocsRail` + +Owns the splash-only collapsed rail, drawer shell, open/close state, accessibility, and fallback link. It consumes the existing Starlight sidebar structure and delegates tree rendering to `SidebarSublist`. + +### Brand assets + +A shared SVG source defines the historical gate geometry. Wordmark, icon, monochrome, and favicon variants reuse that geometry rather than duplicating hand-tuned shapes across unrelated files. + +### Diagram primitives + +SVGs share a documented token vocabulary for color, typography, stroke, spacing, card geometry, arrowheads, and status treatment. The implementation may use a small local authoring helper or reusable SVG definitions if it keeps final assets deterministic and dependency-free. + +### Generator + +Source discovery/extraction remains separate from page rendering. Template changes operate on typed rendered-page data rather than parsing already-rendered Markdown. + +## Interaction and Data Flow + +1. Astro/Starlight produces the canonical sidebar tree. +2. Normal pages send that tree to the existing sidebar. +3. The splash page additionally sends the same tree to `SplashDocsRail`. +4. The rail's client behavior manages only presentation state; it does not fetch or construct navigation data. +5. Documentation generation reads source command/skill/agent/hook material, creates typed page data, renders Markdown, and creates the sidebar JSON before Astro starts. +6. Diagram and brand assets are built as static local assets and receive the `/codeArbiter/` base path through the existing asset pipeline or base-link transform. + +## Failure Handling + +- Missing sidebar generation degrades to a direct **What Is codeArbiter** link. +- Drawer script failure must not hide all documentation access. +- Focus and scroll state are restored even when navigation is cancelled. +- Asset links must remain base-path-safe locally and on GitHub Pages. +- Diagram overflow must be localized to its figure container; it must not create page-level horizontal scrolling. +- A generated page missing required source fields renders an explicit omission or omits the unsupported section; it must not invent content. +- Media whose provenance cannot be verified is removed or relabeled as conceptual. + +## Verification + +### Automated contracts + +- Rail renders only on the splash page. +- Rail and drawer use the canonical sidebar entries. +- Drawer keyboard, focus, backdrop, scroll-lock, and ARIA behavior pass tests. +- No-script and missing-sidebar fallbacks resolve under `/codeArbiter/`. +- Historic gate masters contain the approved open downward chevron and preserve the six semantic elements. +- Diagram audit checks viewBox presence, approved font families, minimum label sizes, allowed palette, and prohibited fuzzy filters at reduced sizes. +- Every diagram filename referenced by content exists. +- Generated output remains deterministic. +- All internal links resolve. +- Favicon and hashed logo assets exist in the production build. +- Existing typecheck, unit, coverage, build, and link-audit suites remain green. + +### Visual review + +Capture and inspect at minimum: + +- `1440x900` desktop; +- `1024x768` compact desktop/tablet; +- `390x844` mobile. + +Review the splash at top, mid-scroll, and drawer-open states; the header wordmark; the rail; representative authored and generated pages; all fifteen diagrams; long tables; code blocks; and the README's GitHub rendering constraints. + +Visual acceptance requires: + +- no vertical hero seam; +- no clipped wordmark or rail label; +- no ambiguous navigation icon; +- no diagram label below the readability floor; +- no accidental horizontal page scroll; +- no misaligned repeated diagram elements; +- no broken or empty destination from the primary navigation; +- no decorative asset presented as runtime proof. + +## Implementation Order + +1. Add failing contracts for the brand, rail, diagram rules, base paths, and page-content requirements. +2. Restore and refine the historic gate brand system. +3. Implement the splash documentation rail and drawer. +4. Fix the landing background layer and verify the seam at target viewports. +5. Establish diagram tokens and redraw `gate-model.svg` as the calibration asset. +6. Redraw and review the remaining fourteen SVGs in coherent families. +7. Audit authored pages and repair content/navigation gaps. +8. Improve generator data/templates for any generated-page gaps. +9. Capture or validate only reproducible runtime media. +10. Synchronize and polish the README. +11. Run automated, visual, accessibility, and source/provenance verification. +12. Commit, push the isolated branch, and update the existing draft pull request without merging. + +## Acceptance + +The work is complete when a first-time reader can follow the site from orientation through a verified repository setup and onward to advanced operation; a returning reader can open the full docs navigation immediately from the splash; every diagram meets the shared visual contract; the historic gate identity is professionally executed and consistent; generated references explain how their surfaces are used; media claims are honest; the README matches the finished site; and all verification is green on the exact branch head. From f6bf29bc2433702410ed6047021779d911b0187e Mon Sep 17 00:00:00 2001 From: SUaDtL Date: Thu, 30 Jul 2026 16:09:18 -0400 Subject: [PATCH 09/21] docs: plan first-class site completion --- .../plans/2026-07-30-docs-site-completion.md | 438 ++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-30-docs-site-completion.md diff --git a/docs/superpowers/plans/2026-07-30-docs-site-completion.md b/docs/superpowers/plans/2026-07-30-docs-site-completion.md new file mode 100644 index 00000000..9b79f367 --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-docs-site-completion.md @@ -0,0 +1,438 @@ +# Documentation Site Completion Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Complete the first-class codeArbiter documentation experience with the historic gate brand, splash docs rail, fixed hero atmosphere, professional diagram family, comprehensive page audit, source-backed generated references, and synchronized README. + +**Architecture:** Keep Astro/Starlight as the site shell and reuse its canonical sidebar tree in a splash-only overlay component. Treat the historical gate as a shared SVG geometry system, retain static SVG for operational diagrams, and strengthen authored/generated content at its source. Add static-contract tests around visual assets, interactions, provenance, and page usefulness before each implementation slice. + +**Tech Stack:** Astro 7, Starlight 0.41, TypeScript 5.7, Vitest 4.1, local SVG/WebP assets, Markdown/MDX, CSS. + +## Global Constraints + +- Work only in `C:\Users\brenn\projects\codeArbiter-worktrees\docs-first-class-site` on `codex/docs-first-class-site`. +- Spend no money beyond the user's ChatGPT subscription. +- Keep the pull request in draft state and never merge it. +- Preserve GitHub Pages base-path safety under `/codeArbiter/`. +- Reuse local Manrope and JetBrains Mono font assets. +- Runtime media must come from real product behavior and carry provenance. +- Do not stage `.superpowers/`. +- Use the historic commit-line gate silhouette; only its filled pivot triangle becomes an open downward terminal chevron. +- Operational explanations remain editable, accessible SVG. +- Use test-first changes and independently verifiable commits. + +--- + +## File Structure + +- `site/src/components/SplashDocsRail.astro`: splash-only rail, overlay drawer, fallback, accessibility, and client state. +- `site/src/components/SidebarSublist.astro`: canonical navigation tree renderer consumed by both normal sidebar and splash drawer. +- `site/src/assets/gate-mark.svg`: shared icon-only historical gate geometry. +- `site/src/assets/logo.svg`: horizontal wordmark using the shared geometry. +- `site/src/assets/favicon.svg`: reduced-detail small-size gate master. +- `site/src/styles/landing.css`: landing background layer and rail/drawer layout. +- `site/src/styles/design-system.css`: shared brand and diagram tokens. +- `site/src/content/docs/index.mdx`: splash integration only. +- `site/public/diagrams/*.svg`: fifteen operational diagrams. +- `site/scripts/diagram-audit.ts`: deterministic static SVG contract checker. +- `site/test/landing/landing-page.test.ts`: splash, rail, hero, and brand contracts. +- `site/test/generator/diagrams.test.ts`: complete diagram-family contracts. +- `site/test/content/documentation-presentation.test.ts`: authored-page usefulness regressions. +- `site/test/generator/render-*.test.ts`: generated-reference usefulness contracts. +- `README.md`, `docs/readme-hero.webp`: synchronized repository entry point. + +### Task 1: Restore the historic gate brand system + +**Files:** +- Create: `site/src/assets/gate-mark.svg` +- Modify: `site/src/assets/logo.svg` +- Modify: `site/src/assets/favicon.svg` +- Modify: `site/src/styles/design-system.css` +- Modify: `site/test/content/documentation-presentation.test.ts` +- Modify: `site/test/link-audit/lib.test.ts` + +**Interfaces:** +- Produces: an icon master with commit line, hollow node, twin posts, beam, downward open chevron, and proven node. +- Produces: full wordmark and favicon variants derived from the same geometry. + +- [ ] **Step 1: Replace the A-mark contract with a failing historic-gate contract** + +Add assertions that the mark contains `data-brand-element` values for `commit-line`, `unproven-node`, `gate-posts`, `gate-beam`, `terminal-chevron`, and `proven-node`; contains no ``; and exposes `viewBox="0 0 160 28"` for the horizontal lockup. + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: `npm test -- --run test/content/documentation-presentation.test.ts` + +Expected: failure because the current A-shaped logo has no historic-gate semantic elements. + +- [ ] **Step 3: Implement the three brand masters** + +Restore the historical geometry from the predecessor of `7a0a089`, redraw it on a consistent grid, replace the filled pivot with: + +```svg + +``` + +Use thicker simplified geometry in `favicon.svg`; do not use glow filters. + +- [ ] **Step 4: Run brand and link-audit tests** + +Run: `npm test -- --run test/content/documentation-presentation.test.ts test/link-audit/lib.test.ts` + +Expected: PASS. + +- [ ] **Step 5: Commit the brand slice** + +Commit message: `feat(site): restore the historic gate identity` + +### Task 2: Add the splash documentation rail + +**Files:** +- Create: `site/src/components/SplashDocsRail.astro` +- Modify: `site/src/content/docs/index.mdx` +- Modify: `site/src/styles/landing.css` +- Modify: `site/test/landing/landing-page.test.ts` + +**Interfaces:** +- Consumes: `Astro.locals.starlightRoute.sidebar`. +- Consumes: `SidebarSublist` with `{ sublist: SidebarEntry[] }`. +- Produces: `.ca-docs-rail`, `#ca-docs-drawer`, and a base-safe fallback to `overview/`. + +- [ ] **Step 1: Write failing rail structure and accessibility tests** + +Assert that the landing imports ``, the component delegates to ``, the opener has `aria-controls="ca-docs-drawer"` and `aria-expanded="false"`, and the component includes Escape handling, focus restoration, scroll lock, backdrop close, and an overview fallback. + +- [ ] **Step 2: Run the landing test and verify RED** + +Run: `npm test -- --run test/landing/landing-page.test.ts` + +Expected: failure because `SplashDocsRail.astro` does not exist. + +- [ ] **Step 3: Implement semantic rail and drawer markup** + +Use a button for the enhanced control, an `

    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 JeIUMo007nsmX`nk literal 0 HcmV?d00001 diff --git a/docs/reports/docs-site/2026-07-30-page-audit.md b/docs/reports/docs-site/2026-07-30-page-audit.md new file mode 100644 index 00000000..9cd85fd0 --- /dev/null +++ b/docs/reports/docs-site/2026-07-30-page-audit.md @@ -0,0 +1,230 @@ +# Documentation Product Audit + +**Date:** 2026-07-30 + +**Scope:** the complete built documentation site, its shared presentation system, and the repository README + +**Standard:** a reader must be able to move from no prior codeArbiter knowledge to source-backed +power-user operation without reconstructing missing steps from chat history + +**Result:** PASS after the repairs and upgrades recorded below + +## What was proved + +- All **139 built index routes** were opened in a real Chromium renderer at **1440 x 1000** and + **390 x 844**. The 278 route/viewport checks each returned a final 200 response, one page title, + one main H1, non-empty main content, zero page-level horizontal overflow, and a semantic overflow + shell around every Markdown table. +- The custom 404 page was reviewed separately, bringing the reviewed built surface to **140 pages**. +- All **16 unique in-page media assets** returned 200 with the expected image content type. +- Six generated compatibility aliases were followed to their canonical skill pages at both + viewports. +- The link audit resolved **19,786 internal links across 140 pages**. +- All 37 hand-authored inner-doc pages carry a learning contract: level, expected time, outcome, + prerequisites, and proof of completion. The landing page uses the separate product-splash + contract. +- The generated catalogs were assessed through their source-backed family templates and by opening + every generated route. Commands explain invocation, example, reach point, gates, relationships, + and source. Skills explain operating context, phases, stops, artifacts, relationships, and source. + Agents explain role, dispatch ownership, tools, constraints, output, relationships, and source. + +## Audit rubric + +Every hand-authored route was judged against five questions: + +1. **Orientation:** does the page say what portion of codeArbiter it owns and why the reader is here? +2. **Prerequisites:** does it name the state, host, tool, or earlier concept the reader needs? +3. **Action or model:** can the reader either perform the workflow or explain the mechanism? +4. **Proof and recovery:** does it say how success is demonstrated and where failure routes? +5. **Progression:** does the page lead into the next useful guide or source-backed reference? + +`PASS` means the route met the rubric without a change in this final audit. `REPAIRED` means the +route failed presentation or navigation and was corrected. `UPGRADED` means it was usable but +received a first-class visual or learning-path improvement. + +## Hand-authored route ledger + +### Product entry and learning path + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/` | REPAIRED + UPGRADED | Outcome-led product splash, verified direct-hook replay, host posture, guarantees, flow, fit, and CTA. The rendered gate art is fixed behind the scrolling narrative; the replay is real product evidence and does not autoplay. The Starlight skip link now reaches a focusable `_top` target. | +| `/overview/` | REPAIRED | Explains command, route, host-specific role execution, gate, and ship without pretending Claude packaged agents, Codex host-provided threads, and Pi hardened children use one mechanism; names the bounded older-Codex fallback, lane families, and context-minimization model; routes to Concepts and Reference. | +| `/learn/` | REPAIRED | Four-stage curriculum now links through a normalized deployment base, states the honest 3–4 hour module total, includes proof after every stage, and ends with an eight-part capstone that points migration/CI/deployment scope to security controls instead of unrelated environment configuration. | + +### Start + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/getting-started/choose-your-host/` | PASS | Compares stability, syntax, distribution, trust, state, UI, and best fit for all three governance hosts. | +| `/getting-started/install/` | REPAIRED | Gives host-specific install and trust steps, prerequisites, activation boundary, verification, update path, and exact next step. Interpreter checks now work in POSIX shells and PowerShell and registration language matches each host. | +| `/getting-started/quickstart/` | PASS | Uses a disposable repository with meaningful source, proves H-03 through doctor, explains expected state, and provides recovery. | +| `/getting-started/claude-code-and-codex/` | PASS | Separates generated parity, hosted packaging checks, and dated manual live-hook evidence; retains the precise core-fanout SVG. | +| `/getting-started/pi/` | REPAIRED | Covers Git-only install, project trust, activation, doctor, footer, security, Windows, pinning, upgrade, and uninstall for the preview host. The prerequisite now matches the exact supported Pi versions and the install begins with mechanical `ca-pi-v*` tag discovery. | +| `/getting-started/compatibility/` | REPAIRED | Distinguishes plugin runtime from docs-development dependencies and records host, platform, interpreter, network, and optional-feature boundaries. Pi's Vitest aggregate prerequisite is explicitly isolated as maintainer-only verification, not adopter setup. | +| `/faq/` | PASS | Answers adoption objections about blocking, bypass, uninstall, speed, data, teams, mixed hosts, misfit gates, and conflict routing with actionable links. | + +### Workflows + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/guides/opt-in-a-repo/` | REPAIRED | Teaches the shared Claude Code, Codex, and Pi repository contract, initialization, brownfield/greenfield routing, activation verification, and safe disable/recovery; retains its exact lane SVG. | +| `/guides/feature-lane/` | PASS | Runs new behavior from intent through spec, plan, TDD, review, commit, and PR with persisted artifacts and completion proof. | +| `/guides/autonomous-sprints/` | PASS | Explains the interactive gate, resumable task state, SMARTS decisions, hard stops, review, and PR boundary. | +| `/guides/recording-adrs/` | PASS | Shows when an ADR is warranted, how it is attributed, how `governs:` affects later reads, and how to verify the record. | +| `/guides/adding-a-dependency/` | PASS | Covers license, provenance, maintenance, vulnerabilities, install timing, and the dependency-review gate. | +| `/guides/releasing-a-version/` | PASS | Gives target-aware release selection, dry run, version/changelog/tag behavior, and proof by target. | + +### Operate + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/guides/overriding-a-gate/` | REPAIRED | Defines the narrow authority of an override, identity requirements, append-only record, and what an override cannot authorize, including H-18's deliberately non-overridable in-session boundary. | +| `/guides/ca-sandbox/` | REPAIRED | Explains the isolated repository boundary, creation and extraction flow, network posture, Windows/WSL notes, and recovery; retains the security-boundary SVG and now includes the marketplace-registration prerequisite. | +| `/guides/the-statusline/` | PASS | Annotates a real captured statusline PNG rather than inventing a UI; explains every segment, enabled-repo behavior, cost source, and removal. | +| `/guides/troubleshooting/` | PASS | Symptom-driven diagnosis covers dormant activation, malformed context, interpreter failures, stale cache, trust, hooks, status UI, and host-specific recovery. | +| `/guides/uninstalling/` | REPAIRED | Separates disable from uninstall, preserves the shared three-host `.codearbiter/` store, covers Claude Code, Codex, and Pi activation protection accurately, turns Pi version removal into a tag/list-driven action, and distinguishes per-host git-enforcer registry cleanup from sentinel-only shared-shim removal after the last host is gone. | + +### Understand + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/concepts/` | REPAIRED | The broken-feeling raw table was replaced with ten purpose-built concept cards keyed by reader question, domain, icon, and destination. | +| `/concepts/gated-lanes/` | REPAIRED | Explains why work types take different paths, soft versus hard gates, the nine-phase commit gate, and how to choose a lane without overclaiming override authority over H-18. | +| `/concepts/smarts/` | PASS | Defines all six lenses, verdicts, recommendation strengths, precedent, user authority, sprint reuse, and audit evidence. | +| `/enforcement/` | REPAIRED | Separates activation, advisory and blocking behavior, fail-loud interpreter posture, commit-time controls, trust, and child-process boundaries. H-19 is now described as cooperative attestation rather than unforgeability. | +| `/concepts/provenance-drift/` | PASS | Explains tracked derivations, stale detection, SessionStart reporting, commit-gate healing, and verification; retains the provenance-flow SVG. | +| `/concepts/adrs/` | PASS | Explains accepted decisions, supersession, attribution, governed files, and the difference between an ADR and ordinary documentation. | +| `/concepts/jit-context-injection/` | PASS | Shows the four-tier priority model, token budget, read-time pointer behavior, and how to confirm the controlling artifact. | +| `/concepts/checkpoints/` | REPAIRED | The reviewer fleet remains a semantic data grid, now inside the shared responsive table shell; the page explains fleet, funnel, run, report, and tribunal boundary. | +| `/concepts/persona-and-context/` | PASS | Separates the orchestrator register from project-owned facts and shows why authors, reviewers, and routing remain distinct. | +| `/concepts/auditability/` | PASS | Traces commits, ADRs, questions, overrides, sprint decisions, checkpoints, and audit packets into a reconstructable record. | +| `/concepts/hardening-history/` | PASS | Explains selected controls through the failure mode that caused each one and routes operators to the current source-backed contract. | + +### Reference and Labs + +| Route | Status | Reader outcome and audit evidence | +|---|---|---| +| `/codearbiter-directory/` | PASS | Comprehensive artifact-by-artifact reference names creator, reader, lifecycle, authority, and recovery for repository-owned state. | +| `/glossary/` | REPAIRED | Defines every terminology-locked term promised by its proof—command, skill, agent, phase, stage, layer, gate, and severity—and records the H-18 override exception. | +| `/hooks/` | REPAIRED | Traces the shared Python core and non-event scripts while separately mapping real Claude Code registrations, Codex adapter registrations, and Pi wrapper/lifecycle events. Interpreter compatibility and marker trust language now match source. | +| `/feature-forge/overview/` | UPGRADED | Adds rendered forge atmosphere for identity while retaining the exact two-axis SVG for SemVer versus feature maturity; the decorative image uses empty alt because its caption carries the meaning. | +| `/feature-forge/whats-in-the-forge/` | PASS | Generated from the same preview allowlist as reference badges; every preview names opt-in, dependency, evidence gap, and graduation signal. | +| `/feature-forge/using-preview-features/` | PASS | Teaches deliberate opt-in, least-consequential mode, fresh-session handling, off-switch verification, and defect reporting. | + +## Generated route ledger + +The generated surface is reviewed by family because one generator controls every page in the +family. Every individual route was still rendered at both audit viewports. + +| Family | Routes | Status | Contract checked | +|---|---:|---|---| +| Reference entry | 1 | REPAIRED | catalog purpose, source-backed provenance, counts, and command → skill → agent usage; regenerated facts are distinguished from hand-reviewed guidance that can drift | +| Commands | 40 | UPGRADED | host syntax, availability, example, owning skill, gates, related surfaces, and exact source; redundant journey cards are suppressed on direct entity landings | +| Skills | 23 | UPGRADED | context, invocation ownership, phases, hard stops, durable artifacts, related surfaces, and source; redundant journey cards are suppressed on direct entity landings | +| Skill aliases | 6 | PASS | meta-refresh and canonical link resolve to the intended current skill page | +| Agents | 28 | REPAIRED | role, dispatch ownership, model/tool constraints, emitted result, relationships, and source; every deep landing repeats Claude packaged-agent, current-Codex host-thread with bounded fallback, and Pi hardened-child semantics, translates Claude-only path placeholders in reader copy, and uses compact mobile orientation | +| Configuration | 1 | PASS | typed groups, defaults, accepted values, effects, safe testing, undo, and source-presence contract | +| Hook gates | 1 | PASS | generated from actual `block()` and `remind()` call sites with gate ID, event, message, and source | +| Changelog | 1 | REPAIRED | accurately labeled as the core `ca` timeline, linked to the three independently versioned sibling changelogs, and classified as Reference | + +The six alias routes checked were: + +- `/reference/skills/context-check-2/` → `/reference/skills/context-check` +- `/reference/skills/debug-2/` → `/reference/skills/debug` +- `/reference/skills/decompose-2/` → `/reference/skills/decompose` +- `/reference/skills/refactor-2/` → `/reference/skills/refactor` +- `/reference/skills/release-2/` → `/reference/skills/release` +- `/reference/skills/tribunal-2/` → `/reference/skills/tribunal` + +## Adversarial follow-up + +The first clean renderer sweep was not accepted as sufficient. A second reviewer pass read all 38 +hand-authored routes and sampled every generated family as a novice entering from search. It +blocked sign-off on accessibility, time honesty, cross-host accuracy, mobile legibility, and +generated-reference framing. Every block was repaired before this ledger returned to PASS: + +- the landing skip target now exists and accepts programmatic focus; +- the learning path's fixed module estimates and capstone destinations are honest; +- Pi support and tag selection are mechanically actionable; +- all 17 `.ca-diagram` figures retain at least a 672px readable canvas inside a local horizontal + scroll region at 390px, with zero page-level overflow; the separately styled real statusline map + uses a wider 864px canvas; +- hook and uninstall language follows the actual Claude Code manifest, Codex adapter manifest, and + Pi extension events; +- repository opt-in and role execution now state all three host contracts without collapsing their + different dispatch mechanisms; +- Pi's platform-aggregate dependency setup is labeled maintainer verification rather than an adopter + prerequisite; +- marker language states the cooperative same-user threat boundary; +- the terminology lock is complete; +- generated entity landings remove the redundant journey card, compact their direct-entry context, + and explain host dispatch semantics; +- the changelog states exactly which independently versioned release line it covers. + +A final read-only advisor recheck after these repairs returned PASS with no remaining contradiction. +The commit review fleet then caught and repaired two last source-of-truth drifts: current Codex role +dispatch versus its older inline fallback, and the ordered multi-host uninstall contract for the +shared hook shim. Architecture, security, and coverage rechecks all returned PASS after those +repairs. + +## Media decision: SMARTS + +Three strategies were considered: + +- **A. Rasterize every SVG.** Maximum atmosphere, weakest precision and maintainability. +- **B. Keep every surface vector.** Maximum precision, weakest brand differentiation on decorative + surfaces. +- **C. Use a hybrid evidence hierarchy.** Rendered art for atmosphere, SVG for explanatory systems, + and real capture for claims about shipped UI or runtime behavior. + +| Lens | A: all raster | B: all vector | C: hybrid | +|---|---|---|---| +| Scalable | Weak: repeated art generation for every diagram. | Strong: small, responsive assets. | Strong: each medium is used only where it pays. | +| Maintainable | Weak: text and logic become pixels. | Strong: diagrams remain editable. | Strong: operational diagrams stay editable; decorative assets are few. | +| Available | Adequate: local assets, but heavier payload. | Strong: compact and dependency-free. | Strong: optimized local WebP plus compact SVG. | +| Reliable | Weak: image generation can invent or blur semantics. | Strong: deterministic geometry and labels. | Strong: only non-semantic atmosphere is generated. | +| Testable | Weak: semantic drift is hard to assert. | Strong: source text and geometry are inspectable. | Strong: asset roles are contract-tested and captures retain provenance. | +| Securable | Adequate: local output, but larger opaque artifacts. | Strong: transparent source. | Strong: no remote runtime assets; proof remains source-digested. | + +**Decision:** C, hybrid. **Strength:** strong. + +Consequences: + +- The fixed landing backdrop and Feature Forge atmosphere use optimized rendered WebP. +- The header brand and navigation icons use crisp project-owned SVG because they must scale and + remain recognizable at small sizes. +- Gate, lane, activation, provenance, fan-out, sandbox, and maturity diagrams remain SVG because + their exact relationships are the content. +- The statusline remains a real captured PNG. +- The hook proof remains a real source-digested replay in MP4/WebM with an exact text transcript. +- No fictional training GIF or video was introduced. + +## README alignment + +The README was reduced from a drifting long-form manual to a professional adoption surface: + +- rendered product and Feature Forge artwork replace the legacy decorative SVG banners; +- host choice, prerequisites, install, trust, first-repository proof, and the docs learning path + appear before architecture internals; +- the complete command catalog remains available in a collapsed section for release-consistency + checks, while the generated site reference remains authoritative; +- Pi, Codex, network, preview, license, and Build Week evidence contracts remain explicit; +- detailed configuration, troubleshooting, hook, and workflow material now routes to the site + instead of being duplicated. + +## Verification commands + +Run from `site/` unless a command shows otherwise: + +```text +npm test +npm run typecheck +npm run coverage +npm run build +npm run link-audit +python ../.github/scripts/check_badge_consistency.py +python ../.github/scripts/test_public_codex_docs.py +python ../.github/scripts/test_public_pi_docs.py +python ../.github/scripts/test_license_consistency.py +``` diff --git a/site/astro.config.mjs b/site/astro.config.mjs index 7f3b1b4c..be7f953f 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -4,6 +4,7 @@ import starlight from "@astrojs/starlight"; import { unified } from "@astrojs/markdown-remark"; import { readFileSync } from "node:fs"; import { rehypeBaseLinks } from "./scripts/rehype-base-links.ts"; +import { rehypeTableShell } from "./scripts/rehype-table-shell.ts"; // Served from https://arbiterforge.github.io/codeArbiter/ — shared by the // `base` option below and the rehype plugin that base-prefixes markdown links. @@ -85,7 +86,7 @@ export default defineConfig({ // scheduled removal. `@astrojs/markdown-remark` is a direct dependency // because of this import — see site/package.json. markdown: { - processor: unified({ rehypePlugins: [rehypeBaseLinks(BASE)] }), + processor: unified({ rehypePlugins: [rehypeBaseLinks(BASE), rehypeTableShell()] }), }, integrations: [ starlight({ diff --git a/site/public/art/feature-forge.webp b/site/public/art/feature-forge.webp new file mode 100644 index 0000000000000000000000000000000000000000..2b4302b0ab640afe09e6432c6ecfca59d1c0f4bf GIT binary patch literal 68474 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≫ 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