CLAUDE.md · git:20260923.c79fe98 · 2026-09-23 · sha256 fefc57612cf6a648

CLAUDE.md git:20260923.c79fe98A

Immutable. This exact content never changes and is served at /api/v1/blob/fefc57612cf6a648.

# CLAUDE.md — ARIA

## What This Is

ARIA (Applied Reasoning and Insight Architecture) — an active knowledge and development discipline plugin for Claude Code. Three pillars: a five-phase knowledge lifecycle (capture → govern → promote → apply → refresh), decision discipline (Rule 22 enforcement at every edit), and codebase & task mapping (/codemap for repo traces, /stitch for cross-repo contract binding, /distill for task spec shaping). The "Applied" framing emphasizes the apply phase: trusted knowledge actively shapes the next decision via /context, /rules, /codemap, /stitch, /distill, and Rule 22 — not just stored and recalled.

**Repository:** GitHub (`mikeprasad/aria-knowledge`) — **public repo**

## Cowork Port (plugin-claude-cowork/)

The Claude Cowork port lives at `plugin-claude-cowork/` — consolidated into this repo in v2.20.0 (2026-05-24) from the previously standalone `mikeprasad/aria-cowork` (last standalone release: v1.1.0, 2026-05-19). **Current cowork release: v1.7.0** (2026-08-19, with aria-knowledge v2.46.3 — `/intake` absorbs `/clip`+`/clip-thread`+`/extract-doc` (breaking), `/audit-knowledge` gains Step 2f, `/interview` defaults to `guided`, working-rules preamble + `retrospect-patterns.md` land; 27 → 24 skills; tag `cowork-v1.7.0`, NOT marked Latest so the canonical release keeps the 6 stable aliases). Prior: v1.5.0 (2026-06-29, with aria-knowledge v2.38.0 — working-rules synced to canonical parity: added Rules 35/36/37, strengthened Rules 15/21; working-rules.md 34 → 37 rules; no skill change). Prior: v1.4.0 (2026-06-18, with aria-knowledge v2.31.0 — `/interview` skill ported, cowork-adapted; the one genuine parity gap with canonical v2.31.0, closed; summed-description cap-relief trim included). Prior: v1.3.0 (2026-06-11, with aria-knowledge v2.30.0 — `/foundational-review` + `/readiness-audit` ported; 2 audit aliases removed for cap relief). Prior: v1.2.0 (2026-06-10, with aria-knowledge v2.28.0 — `snap` mode parity; tag `cowork-v1.2.0` + GH release w/ `.plugin` asset). Prior: v1.1.5 (2026-06-04, with aria-knowledge ~v2.24.x — `/index` ephemeral-tag exclusion + `/wrapup` picker fix). Prior: v1.1.4 (2026-05-29, with aria-knowledge v2.20.3 — Opus 4.8 readiness: `working-rules.md` `Why` clause de-versioned, mirroring plugin-claude-code; Cowork is skills-only so the v2.20.3 hook hardening does not apply here). Prior coordinated cowork release: v1.1.3 (2026-05-25, with aria-knowledge v2.20.2 — wrapup/handoff spec fixes: closing-heading labeling correction + auto-mode extract-always-runs invariant). Both ports share the user's `~/Projects/knowledge/` folder and write to the same canonical config (`aria-config.md`) under an additive-only schema (per ADR-002). Edits to shared surfaces — `aria-config.md` field names, `template/rules/` content, `working-rules.md` rule numbering — must preserve cross-port compatibility. 27 skills (27 distinct; v1.3.0 removed the 2 aliases, v1.4.0 added `/interview`). Cowork-specific authoring constraints documented in `knowledge/guides/claude/cowork-plugin-validation.md`. **Cowork-specific release constraint**: aria-cowork release.sh enforces a 9000-char hard cap on summed SKILL.md description chars (empirical install-fail at 9233, documented v0.2.1 + v1.0.0); v2.20.1's trailing-parenthetical port-id uses a short form on cowork (`(Cowork variant — namespaced-only.)` ~36 chars) versus Code's verbose form (~96 chars), with the full ADR-094 narrative in the Runtime Gate body where no cap applies.

**Bidirectional feature flow (since v0.3.0 / v2.17.0):** Features may originate in either port and port to the other; plugin-claude-code remains the schema source-of-truth (output formats, knowledge-folder conventions, archive structures). v0.3.0's `/handoff brief` and `/intake doc` modes are the first cowork-originated features ported into aria-knowledge. See plugin-claude-cowork ADR-014 for the architectural rationale.

**Bare-slash ownership (ADR-094, v2.19.1):** When both ports are loaded in the same session (most common in Claude Desktop), 24 colliding skill names (`/handoff`, `/wrapup`, `/extract`, `/intake`, etc.) deterministically resolve to **plugin-claude-code** as canonical owner. plugin-claude-cowork's variants are namespaced-only (`/aria-cowork:handoff`, etc.). Each colliding skill carries a Runtime Gate in its body that **self-corrects**: invoked from the wrong runtime (Bash-availability is the fingerprint), it announces the mismatch in one line and redirects to the correct variant with the original arguments, without asking. ⛔ **As of v2.51.0 it does NOT ask, and there is no opt-out** — superseding ADR-094 §Part 2's `y`/`n` question and §Part 3's auto-mode exception, which existed only because the gate was a question. ⚠ A gate whose counterpart does NOT exist must never auto-redirect — it keeps a capability-precondition shape instead, since a redirect is only honest when a target exists. Edits affecting cross-port compatibility (description prepend conventions, gate clause text, anti-trigger language) should preserve this ownership rule. Full design: [`~/Projects/knowledge/projects/aria/decisions/094-bare-slash-canonical-owner-and-dual-runtime-gate.md`](../../knowledge/projects/aria/decisions/094-bare-slash-canonical-owner-and-dual-runtime-gate.md).

## Cursor Port (plugin-cursor-template/)

The Cursor port is a **repo skeleton** (not a Claude marketplace plugin). Users unzip `aria-knowledge-cursor-<version>.zip` into the root of their own project. **Current Cursor release: `2.46.2-cursor.0`** (2026-08-19, parity with plugin-claude-code v2.46.2; `.mdc` verified by recompiling `port-skills-to-mdc.py` and reproducing the committed files byte-for-byte). Prior: `2.30.0-cursor.0` (2026-06-11, parity with v2.30.0). Prior: `2.24.2-cursor.0` (2026-06-04, statusline-only alignment).

**What ships:** `.cursor/hooks.json`, `.cursor/rules/*.mdc` (5 compiled rule files), `.cursor/aria-knowledge.local.md`, root `AGENTS.md` (Cursor's equivalent of `CLAUDE.md`), `scripts/aria/*.sh`, and a root-level `knowledge/` mirror of `plugin-claude-code/template/`.

**Skill surface (27 commands in `aria-commands.mdc` + audits in `aria-audit.mdc`):** 22 core workflow commands + 3 aliases (documented in preambles) + 5 MCP-consuming skills (`/clip-thread`, `/extract-doc`, `/meeting-notes`, `/digest`, `/sync-decisions`) + `/help` + `/audit-share`. Includes v2.17.0 `/intake doc`, v2.20.2 wrapup/handoff auto-mode `/extract` invariants.

**Intentional Cursor divergences (do not port ADR-094 Runtime Gates per-skill):** aria-cowork is not loaded in typical Cursor sessions. A single preamble note in `aria-commands.mdc` replaces per-skill gates. Rule 22 uses an edit-intent marker (`record-edit-intent.sh`) — advisory `beforeFileEdit`, no transcript proof. No PreCompact; `stop` → `capture-task-boundary.sh` substitutes. MCP servers are user-configured in **Cursor Settings → MCP** (no bundled `.mcp.json` in the template). See `plugin-cursor-template/PORTING.md` and `plugin-cursor-template/audit/ARIA_CURSOR_AUDIT_REPORT.md` §5.

**Maintainer re-sync after canonical skill edits:**

```bash
python3 plugin-cursor-template/scripts/port-skills-to-mdc.py
./release-cursor.sh
```

`port-skills-to-mdc.py` strips Runtime Gate blocks, adapts paths (`AGENTS.md`, `.cursor/aria-knowledge.local.md`), and upserts MCP skill sections idempotently.

## Codex Port (plugin-openai-codex/)

The OpenAI Codex port lives at `plugin-openai-codex/` — an independent installable adapter that uses the shared ARIA knowledge folder and content schema while diverging on plugin manifest shape, hook registration, and tool-boundary behavior for Codex. **Current Codex release: `2.46.4-codex.0`** (2026-08-20; content parity with canonical, port suite 24/24. ⛔ Three canonical `post-*-check.sh` scripts were shipped here unwired and are REMOVED — Codex routes every hook through `bin/codex-hook.py`, which already inlines them as `tautology_message()`, `auto_prospect_message()` and `auto_retrospect_message()`. Do NOT re-copy them on a parity sync; see `PORTING.md`.) Prior: `2.30.0-codex.0` (2026-06-11, parity with v2.30.0 where Codex has an equivalent runtime surface). Wires MCP servers via `.mcp.json` (Codex manifest convention uses `mcp_servers` snake_case key vs. Claude's `mcpServers` camelCase — both are correct for their target runtime). Strips ADR-094 Runtime Gate sections from ported skills (gates are Bash-availability-fingerprint-dependent and don't fire in Codex). Ships `/foundational-review` + `/readiness-audit` with the bundled canonical process doc; keeps `/statusline` and `/aria-assist` as explicit non-equivalents for now (no Codex statusline/usage payload, no bundled launchd/headless scheduler path). Ships a port-specific test suite at `plugin-openai-codex/tests/run.sh` that release-codex.sh gates on (skills carry codex metadata, no ADR-094 gates leaked through, review skills present, `apply_patch` denial shape current, transcript reader doesn't scan without `turn_id`).

**Build with** `./release-codex.sh` from repo root. Reads version from `plugin-openai-codex/.codex-plugin/plugin.json` (single source of truth, strips `-codex.N` suffix for filename). Produces `aria-knowledge-codex-<canonical>.zip` + version-stable `aria-knowledge-codex.zip`. Verification gates: Codex port tests, port-drift report-only gate, MCP manifest present, `tests/` excluded from artifact, no junk, clean zip rebuild before archive creation.

## Antigravity Port (plugin-antigravity/)

The Antigravity IDE port lives at `plugin-antigravity/` — targets **Antigravity IDE** (VS Code fork) and **Antigravity CLI** (`agy`) from Google's Antigravity team. **Current Antigravity release: `2.46.2`** (2026-08-18, parity with plugin-claude-code v2.46.2; skill set identical to canonical's 36). Prior: `2.20.2` (2026-05-28, parity with v2.20.2; same-day refresh added native workflow + rules scaffolding and expanded plugin.json — commit `f635e61`). The setup skill's **Step 7ca** scaffolds `.agents/workflows/` (10 thin-shim workflows) and `.agents/rules/aria-rules.md` into the user's workspace, enabling **true native slash-command invocation** (`/setup`, `/handoff`, `/wrapup`, `/extract` fire as first-class Antigravity workflows) and **native Always-On rule enforcement** — distinct from Claude Code's hook-based enforcement model.

**Version sources (two, kept in sync by the maintainer):** `plugin-antigravity/plugin.json` carries the full standard plugin manifest including a `version` field (Antigravity's manifest schema ignores unknown fields, and the standard plugin shape is now used here for parity with sibling ports). `plugin-antigravity/version.txt` is a sidecar that `build.sh` auto-syncs from canonical `plugin-claude-code/.claude-plugin/plugin.json` — `bin/` scripts and the setup skill read from the sidecar. **Maintainer responsibility:** when bumping canonical version, also hand-bump `plugin-antigravity/plugin.json`'s `version` field. `build.sh` auto-syncs `version.txt` but does NOT touch `plugin.json` (it's in the preserved hand-authored files list per `build.sh` line 284). Candidate for `build.sh` automation later — flag as follow-up if the manual two-step becomes a friction point.

**Custom setup behavior is preserved via the overlay pattern.** `build.sh` (lines 120-145) copies canonical skills from `plugin-claude-code/skills/`, then applies `plugin-antigravity/overlays/skills/<name>/SKILL.md` overlays LAST — so port-specific bodies survive canonical resyncs. ⛔ **CORRECTED 2026-08-26 — this said "the setup skill currently has an overlay snapshot at `plugin-antigravity/overlays/skills/setup/SKILL.md` (byte-identical to the live file)". That file DOES NOT EXIST and there is no evidence it ever did.** Measured: `overlays/skills/` holds exactly three entries — `audit-config`, `audit-knowledge`, `snapshot`. So canonical's `setup/SKILL.md` flows straight through to this port with only the uniform path substitution applied, and there is no port-specific setup body to protect.

⚠ **Why that false claim mattered, and it is not a trivia correction.** It reads as "antigravity's setup is insulated from canonical", which is the opposite of true, and it was load-bearing while adding a Claude-Code-only step to canonical `/setup`: the natural fix looked like "edit the overlay", and there was none to edit. Creating one would have frozen antigravity's setup against every future canonical change — and its own drift detection is admitted future-work. The step was instead made **self-gating** on hook registration, which needs no overlay and is correct in every port. **Do not restore the overlay claim, and prefer a self-gating canonical step over a new overlay** unless a port genuinely needs a divergent body.

**Two-script model** (different from sibling ports' one-script model):
1. `plugin-antigravity/build.sh` — *regenerates* port content from canonical sources (copies skills from `plugin-claude-code/skills/`, applies overlays from `plugin-antigravity/overlays/`, syncs `version.txt`, patches setup/SKILL.md paths). Run when canonical sources change.
2. `release-antigravity.sh` (at repo root) — stages regenerated port content with junk exclusions and emits `aria-knowledge-antigravity-<version>.zip` + version-stable copy.

**Zip structure is flat (no top-level wrapper dir).** This differs from claude-code/codex/cursor zips which nest content under a plugin-name wrapper, matching Antigravity IDE's install machinery. `release-antigravity.sh` verifies this invariant (`wrapper` count must be 0).

## Project Structure

```
aria/
├── README.md          ← GitHub-facing intro
├── LICENSE            ← CC BY-NC-SA 4.0
├── CHANGELOG.md       ← Version history
├── CLAUDE.md          ← You are here
├── plugin-claude-code/            ← The installable plugin
│   ├── .claude-plugin/
│   │   └── plugin.json
│   ├── bin/           ← Hook scripts (bash)
│   ├── skills/        ← Skill definitions (SKILL.md files)
│   └── template/      ← Knowledge folder templates
├── plugin-openai-codex/      ← Codex port (independent installable unit)
│   ├── .codex-plugin/
│   │   └── plugin.json
│   ├── hooks.json     ← Codex hook registration
│   ├── bin/           ← Claude-standard scripts + Codex adapter
│   ├── commands/      ← Codex command entrypoints
│   ├── skills/        ← Copied ARIA skills (schema-compatible)
│   └── template/      ← Copied Claude-standard knowledge templates
├── plugin-claude-cowork/     ← Cowork port (skills-only; no hooks API in Cowork runtime)
│   ├── .claude-plugin/
│   │   └── plugin.json
│   ├── skills/        ← 26 skills (24 distinct + 2 aliases)
│   ├── template/      ← Knowledge folder templates (schema mirror of plugin-claude-code/)
│   ├── .mcp.json      ← 12 MCP servers across 4 categories
│   └── release.sh     ← Builds aria-cowork-<version>.plugin
├── plugin-cursor-template/   ← Cursor port (repo-skeleton, not a plugin install)
│   ├── .cursor/       ← Cursor-native config
│   │   ├── hooks.json
│   │   ├── aria-knowledge.local.md
│   │   └── rules/     ← 5 compiled .mdc files (27 commands in aria-commands + audits)
│   ├── AGENTS.md      ← Cursor's equivalent of CLAUDE.md (ships to user projects)
│   ├── QUICKSTART.md  ← Cursor-adapted quickstart
│   ├── PORTING.md     ← Maintainer drift tracking + skill→.mdc map
│   ├── knowledge/     ← Knowledge folder (hoisted to root, not under template/)
│   ├── scripts/aria/  ← Hook scripts (instead of bin/)
│   │   └── VERSION    ← Port version (e.g. 2.20.2-cursor.0)
│   ├── scripts/port-skills-to-mdc.py  ← Re-sync from plugin-claude-code/skills/
│   └── audit/         ← Frozen audit artifacts (2.16.1 baseline; see PORTING.md for current)
├── plugin-antigravity/         ← Antigravity IDE / CLI port (flat-zip install)
│   ├── plugin.json   ← Manifest (no version field per Antigravity docs)
│   ├── version.txt   ← Port version sidecar (source of truth — synced by build.sh)
│   ├── mcp_config.json
│   ├── hooks.json
│   ├── bin/          ← Canonical bin scripts + Antigravity adapter (bin/antigravity/)
│   ├── skills/       ← Regenerated from canonical via build.sh
│   ├── template/     ← Regenerated from canonical via build.sh
│   ├── overlays/     ← Per-skill overrides applied by build.sh
│   ├── build.sh      ← Regenerates port content from canonical sources
│   └── tests/        ← Port test suite (smoke + structural)
├── RELEASING.md                ← Build→publish flow + stable-alias asset contract (read before cutting a release)
├── publish-release.sh          ← Attaches all 6 stable aliases to the canonical release so site /latest/ links resolve
├── release.sh                  ← Builds claude-code zip (canonical)
├── release-codex.sh            ← Builds codex zip
├── release-cursor.sh           ← Builds cursor zip
├── release-antigravity.sh      ← Builds antigravity zip (flat layout)
└── docs/              ← Extended documentation (future)
```

## Key Conventions

- **`plugin-claude-code/` is the installable unit** — everything inside it is what users copy to their plugins directory
- **`plugin-openai-codex/` is the Codex installable unit** — independent adapter surface, same knowledge schema. Claude `plugin-claude-code/` remains the standard for template/content shape.
- **`plugin-cursor-template/` is the Cursor repo-skeleton** — not a plugin install. Users clone or unzip its contents into the root of their own project. Cursor compiles 29 commands (24 core + 5 MCP) into 5 `.cursor/rules/*.mdc` files because Cursor's Rules system doesn't have a one-skill-per-folder concept. Port version tracks canonical via `scripts/aria/VERSION` (currently `2.30.0-cursor.0`). Maintainer re-sync: `python3 plugin-cursor-template/scripts/port-skills-to-mdc.py`. ADR-094 Runtime Gates are omitted in Cursor. Knowledge folder schema stays compatible with `plugin-claude-code/template/`.
- **Template files** in `plugin-claude-code/template/` are either plugin-managed (diffable on `/setup`) or user-owned (created once, never overwritten). See `plugin-claude-code/skills/setup/SKILL.md` for the authoritative list.
- **Version** lives in `plugin-claude-code/.claude-plugin/plugin.json`
- **Hook scripts** in `plugin-claude-code/bin/` are bash — they read config from `~/.claude/aria-knowledge.local.md`
- **Skills** are markdown files — each skill is a `SKILL.md` with YAML frontmatter
- **Codex hooks** require Codex `plugin_hooks` enabled; the adapter reads `~/.codex/aria-knowledge.local.md` first, then falls back to `~/.claude/aria-knowledge.local.md`
- **`plugin-claude-cowork/` is the Claude Cowork installable unit** — sibling to plugin-claude-code/, both share schema-identical knowledge-folder outputs (per ADR-013). Cowork runtime is skills-only (no hooks API); enforcement is skill-embedded. Per ADR-094, bare-slash skill names resolve to plugin-claude-code as canonical owner when both ports are loaded in the same session; cowork-namespaced variants are `/aria-cowork:handoff` etc.
- **Cursor hooks** use `.cursor/hooks.json` and resolve script paths via `git rev-parse --show-toplevel`. Some Claude enforcement is weaker on Cursor (no transcript access, no documented pre-edit deny) — port uses an edit-intent marker file as the closest available mechanism. See `plugin-cursor-template/audit/ARIA_CURSOR_AUDIT_REPORT.md` §5.
- **`plugin-antigravity/` is the Antigravity IDE/CLI installable unit** — independent port targeting Google's Antigravity IDE (VS Code fork) and `agy` CLI. **Two version sources**: `plugin.json` carries the standard plugin manifest including `version` (Antigravity ignores unknown manifest fields); `version.txt` sidecar is auto-synced by `build.sh` from canonical and read by `bin/` scripts. Maintainer must hand-bump `plugin.json` on canonical bumps (`build.sh` preserves it). Two-script build model: `plugin-antigravity/build.sh` regenerates port content from canonical sources + applies overlays at `overlays/skills/<name>/SKILL.md`; `release-antigravity.sh` (repo root) zips. Zip layout is **flat** (no top-level wrapper dir) vs. sibling ports' wrapped layout. Setup skill's **Step 7ca** scaffolds Antigravity-native `.agents/workflows/` + `.agents/rules/` so slash commands fire as first-class workflows.

## Development Workflow

1. Edit files in `plugin-claude-code/`
2. To test, copy `plugin-claude-code/` to `~/.claude/plugins/marketplaces/local-desktop-app-uploads/aria-knowledge/`
3. Restart Claude Code to pick up changes

### Codex Port Workflow

1. Edit Codex adapter files in `plugin-openai-codex/`
2. Keep durable knowledge template/schema changes in sync with `plugin-claude-code/` — Claude remains the schema standard
3. Enable Codex plugin hooks with `codex features enable plugin_hooks` before testing automatic hooks
4. Install via `.agents/plugins/marketplace.json` or copy `plugin-openai-codex/` into a Codex local marketplace

### Cursor Port Workflow

1. Edit canonical skills in `plugin-claude-code/skills/` first (schema source-of-truth), then re-sync the Cursor port:
   ```bash
   python3 plugin-cursor-template/scripts/port-skills-to-mdc.py
   ```
2. Keep durable knowledge surfaces in sync with `plugin-claude-code/template/` — apply the same edits to `plugin-cursor-template/knowledge/` (root-level, not nested under `template/`). See `plugin-cursor-template/PORTING.md` §A for the lockstep file list.
3. The 5 `.mdc` rule files are *compiled* views of canonical `SKILL.md` bodies. `port-skills-to-mdc.py` **overwrites FOUR of the five** — `aria-commands.mdc` (core + MCP skills, wrapup/handoff patches, `/help` table), `aria-audit.mdc`, `aria-context.mdc` and `aria-rule-22.mdc` — and strips ADR-094 Runtime Gates. ⛔ **Only `aria-core.mdc` is hand-authored.** This sentence named all four of the others as hand-edit surfaces until 2026-09-14, which is a silent-work-loss trap: a hand edit to any of the three generated ones is reverted by the next documented re-sync, with exit 0 and no diff to notice. The authority is the script's own `RULES_DIR` constants, not this list — re-derive them before hand-editing any `.mdc`. ⚠ And a clean `git status` does NOT mean a generated port matches its generator: measured 2026-09-14, the worktree read clean while cursor was 473 lines behind canonical and antigravity was missing an entire skill (`audit-rules`). **Run the generator to find out; the release for 2.52.7 did not.**
4. Bump `plugin-cursor-template/scripts/aria/VERSION` (`<canonical>-cursor.0`) and run `./release-cursor.sh` from repo root. Zip: `aria-knowledge-cursor-<canonical>.zip`.
5. Users install by unzipping the artifact into the root of their own project, then restarting Cursor. End-user doc: `plugin-cursor-template/QUICKSTART.md`; project instructions file: `AGENTS.md` (not repo-root `CLAUDE.md`).

### Cowork Port Workflow

1. Edit Cowork-specific files in `plugin-claude-cowork/`. Most skills mirror `plugin-claude-code/` via ADR-013 schema-identical outputs; cowork-specific skills (`daily-audit`, cowork-modified `/extract-doc`, etc.) live only here.
2. Keep MCP-consuming skills (clip-thread, extract-doc, meeting-notes, digest, sync-decisions) byte-faithful between ports per ADR-014; plugin-claude-code remains schema source-of-truth.
3. Per ADR-094, when both ports load in the same session, bare-slash command names (`/handoff`, `/extract`, etc.) resolve to plugin-claude-code as canonical owner; cowork-namespaced variants are `/aria-cowork:handoff` etc.
4. Build with `./release.sh` in `plugin-claude-cowork/` — produces `aria-cowork-<version>.plugin`. Install by dragging into a Cowork conversation or via Settings → Plugins → Install from file.
5. Cowork runs as a skills-only plugin (no hooks API). The `/daily-audit` skill substitutes for SessionStart on first message.

### Antigravity Port Workflow

1. Edit canonical skills in `plugin-claude-code/skills/` first (schema source-of-truth). For Antigravity-specific adapter behavior, edit hand-authored files preserved by build.sh: `plugin.json`, `hooks.json`, `mcp_config.json`, `GEMINI.md`, `bin/antigravity/*`, `PORTING.md`, `README.md`, `SMOKE-TEST.md`. Place per-skill overrides in `plugin-antigravity/overlays/`.
2. Regenerate port content from canonical sources:
   ```bash
   ./plugin-antigravity/build.sh
   ```
   This copies skills/template from `plugin-claude-code/`, applies overlays, syncs `version.txt` from the canonical `plugin.json` version, and patches setup/SKILL.md to read version from the sidecar instead of JSON.
3. Bump `plugin-claude-code/.claude-plugin/plugin.json` version first (canonical), then re-run `build.sh` — `version.txt` syncs automatically. **Also hand-bump `plugin-antigravity/plugin.json`'s `version` field** — it's in build.sh's preserved hand-authored files list (line 284) and won't be touched automatically. Forgetting this creates silent drift between `version.txt` and `plugin.json`.
4. Build the release zip from repo root:
   ```bash
   ./release-antigravity.sh
   ```
   Produces `aria-knowledge-antigravity-<version>.zip` (flat layout) + version-stable `aria-knowledge-antigravity.zip`. Verification gates: `plugin.json` present, `version.txt` present, no top-level wrapper dir, no junk.
5. Users install per Antigravity IDE / CLI conventions; see `plugin-antigravity/README.md` and `plugin-antigravity/SMOKE-TEST.md`.

## Rules

- Follow the universal rules in `Projects/CLAUDE.md`
- **This is a public repository** — never commit personal information, API keys, secrets, credentials, internal URLs, or any sensitive data. Content here is visible to anyone on GitHub.
- **That rule is now mechanically enforced — `release.sh` Gate D (`plugin-claude-code/bin/check-public-hygiene.sh`, added 2026-08-25) is FATAL.** It fails a release when a private project identifier appears in **tracked** content. Why it exists: a repo-wide census that day found **113 occurrences across 35 files in all five ports** — client project directory names, a private domain with its file count, design-system artifact names inside the shipped Rule 22 template, and a portfolio inventory with per-repo commit counts. None malicious; every one entered as a worked example in a design doc or test fixture, and nothing connected "internal name" to "public artifact". ⛔ **Exit 2 means the gate's own self-test failed — that is NOT a clean result** and `release.sh` dies distinctly on it. ⚠ **Its bound, stated in its header: it detects the identifiers in its TERMS/CODES/ARTIFACTS lists, so a NEW client project is invisible until added there — add it when one appears.** Placeholders this repo uses: `proj-a` / `proj-b` / `proj-c`. ⚑ Three instrument lessons are baked in because each cost a re-census: patterns are **boundary-anchored** (an unanchored two-letter code plus a slash also matches the tail of `docs/` — 499 of those here). ⚑ Note this bullet cannot quote the banned forms literally: the gate scans every tracked file, so a specimen written here is itself a finding — which is how this very line first failed the gate, the scan is **case-insensitive** (the last real leak referenced two private projects by their UPPERCASE initials, which every case-sensitive sweep before it missed), and it scans **tracked files only** (an untracked scratch tree produced ten hits that could never ship, which is the noise that gets a gate muted).
- The plugin's own template content (working-rules, change-decision-framework, enforcement-mechanisms) is both shipped content AND documentation of how the plugin works — edits to these have dual impact
- Bump version in `plugin.json` when making release-worthy changes

## Knowledge Repository

Project-specific architecture decisions live in `~/Projects/knowledge/projects/aria/`:

- `decisions/002-knowledge-extraction-architecture.md` — task-based /extract + audit promotion model
- `decisions/006-full-rule22-format-every-edit.md` — full format on every edit (no compression)
- `decisions/008-skill-knowledge-connections.md` — skill-knowledge connection discovery + drift detection

Cross-project knowledge that applies to ARIA:
- `knowledge/rules/working-rules.md` — the 37 universal rules (ARIA's source of truth ships in plugin-claude-code/template)
- `knowledge/rules/change-decision-framework.md` — Rule 22 framework
- `knowledge/rules/enforcement-mechanisms.md` — enforcement tier model
- `knowledge/guides/claude/plugin-development.md` — Claude Code plugin patterns
- `knowledge/guides/claude/cowork-plugin-validation.md` — Claude Cowork plugin patterns (sibling guide; relevant if coordinating with aria-cowork or shipping a Cowork-side plugin)
- `plugin-cursor-template/PORTING.md` — Cursor skill→`.mdc` compilation map and drift tracking
- `plugin-cursor-template/audit/ARIA_CURSOR_AUDIT_REPORT.md` — Cursor enforcement-gap matrix (§5)

Pre-staged ADR candidates live in `~/Projects/knowledge/intake/decisions-backlog.md` — check there for what's currently queued for next `/audit-knowledge`. Themes queued here historically drift as ADRs promote; the live backlog is the source of truth.

Use `/context aria` to load relevant knowledge by project tag.

---

*Session 2026-09-15 — **the session-ledger status vocabulary and prune receipts: COMMITTED + PUSHED (`32a47af`), UNRELEASED** (no version bump; `plugin.json` still reads 2.52.8, so none of this is in an installed artifact yet — only in the working tree and, for claude-code, hand-synced into the installed copy). Three changes, all in `bin/lib-session-state.sh` across the three ports that carry ledger functions (⛔ **codex is correctly EXCLUDED — measured, it ships none of the `kt_ss_ledger_*` functions**). **(1) `kt_ss_ledger_mark_superseded`** — a human retiring an entry that was never *consumed* by a resuming session had no sanctioned verb, so they hand-typed a status; that free text was reaped by **nothing** (prune keys on a word-bounded `consumed`) and accumulated forever, while the reader classified it LIVE so removing it by hand read as a destroyed handoff. ⛔ **The emitted token CONTAINS the word `consumed` on purpose — do not "improve" it to a bare `superseded`**, which matches neither prune nor `mark_consumed`; verified two-sided. ⛔ **No free-text reason in the header** — the header is a machine-parsed ` · ` field and unstructured text there IS the defect; the pre-mortem caught the first draft doing exactly that. **(2) prune receipts** — `kt_ss_ledger_prune` now records what it reaped to `~/.claude/session-ledger-receipts` (override `KT_SS_RECEIPTS`), so the reader can tell a legitimate prune from a destruction when it never observed the terminal status. ⛔ **The census is OUTSIDE the awk deliberately: that awk's STDOUT IS THE NEW FILE, guarded by `> tmp 2>/dev/null && mv`, so a receipt write inside it puts the receipt path in prune's own failure path — a failed write makes awk exit non-zero, `mv` never runs, and with stderr discarded PRUNE SILENTLY STOPS PRUNING.** It is computed as before-minus-after and appended only after the mv commits; that is also the EXACT set, since "headers carrying a terminal token" over-estimates (the `hasterm`/`## `-reset branches mean some marked entries are not reaped, and a receipt for a survivor would later suppress a real loss). Receipts are **advisory-only** — they can suppress a finding, never create one — so every failure mode degrades to a false positive, never a missed destruction. **(3) cursor's `mark_consumed` ported** — it still carried the pre-2026-08-27 anchored form and silently matched nothing for a parenthetical after the sid, a bold status, or a trailing title; all three now rewrite and reap. ⛔ **Comments must stay OUTSIDE the single-quoted awk program — an apostrophe in prose TERMINATES it** (broke the file once here). ⚠ **`tests/repros/session-state.sh` was writing the user's REAL receipts file** on all 11 of its prune calls (20 of 22 live entries were its temp paths); fixed by exporting `KT_SS_RECEIPTS` into the scratch dir, proven by in-place mutation (42→52 without, 52→52 with) — ⛔ note the obvious control is DEAD: a copy-to-`/tmp` of that suite exits 1 at line 12 because it resolves its lib path relative to itself, and never reaches a prune call. ⚑ The miss generalises: the sibling checker suite already guards the identical hazard for its **watermark**, but that guard is scoped to the watermark, so a second global-state file was uncovered — **when a helper gains a NEW side effect on global state, census its CALLERS, not just its author.** ⛔ **CORRECTED 2026-09-15 — the fix shape recorded here was WRONG and implementing it literally would have been HARMFUL. Do not restore it.** The claim was that a demoted entry vanishes from the `(sid|ts)` key while its content is preserved, and that *"the demote path should write a receipt as prune now does"*. **Measured against a fixture: `kt_ss_ledger_add` is PURELY ADDITIVE** — entry keys 1 → 2, the pre-existing key survived untouched, no receipt written, correctly, because nothing was removed. So a receipt emitted there would name a key that **SURVIVED**, and `kt_ss_ledger_prune`'s own contract comment states the cost: *"A receipt naming an entry that SURVIVED would later suppress a genuine loss report for it."* The literal fix does not merely fail to help — it **blinds the detector to a future real destruction** of that entry. ✅ **The obligation moved to the PROMOTE path** (spec D5 step 3, Unit C), which is the operation that actually removes a key; and the provenance token (D4, shipped) largely dissolves the symptom by making the demote restore the ORIGINAL sid + `at` so the key reappears rather than being replaced. Killed as J1 in `knowledge/logs/auto/2026-09-15-handoff-token-merged-judgments.md`, **accepted by Mike 2026-09-15**. ⚑ The 2026-09-14 symptom report was accurate; only its prescribed fix was not. Gates: `knowledge/logs/prospect/2026-09-14-file-{d3-ledger-vocabulary,prune-receipts}.md`; retrospect `…/logs/retrospect/2026-09-14-range-session-ledger-vocabulary-and-receipts.md` (closed); ledger `…/logs/auto/2026-09-14-d3-ledger-vocabulary-judgments.md` (3 entries, all accepted).*

*Session 2026-09-17 — **v2.53.2 SHIPPED + PUSHED + GH-RELEASED + ARTIFACT-VERIFIED** (`origin/main`=`b18f7ce`, annotated tag `v2.53.2` dereferencing to it, `/releases/latest`=v2.53.2, 6 assets, all 5 stable aliases 200 against a **firing 404 control**, published zip **byte-identical** at 622,167 B and content-checked inside — manifest 2.53.2, `consume_active` 1, `read_active_token` 1, `mark_superseded` on `index()` 2, post-edit auto-consume **invocations 0**). ⛔⛔ **HANDOFF CONSUMPTION HAD NEVER ONCE BEEN RECORDED, AND NOTHING SIGNALLED IT.** `kt_ss_ledger_mark_consumed` rewrites only `^### ` headers and the ACTIVE prompt has none, so the documented resume path called the one function that structurally could not work — matched nothing, changed nothing, returned 0. Measured on a real ledger: **142 entries, 0 consumed, 142 unconsumed**; and since `prune` reaps only a word-bounded `consumed`, **nothing had ever been prunable** (that file reached 636,898 chars with 53 stale handoffs still offered as live). ⭐ **The corpus census is the instrument — a single observation cannot see this**, because every individual no-op is indistinguishable from "nothing to do". ✅ **`kt_ss_ledger_consume_active`** (demote → mark consumed → clear the active slot) + **`kt_ss_read_active_token`** (token wins over front matter). ⛔ **Clearing the active slot is REQUIRED, not cosmetic: `token_locate`'s ACTIVE branch prints the LITERAL string `unconsumed`**, so a prompt left there is re-offered forever whatever is written elsewhere. ⛔ **TWO PRE-EXISTING SILENT DEFECTS, both found by testing, both returning 0: (1) `kt_ss_ledger_add` DISCARDED the entry whenever the section heading was the file's LAST LINE** — the splice awk opens its tail file only on reaching a line *after* the anchor, so `cat` exits non-zero, the `&&` short-circuits and `mv` never runs (two-sided: heading-last 0 entries, heading-with-anything-after 1). Latent then, but load-bearing now, because `consume_active` clears the active slot after calling it — on that path a silent discard **destroys the handoff**; fixed in all three carrying ports, which the parity contract requires. **(2) `kt_ss_ledger_mark_superseded` still used the retired unescaped-regex matcher WHILE ITS OWN COMMENT CLAIMED IT MIRRORED `mark_consumed` VERBATIM** — asked to retire sid `a.c` it also retired the unrelated `abc`, and it matched the whole line rather than field 1. ⚑ **The false parity claim is what let it survive: an assertion that two things agree is exactly what stops the next reader checking.** Now mirrors it exactly, incl. the optional 5th `at` — load-bearing, since 12 sids on one real ledger carry two timestamps and 8 mix a retirable entry with a LIVE one. ⭐ **`consume_active` verifies the demote LANDED before clearing anything** — `ledger_add` returns 0 whether or not it wrote, so its exit code carries no information; with `ledger_add` neutered the prompt stays put, and without that guard the first fixture lost it outright. ⛔ **The edit-time auto-consume in `post-edit-check.sh` is REMOVED (Mike's ruling: no new pre/post-edit hook checks, session start only).** It inferred *"someone resumed this prompt"* from *"someone edited a file here"*, fired on 1 of 62 live ledgers, could not reach the active prompt at all, and passed no `at`. Consumption now happens ONLY on the session-start path, gated on a token actually present in the pasted opener. The always-on digest and the SessionStart hook had also drifted into describing **two different resume procedures** (the digest carried the pre-token one) and now agree. ⚠ **`plugin-antigravity` gained the new functions BY REGENERATION, not by a porting decision** — an earlier pass hand-edited its lib and `port-generation-idempotence` correctly rejected it: that port is a copy of canonical and `bin/` is not in `build.sh`'s preserved list. Cursor is unaffected (hand-maintained lib); codex ships no ledger functions; neither was rebuilt, so their prior stable aliases were reused per `RELEASING.md`. ⚑ **The suite caught a constraint that lives nowhere in the source it governs** — `kt_ss_ledger_add` is under a 3-port byte-parity contract named only in `tests/repros/session-state.sh`, so a fix landed in one port silently diverges the others. ⛔ **My own leak, self-caught and fixed: `bb420d7` put a private project identifier into this PUBLIC repo twice** (plan doc); gate D reports commit-message coverage as `0 commits, NOT CHECKED` until a commit exists, **so running it before committing structurally cannot see that class** — run it AFTER. Forward-fixed in `539a550` rather than amended (shared worktree), so the identifier remains in `bb420d7`'s blob — already pushed, and per the standing rule a hole in a pushed commit is DOCUMENTED, not rewritten. Suites bare exit 0: session-state 92, plugin-claude-code 332, port idempotence 5. ⚠ **OPEN:** `consume_active` is fixture- and artifact-verified but **never yet exercised against a real paste** — the first genuine signal is the next handoff opener pasted into a fresh session after reinstall; `CODEMAP.md` is **22 days** old against a 14-day threshold; and `SESSION.md` here stays **gitignored and unstaged despite `session_state_tracked: true`**, because this repo is PUBLIC — ⛔ do NOT "fix" that by deleting `.gitignore:17`.*

*Session 2026-09-16 — **v2.53.0 then v2.53.1, both SHIPPED + PUSHED + GH-RELEASED + ARTIFACT-VERIFIED** (`origin/main`=`b0a848f`, `/releases/latest`=v2.53.1, 6 assets, all 5 stable aliases 200 against a firing 404 control, published zip byte-identical at 616,735 B and content-checked inside: manifest 2.53.1, private identifiers **0** with a firing positive control, widened gate present, third self-test arm present). **The arc:** a handoff reported destroyed by `kt_ss_ledger_prune` — prune exonerated two-sided, and the report wrong on 3 of 4 claims. Real mechanism: the active prompt's ONLY attribution channel is the front-matter, which `kt_ss_mark_inprogress` rewrites on a session's first edit while passing the body through, so a prompt gets permanently stamped with another session's identity. Five units shipped: the `at`-ordering guard with self-demote (fail direction deliberately conservative), `mark_consumed` fixed from a REGEX match to a literal `index()`, the provenance token in the opener fence (tested BEFORE the fence skip, since it lives inside one), `/handoff resume` + picker, and port parity. ⛔ **REPORTED NOT FIXED: codex references `kt_ss_ledger_add` twice in `handoff` and once in `wrapup` and ships NO ledger functions** — porting the guard there would add a second call to a helper that does not exist.

⛔⛔ **THE FINDING WORTH CARRYING: GATE D WAS BLIND TO THE PATH FORM, WHICH IS THE FORM THE IDENTIFIER ACTUALLY TAKES.** Its `CODE_RE` boundary class excluded a slash, so it fired on the space-prefixed form and stayed silent on a home-relative `Projects/<code>/CODEMAP.md`. **Both shipped self-test arms passed the whole time and neither could see it** — one pins the space form firing, one pins an innocent lookalike staying silent; there was no arm for a path. The exclusion is in the gate's FOUNDING commit, not a repair for any measured false positive. ⭐ **Found by verifying the PUBLISHED v2.53.0 artifact, not by any gate** — an ad-hoc grep with a narrower exclusion caught 5 occurrences the sanctioned gate passed; 3 were the gate's own self-excluded specimen, 2 were real and had shipped since 2.52.8. Widening adds **22 findings / 10 files / ZERO false positives**; ⚑ two of those lines were already HALF-genericized, which is what a blind spot looks like from outside — an earlier cleanup ran and missed exactly what the gate could not see. Mutation-verified two-sided, and the OLD pattern is silent on the same mutation, so the widening is load-bearing. A third self-test arm now pins the path form. ⚠ **Gate D's other structural bound, unchanged and worth knowing: it scans `origin/main..HEAD` ONLY, so commit messages on any other local branch are invisible** — a backup branch created during this work held all 5 violations while the gate read clean.

⛔ **MY OWN LEAK, PUBLIC — RULED LEAVE-IN-PLACE by Mike 2026-09-17, do NOT reopen it: commit `776aff2`'s MESSAGE carries 4 occurrences across 3 lines** (3 path-form plus one quote-prefixed, which the OLD gate could already see). ⚑ The footer said *"3 path-form occurrences"* until 2026-09-17 — that figure came from `grep -c`, which counts LINES, so it was right-looking and wrong in both its value and its unit. Cause is procedural, not analytic — I pushed it WITHOUT building first, so gate D's commit-message arm never ran on it. **Build BEFORE pushing is not a style preference; it is the only thing that scans a commit message.** Standing rule says a hole in a PUSHED commit is DOCUMENTED, not rewritten (never force-push `main`), and Mike ruled exactly that: **leave them.** The cure — re-SHA-ing 4 commits and force-pushing `main` — costs every clone's view to remove four directory shorthands from prose that is already public. ✅ **CLOSED 2026-09-17 — the `v2.53.0` tag was LIGHTWEIGHT and is now annotated** (`ref=7052779` dereferencing to `ddb6ea8`, the same shape as `v2.52.8` and `v2.53.1`). The published release survived the rewrite untouched: 6 assets, not a draft, `/releases/latest` still `v2.53.1`, download links 200 against a firing 404 control. ⚠ The agent could not do it — the permission layer denied `git push --force` **four** times regardless of Mike's authorization, so Mike ran it. ⛔ Do NOT route around such a denial with an equivalent syntax (`+refs/tags/...`); hand over the command.

⚑ Two instrument errors this session, same family, both self-caught: a commit scan over `%s` (subject) reported 0 where the gate reported 5 because the hits were in `%B` (body); and a leak count piped `grep -rh` (filenames suppressed) into a filter keyed on the FILENAME, so the filter was inert and the gate's own specimen was counted as a live leak. **In both, the filter and the data were in different units.**

*
*Last reviewed: 2026-09-14 — plugin-claude-code **v2.52.8** (**SHIPPED + PUSHED + GH-RELEASED + ARTIFACT-VERIFIED**; `origin/main`=`1b559c6`, annotated tag `v2.52.8` dereferencing to it on the remote, `/releases/latest` = v2.52.8, 6 assets, all 5 stable aliases 200 against a **firing 404 negative control**, published zip byte-identical to the local build at 606,113 B and content-checked inside — manifest `2.52.8`, ledger fix present, retired `ls -t` idiom 0, home-path leak 0.) ⛔ **THE RELEASE EXISTS BECAUSE 2.52.7 RAN NEITHER PORT GENERATOR AND NOTHING SAID SO.** `plugin-antigravity/build.sh` and `plugin-cursor-template/scripts/port-skills-to-mdc.py` are the documented re-sync steps; both port worktrees read **clean** the entire time. ⭐ **A clean `git status` answers "has anyone edited this?", never "does this GENERATED file match its generator?"** — running them changed 38 files: cursor was **473 lines** behind (the `/audit` four→six sub-audit expansion, the `/audit <verb>` canonical-form migration, argument passthrough, the 09-10 `/index` tag-provenance rules) and antigravity was **missing an entire skill** (`audit-rules`). ⚑ **The generator also caught a 2.52.6 defect no test could reach:** `0b0cc45` hand-mirrored `pm-morning-run.sh` into antigravity, updating the candidate-path CODE at line 41 and leaving the COMMENT at line 33 naming the old path — nothing tests a comment, and the uniform substitution fixed both because it does not distinguish code from prose. ⇒ **run the generator; never hand-patch a generated port.** ⛔ **Fixed here: this file told maintainers to hand-edit FOUR `.mdc` files and `port-skills-to-mdc.py` overwrites THREE of them** (`RULES_DIR` constants are the authority; only `aria-core.mdc` is hand-authored) — follow the old text and your edit vanishes at exit 0. ⛔ **Two version records had gone stale silently:** `plugin-antigravity/plugin.json` read **2.48.2** against a `version.txt` sidecar reading 2.52.7, because `build.sh` auto-syncs the sidecar and PRESERVES the manifest as hand-authored — two version sources, one automated and one manual, fails exactly this way; and `PORT-LEDGER.json` had not moved since v2.46.4 (2026-08-20), the source of the drift checker's "synced to 2.46.4" lag line. ⚠ **`plugin-openai-codex` and `plugin-claude-cowork` did NOT move, for the same reason the other two did** — codex is hand-synced with genuine drift across ~10 skills (a bump would assert a parity that does not exist) and cowork is on its own `cowork-vX.Y.Z` cadence with 32 files changed since `cowork-v1.7.0`. The versions that moved are exactly the two whose generators were run and verified. ⚑ **Instrument warning, worth three wrong readings in one session: `check-port-drift.sh`'s per-file `drifted` is NOT a staleness signal for a generated port** — codex strips ADR-094 gates, antigravity rewrites paths and frontmatter, so those files differ from canonical BY CONSTRUCTION and always will. Only running the generator, or the `PORT-LEDGER.json` watermark, answers staleness. ⚠ **OPEN:** codex's ~10-skill drift is unaddressed; cowork has 32 unreleased files; `CODEMAP.md` is **19 days** old against a 14-day threshold; and `SESSION.md` here stays **gitignored and unstaged despite `session_state_tracked: true`** — this repo is PUBLIC and the global knob cannot express per-repo intent, so the skill's true-branch would publish session state. ⛔ Do not "fix" that by deleting `.gitignore:17`.*

*Session 2026-09-11 (later) — plugin-claude-code **v2.52.6** (**SHIPPED + PUSHED + GH-RELEASED + ARTIFACT-VERIFIED**; `origin/main`=`b9f03ba`, annotated tag `v2.52.6`=`c7d094a` pinned there, 6 assets, all 5 stable aliases 200. Verified BY MARKER on the downloaded artifact: byte-identical to the local build at 599,295 B, and its `skills/{wrapup,handoff}/SKILL.md` read the retired ledger claim **0**, the bypass read instructed **1**, the demote conjunct **0**. ⭐ **Built BEFORE pushing on purpose** — gate D's commit-message arm read `(2 commits)` where the v2.52.5 build read `= 0 commits, NOT CHECKED`, which is that fix verified two-sided on real release runs. ⚠ v2.52.5 shipped the FALSE 'no ledger' claim; 2.52.6 is what corrects it, so treat 2.52.5 as superseded rather than merely older. ⚠ The INSTALLED tree is a separate question this release does not answer — `committed-is-not-installed`.)*

*Session 2026-09-10 — plugin-claude-code **v2.52.1** (**SHIPPED + PUSHED + GH-RELEASED + LIVE-VERIFIED**; `origin/main`=`acd434c`, `ls-remote` SHA match + 9 per-commit ancestor checks with a firing negative control; annotated tag dereferences to `acd434c` on the remote; `/releases/latest` = v2.52.1, 6 assets, all 5 stable links 200 against a 404 control, published zip **byte-identical** at 589,744 B and content-checked inside — `hasterm[opened]` present, manifest `2.52.1`, retired claim 0, private path 0, leftovers 0): **`kt_ss_ledger_prune` was destroying a section heading and everything under it.** An unterminated CONSUMED entry had no boundary but the terminator it was missing, so a `## ` heading in between was swallowed — the class that deleted two unconsumed handoffs on 2026-09-04. ⛔ Three simpler fixes are FALSIFIED and recorded at the code: the old unconditional `/^## /` reset LEAKS, an unconditional `if (drop) next` swallows a live handoff (M9c exists to fail it), and closing the pass-1 span at any column-0 heading is FENCE-BLIND. What ships discriminates per entry — pass 1 records which entries HAVE a terminator, and the recovery is scoped to those that do not. ⛔ **The instruction that caused the damage said the opposite:** */handoff* 3f claimed a stored prompt *"may safely contain column-0 `## ` lines"* — TRUE of the shell helpers, FALSE of every heading-scoped reader, and a caller acted on it, putting three column-0 headings inside one entry and 75 downstream entries outside the ledger. ⚑ The structural alternative (fence the prompt in `add()`) is FALSIFIED on the measured payload: the broken body already contains a fence, so a conditional wrapper no-ops. ⭐ Porting surfaced that **`plugin-cursor-template` was TWO fixes behind** — no `is_entry_header()` at all — so the parity arm is now a function LIST, mutation-verified by restoring cursor's REAL pre-port function. ⚠ **Three of this session's own mutations were UNFAITHFUL and each was caught only by proving the mutation CREATED the condition**: one hit a comment that the comment-normalised cksum strips, one matched a word case-insensitively and was satisfied by descriptive prose while the imperative it guarded was gone, and one never applied at all. ⛔ **Gate D caught a private workspace path in THREE shipped skill files that every other green check had passed over** — a suite verifies behaviour and nothing in it looks at what the text discloses. ⚠ **Known and NOT fixed:** `plugin-openai-codex` ships 2 of 7 `kt_ss_*` functions and has NO ledger while its handoff skill instructs calling one, so on that port the demote cannot run and the rewrite destroys a prior pickup; and `post-edit-check.sh` still stamps `sessionId` over another session's prompt without demoting. Both are filed with full option sets; all four non-canonical ports read `synced to 2.46.4 → canonical 2.52.0`, `sla=undeclared`, which is the condition underneath both.*

*Session 2026-08-26 (later) — **the cap is not a constant, and that is why this arc did not converge.** Read out of the LIVE binary (`~/.local/share/claude/versions/2.1.245`, Bun-compiled with the JS embedded; ⛔ the homebrew `cli.js` is **2.0.8** and reading it would have been the same wrong-unit error a third time): `Rae(toolName, maxResultSizeChars, ceiling, skipAggregate)` returns `min(maxResultSizeChars, ceiling)` unless a **per-tool override map keyed by TOOL NAME** — behind the gate `"tengu_velvet_ibis"` — supplies a value. ✅ `K5 = 2000` and `V5 = "<persisted-output>"` **confirmed**. ⇒ **The threshold is PER-TOOL and (strong inference, not measured — `tengu_*` is this binary's gate namespace and no settings key exists; a gate fetch was not observed) REMOTELY MUTABLE.** Three consequences: it is *structural* confirmation that the Bash-tool probes never transferred (corroborated in-session — a **26,389 B** Bash result came back whole while a **19,557 ch** hook payload was wrapped, and later a 47.7 KB Bash result WAS wrapped, bracketing that consumer separately); the gate is **not a user lever**; and ⛔ **no payload may be sized against the cap, so locating it exactly would not make a hook design safe.** ⭐ **The composition measurement that replaces the four candidate designs:** the emission is assembled at `bin/session-start-rules.sh:35-39` and is **61% static / 39% computed** — static digest 11,960 ch (**12,166 B, already under probe A's proven 13,101 B** — ⚠ and that comparison is exactly the reasoning the 2026-08-27 correction below invalidates: "under the proven floor" was treated as the safety test, when the floor was never the constraint) plus 7,598 ch of blocks computed from `autonomy`, `session_state`, the project tag index and `user-rules.md`. ⛔ **So option Z is NOT a superset of the hook channel** — a file written once cannot carry a config-conditional block — and the split is by **MUTABILITY**, not by audience (§8.1) or fidelity (§10.3): the file carries the *behaviour*, the hook carries only the resolved *values*, which makes hook truncation non-damaging instead of cap-fitted. ✅ **PROBE RESULTS.** Probe A: all eight sentinels incl. the tail at 13,051 ⇒ the user-scope `~/.claude/rules/*.md` channel carries **13,101 B** (was 2,338 B, a 5.6× improvement). ⛔ **SUPERSEDED — do not quote this as the channel's capacity. 13,101 B is a FLOOR: the largest payload probe A happened to test.** A probe that fully delivers proves the channel carries *at least* that much and says nothing about the ceiling. ⛔⛔ **The live defect this file had until 2026-08-27 was not a missing measurement — it was PROPAGATION: probe C's result is recorded in `docs/superpowers/plans/2026-08-26-rules-file-hook-split.md:253` as "the channel at 32,056 B in one file and 34,394 B aggregate", and 32,056 already appears elsewhere in THIS footer — so both numbers sat in one always-loaded file with the stale one stated as the capacity.** A stale sentence reads exactly like a current one. **Authoritative figures: 32,056 B single-file, 34,394 B aggregate (probe C).** ⚑ Independently corroborated 2026-08-27 by a different instrument — not a sentinel-gradient probe file but the running session's own context: **29,875 B across three files delivered in full**, verified by three **tail sentinels**, one per file, *including the last file in alphabetical load order* (which is what rules out an aggregate budget exhausting mid-directory), plus a **38-of-38 rule census with section membership** on the 21 KB `aria-rules.md` (Coding 1–18 · Process 19 20 21 22 27 36 37 · Meta 23 24 25 26 28 29 30 31 32 33 34 35 38), which catches a mid-file hole *and* a reorder that tail sentinels alone cannot. **29,875 is LOWER than probe C's figure and is not a replacement for it** — its value is being obtained a different way, at zero cost, from a session's delivered context rather than from an armed probe file. ⚠ Every one of these is a floor, and per this same arc's binary read the cap machinery is per-tool and plausibly remotely mutable, so any figure here measures a moment. ⛔⛔ **AND THE SHARPER FINDING, which is not about size at all: this channel loads ONCE at session start, so a rule added mid-session is silently absent from every running session** — measured live, a parallel session grew `user-rules.md` from 19 to **25** U-rules at 20:19 and **U20–U25 were not in the running session's context**, with no marker of any kind. That is a *temporal* gap, and unlike the digest's 240-byte truncation (which appends `…`) it announces nothing. It also means the directory now stands at **31,822 B, above the verified figure and unverified.** Probe B: `PROBE-B-QX7K` **absent** ⇒ the `claudeMd` settings key is **inert at user scope and fails SILENTLY** — no warning, no error, no log line; readable only because probe A was the positive control in the same arm. ⛔ **A claim made and retracted inside this one session: "Z is unproven by 51%" compared probe A's capacity against the hook's *composed* emission rather than against what a file would carry (12,166 B). Same wrong-unit shape as probe A itself, in the session that named the pattern.** ⛔ **RULED (Mike): the file channel is `~/.claude/rules/`, not the `CLAUDE.md` `@`-import** — accepting one probe session in exchange for never writing the user's repo. ⏳ **PROBE C ARMED** at `~/.claude/rules/_probe-c-size.md`, **32,056 B**, nine markers at **703 / 4,052 / 8,067 / 12,008 / 16,023 / 20,038 / 24,053 / 28,068 / TAIL 32,009** — deliberately ABOVE the ~25,800 B worst case so a pass settles the channel permanently. ⚠ It costs ~8k tok in EVERY session in EVERY project until deleted: `rm ~/.claude/rules/_probe-c-size.md`. ✅ **AC1's GUARD IS BUILT** (`eb82199`) — 11 assertions in `plugin-claude-code/tests/test-aria-rules-digest.sh`: a positive control on the needle, a deterministic worst-case fixture proven to carry all six conditional blocks, a downward-only ratchet at **20,322** normalised codepoints, and the last-rule-title check on the minimal AND worst-case paths. Suite **213 passed / 0 failed, bare exit 0**. ⛔ **The FIRST version survived its own mutation:** it counted raw characters, and the payload interpolates the knowledge-folder path **3 times**, so the total tracked the temp-dir path length (21,074 at a 144-char path vs 20,942 at 100 — exactly 3× the difference); `wc -m` was no better, counting bytes under `LC_ALL=C`. The measure is now jq's literal string split, path- AND locale-independent. ⚑ **Four of seven mutation attempts were unfaithful** (a no-op insert, a `sed` that never matched, and twice truncating a variable after its value had been copied out) — every one caught by proving the condition was created, **none by re-reading**. ⛔ **NEW DEFECT FOUND, not fixed, Mike's call: `bin/pre-bash-write-check.sh`'s temp-path exemption tests whether the COMMAND STRING MENTIONS a temp path, not whether the MUTATION TARGET is one.** Proven three ways: `sed -i` on a tracked `.sh` **warns**; the identical edit in a command that also references a scratchpad path is **silent**; `cat >>` into a tracked `.sh` **warns**. ⚑ The irony is what makes it matter — **backup-to-scratchpad-then-mutate is the safe pattern this workspace's own discipline mandates**, so doing the careful thing silences the hook, and its measured 0.674% fire rate is therefore an **underestimate** (the corpus cannot have counted what the hook was blind to). It is a false NEGATIVE, so the KILL-not-loosen rule (which governs false positives) does not bind; the fix is narrowing the exemption to the resolved target. ⚠ Also: the harness's own bash-first directive rides along with **auto mode** (`steerOnly`) — same instruction block also serves bypass-permissions mode and a `bashFirst` flag — so under auto mode it actively pushes the shell edits that trip that hook. Commits `eb82199` (guard) + `f1fcc29` (spec). ⚠ **19 ahead of `origin/main`, NOT pushed.** ⚠ CODEMAP.md is **37 days** old against a 14-day threshold.*

*Session 2026-08-26 (later still) — **`/auto`'s modifier surface carried two defects; both fixed in canonical, neither live.** ⛔ **F2 — three knobs documented in Step 5 were in no parser list.** `workflow`, `fanout=<pct>` and `agents=<N>` are described there as invocation overrides for the three fan-out stopgaps, and appeared in no modifier bullet, no `argument-hint` and no `description`. Under the skill's own ENDS scan an unrecognised token ends the modifier run, so typing one **dropped the opt-in AND put a stray word in the goal** — `/auto full workflow audit X` yielded goal *"workflow audit X"* with Workflow still hard-OFF. ⚑ It **failed safe** (tighter defaults held, nothing errored), which is exactly why it survived in shipped docs: a knob that silently keeps its default is indistinguishable from one you never reached for. Also corrected the axis count — the description said "one word per axis" then listed three plus two loose extras; there are **six** (authority · presence · duration · work-source · context-recovery · fan-out). **Ruled ENDS-only and uniform, NOT a second key=value-anywhere rule:** a general `<word>=<value>` scan would eat a legitimate goal like *"fix the assertion that checks count=20"*, which is the mis-parse the ENDS rule exists to prevent; `=`-bearing tokens are collision-free by shape and need no rule of their own. ⛔ **F4 — the Runtime Gate offered a handoff to a skill that exists in no port.** It named a Cowork counterpart of `/auto` four times and, on `y`, said it would invoke it via the `Skill` tool. Verified absent in `plugin-claude-cowork/skills` with a positive control, in source and in the installed tree. Cause: the section was doing two jobs — ADR-094's gate exists for the **24 colliding names**, where the point is offering the runtime-appropriate variant, and this skill is Code-only with no counterpart, so it is not in the collision set. **The dead redirect is what a colliding-name template produces when applied to a non-colliding name.** Rewritten as a Bash **capability precondition**: the check is unchanged, the stop branch now routes to the four Cowork gates that do exist (`prospect`, `retrospect`, `handoff`, `wrapup`), and the proceed branch gained the honesty instruction it lacked — never report a Bash-backed step as done when it did not run. `y`/`n` kept for family uniformity with the question polarised to *"Stop here?"*, so `n` still means *run it anyway*; phrasing it "proceed anyway?" would have inverted `n` for this skill alone. ⛔⛔ **A PRE-EXISTING ASSERTION WAS CERTIFYING THE DEFECT: `tests/repros/auto-modes.sh` assertion `I` required the skill to NAME the dead variant**, so it would have gone red for anyone who fixed the redirect. Found only because the green arm failed at 155/1. It came from the colliding-name template rather than a measurement; replaced (not deleted) with the fact that is true — the skill declares itself Code-only. ⚑ **This is the inverse of ordinary test rot: a vacuous assertion goes quietly green and protects nothing, while this one went actively green on a broken behaviour and would have BLOCKED the repair. Both are invisible until you fix the code — which is why the green arm is not a formality.** ⚑ Also self-caught: my own `FG5` emitted five failures for one defect (a per-gate loop plus a redundant summary covering 2 of 4); collapsed to one assertion naming every miss. **Gates:** Gate B measured before and after rather than assumed — **19,552 → 19,622 B of 19,968, headroom 346** (the `description` IS counted; `argument-hint` is not, `release.sh:78` stops at the next top-level key, so the hint edits were free). auto-modes **146 → 156**; two-sided via a scratch mirror tree running the edited suite against the committed pre-edit file — **151 passed / 5 failed**, each for its own named reason, GREEN 156/0. `FG3` passes in **both** arms and is labelled in-file as a non-regression guard against an over-broad deletion, **not landing evidence**. Full plugin suite 253/0, repro set 37 suites/0, all bare exit 0. Commits `d6fd0fa` (F2/F3) + `7ec3ca7` (F4), staged by exact path. ⚠ **NEITHER FIX IS LIVE** — measured in the installed plugin: 4 dead refs remain, `fanout=` absent from `argument-hint`, description still undercounts the axes. A surgical copy of `skills/auto/SKILL.md` makes them live; a full re-install also lands 42 other commits. ⚠ **NOT pushed** — 44 ahead of `origin/main`, 42 of them another arc's. ⚠ Ports: F2/F3 reach antigravity and cursor when their build scripts next run (`build.sh` copies canonical `skills/` and has no `auto` overlay; cursor recompiles via `port-skills-to-mdc.py`); codex is hand-sync; **F4 needs no propagation — those ports strip ADR-094 gates, censused 0**. ⚠ **A `/wrapup` conflict left unresolved rather than actioned: `session_state_tracked` is global-`true`, but this repo ignores `SESSION.md` at `.gitignore:17`, is public, and has a fatal Gate D on home paths.** The skill's true-branch would delete that ignore line and stage session state into a public artifact; not done, and `.gitignore` was concurrently dirty. This is the documented global-knob defect firing where the consequence is publication. ⚠ Unretrospected: `d6fd0fa~1..7ec3ca7`.*

*Session 2026-09-23/24 — **v2.53.3 SHIPPED + PUSHED + GH-RELEASED + ARTIFACT-VERIFIED** (`origin/main`=`17c15c8` by `ls-remote`, annotated tag `v2.53.3` dereferencing to it, `/releases/latest`=v2.53.3, 6 assets, fake-asset control 404, plugin and antigravity aliases byte-identical to the local builds, shipped hook carries the fix and 0 copies of the retired deny wording). ⛔ **Claude Code 2.1.280 stopped persisting most assistant text blocks** — saved instead as a *paraphrased* thinking block with no `[Rule 22]` token, even when the text block held only the marker (2.1.278 ~85% text, 2.1.280 24–31%). The gate now also accepts the marker at the start of a line in a non-edit tool's input; carrier = a Bash heredoc `cat <<'R22'`. ⛔ **Separately, on every version the hook usually ran ~10 ms after its own tool call was created, before the line was flushed, and fail-opened** (1,640 fail-opens vs 70 denials; 15/30 replayed would have denied); it now re-reads for up to 1.5 s (`ARIA_R22_FLUSH_WAIT_MS`). ⚑ **The first diagnosis was wrong for a structural reason: it read the same transcript the hook reads, so it could not tell "not emitted" from "emitted but not recorded" — only a live probe separated them.** ⚑ Running the final suite against the OLD hook with a declared prediction found a dead control (H2's `>= cap` bound passed on the pre-poll hook at load ~60); mutation testing alone would not have. ⚠ **OPEN:** not installed yet (auto mode blocks self-modifying the governing hook — Mike's step); field recount owed after one session on 2.53.3; `CODEMAP.md:105,149` still describe text-only detection; `PORT-LEDGER.json` antigravity watermark still reads 2.52.8 though the port is regenerated; `/retrospect range b18f7ce..17c15c8` owed. Gates: `knowledge/logs/{prospect,preflight}/2026-09-23-*r22-marker-channel-and-flush-poll.md`.*