@arbiterforge/ca-pi 0.6.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -98
- package/package.json +1 -1
- package/plugins/ca-pi/CHANGELOG.md +145 -0
- package/plugins/ca-pi/COMMANDS.md +138 -68
- package/plugins/ca-pi/SKILLS.md +137 -30
- package/plugins/ca-pi/agents/INDEX.md +3 -2
- package/plugins/ca-pi/agents/checkpoint-aggregator.md +8 -7
- package/plugins/ca-pi/agents/design-quality-reviewer.md +1 -1
- package/plugins/ca-pi/agents/finding-triage.md +31 -14
- package/plugins/ca-pi/agents/verdict-aggregator.md +64 -0
- package/plugins/ca-pi/{ORCHESTRATOR.md → arbiter.md} +37 -36
- package/plugins/ca-pi/extensions/codearbiter.js +844 -19
- package/plugins/ca-pi/generated/command-catalog.json +386 -196
- package/plugins/ca-pi/generated/roles.json +9 -0
- package/plugins/ca-pi/hooks/_arbiterstatelib.py +59 -11
- package/plugins/ca-pi/hooks/_bashguardlib.py +30 -12
- package/plugins/ca-pi/hooks/_gitexec.py +23 -0
- package/plugins/ca-pi/hooks/_githooks.py +50 -23
- package/plugins/ca-pi/hooks/_hooklib.py +148 -20
- package/plugins/ca-pi/hooks/_host.py +9 -1
- package/plugins/ca-pi/hooks/_metricslib.py +20 -0
- package/plugins/ca-pi/hooks/_modelib.py +762 -0
- package/plugins/ca-pi/hooks/_protectedlib.py +13 -4
- package/plugins/ca-pi/hooks/_prunelib.py +51 -12
- package/plugins/ca-pi/hooks/_prunepolicy.py +33 -7
- package/plugins/ca-pi/hooks/_readinjectlib.py +10 -4
- package/plugins/ca-pi/hooks/_releaselib.py +278 -48
- package/plugins/ca-pi/hooks/_updatelib.py +230 -50
- package/plugins/ca-pi/hooks/doctor.py +58 -9
- package/plugins/ca-pi/hooks/git-enforce.py +10 -3
- package/plugins/ca-pi/hooks/hostapi.py +220 -22
- package/plugins/ca-pi/hooks/pi-bridge.py +10 -4
- package/plugins/ca-pi/hooks/prompt-submit.py +486 -0
- package/plugins/ca-pi/hooks/prune-transcript.py +23 -3
- package/plugins/ca-pi/hooks/session-start.py +529 -435
- package/plugins/ca-pi/hooks/statusline.py +28 -10
- package/plugins/ca-pi/hooks/wire-statusline.py +13 -8
- package/plugins/ca-pi/includes/anti-slop-design/INDEX.md +1 -1
- package/plugins/ca-pi/includes/command-compatibility.md +16 -0
- package/plugins/ca-pi/includes/dangerous-mode.md +57 -0
- package/plugins/ca-pi/includes/ops-mode.md +96 -0
- package/plugins/ca-pi/includes/pi-host-notes.md +10 -1
- package/plugins/ca-pi/includes/redirect.md +12 -1
- package/plugins/ca-pi/includes/routing-table.md +14 -5
- package/plugins/ca-pi/includes/safety-core.md +86 -0
- package/plugins/ca-pi/includes/smarts/core.md +1 -1
- package/plugins/ca-pi/routines/INDEX.md +1 -1
- package/plugins/ca-pi/routines/decision-lifecycle/SKILL.md +55 -3
- package/plugins/ca-pi/routines/decision-lifecycle/references/adr-template.md +9 -1
- package/plugins/ca-pi/routines/decompose/SKILL.md +1 -1
- package/plugins/ca-pi/routines/dispatching-parallel-agents/SKILL.md +4 -4
- package/plugins/ca-pi/routines/release/SKILL.md +1 -1
- package/plugins/ca-pi/skills/ca-checkpoint/SKILL.md +5 -4
- package/plugins/ca-pi/skills/ca-cleanup/SKILL.md +6 -0
- package/plugins/ca-pi/skills/ca-context-check/SKILL.md +6 -0
- package/plugins/ca-pi/skills/ca-create-context/SKILL.md +6 -0
- package/plugins/ca-pi/skills/ca-decompose/SKILL.md +6 -0
- package/plugins/ca-pi/skills/ca-doctor/SKILL.md +4 -0
- package/plugins/ca-pi/skills/ca-init/SKILL.md +18 -1
- package/plugins/ca-pi/skills/ca-pr/SKILL.md +17 -1
- package/plugins/ca-pi/skills/ca-review/SKILL.md +3 -4
- package/plugins/ca-pi/skills/ca-spike/SKILL.md +15 -8
- package/plugins/ca-pi/skills/ca-status/SKILL.md +13 -1
- package/plugins/ca-pi/skills/ca-watch/SKILL.md +6 -0
- package/plugins/ca-pi/includes/dev-mode.md +0 -30
- package/plugins/ca-pi/skills/ca-arbiter/SKILL.md +0 -36
- package/plugins/ca-pi/skills/ca-dev/SKILL.md +0 -42
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
# _segmentslib the individual content segments (ctx bar, rate cells, pill, …)
|
|
49
49
|
# _ledgerlib token/cost ledger (extracted earlier, T-12)
|
|
50
50
|
# `seg_update` / `plugin_root_for_render` (the update-notifier surface) and
|
|
51
|
-
# `
|
|
51
|
+
# `current_mode`'s render-time callers stay wired here because a test patches
|
|
52
52
|
# `statusline.plugin_root_for_render` directly — that seam must resolve through
|
|
53
53
|
# this module's own globals, not a lib's.
|
|
54
54
|
#
|
|
@@ -251,7 +251,7 @@ try:
|
|
|
251
251
|
frontmatter, count_matches = _arbiterstatelib.frontmatter, _arbiterstatelib.count_matches
|
|
252
252
|
_ARBITER_CACHE, _ARBITER_FILES = _arbiterstatelib._ARBITER_CACHE, _arbiterstatelib._ARBITER_FILES
|
|
253
253
|
_arbiter_mtime_key = _arbiterstatelib._arbiter_mtime_key
|
|
254
|
-
|
|
254
|
+
current_mode = _arbiterstatelib.current_mode
|
|
255
255
|
|
|
256
256
|
def _arbiter_enabled(ctx_path):
|
|
257
257
|
return _arbiterstatelib._arbiter_enabled(ctx_path, _frontmatter_enabled)
|
|
@@ -285,8 +285,8 @@ except Exception: # pragma: no cover — never let an import break the statusli
|
|
|
285
285
|
def arbiter_state(root, ctx_text=None, ot_text=None, oq_text=None):
|
|
286
286
|
return None
|
|
287
287
|
|
|
288
|
-
def
|
|
289
|
-
return
|
|
288
|
+
def current_mode(session_id, root=None, payload=None):
|
|
289
|
+
return "arbiter"
|
|
290
290
|
|
|
291
291
|
# --------------------------------------------------------------------------- subagents (_subagentslib)
|
|
292
292
|
try:
|
|
@@ -431,7 +431,7 @@ def seg_update(plugin=None):
|
|
|
431
431
|
try:
|
|
432
432
|
plugin = plugin if plugin is not None else plugin_root_for_render()
|
|
433
433
|
state = _updatelib.read_state(_updatelib.state_path())
|
|
434
|
-
latest = state.get("latest")
|
|
434
|
+
latest = _updatelib.target_state(state).get("latest")
|
|
435
435
|
installed = _updatelib.installed_version(plugin)
|
|
436
436
|
if not _updatelib.update_available(installed, latest):
|
|
437
437
|
return None
|
|
@@ -549,12 +549,27 @@ def _render_active_palette(raw):
|
|
|
549
549
|
root = safe(project_root, data) or os.getcwd()
|
|
550
550
|
sid = data.get("session_id") or data.get("sessionId")
|
|
551
551
|
arb = safe(arbiter_state, root)
|
|
552
|
+
# #437 (mode-plane-deterministic-flip): resolved ONCE per render and reused
|
|
553
|
+
# by both the badge below and the redshift gate near the bottom of this
|
|
554
|
+
# function, rather than re-reading the mode marker twice. Resolves via
|
|
555
|
+
# `payload=data` (NOT the pre-resolved `root` above) so the mode file is
|
|
556
|
+
# read at marker_root, agreeing with every other `.markers/` writer in a
|
|
557
|
+
# linked worktree (AC-5) — `root` here is _gitlib's plain ancestor walk,
|
|
558
|
+
# a different resolution that must not leak into mode reads.
|
|
559
|
+
mode = safe(current_mode, sid, payload=data) or "arbiter"
|
|
552
560
|
effort = (get(data, "effort", "level") or "").lower()
|
|
553
561
|
sprint = bool(arb and arb.get("sprint"))
|
|
554
|
-
#
|
|
555
|
-
#
|
|
556
|
-
|
|
557
|
-
|
|
562
|
+
# A non-arbiter MODE takes precedence over sprint, mirroring the retired
|
|
563
|
+
# /dev-over-sprint precedence exactly. Each mode gets its own distinct
|
|
564
|
+
# textual tell (AC-38) — dangerous keeps the alarming [DEV]-style bracket
|
|
565
|
+
# tag (renamed) so the full-bar redshift below still reads even where
|
|
566
|
+
# color is stripped or unseen; ops is advisory-only (no redshift) but
|
|
567
|
+
# still needs its own unmistakable badge, distinct from both arbiter and
|
|
568
|
+
# dangerous.
|
|
569
|
+
if mode == "dangerous":
|
|
570
|
+
badge = f"{BOLD}[DANGEROUS]{RESET}"
|
|
571
|
+
elif mode == "ops":
|
|
572
|
+
badge = f"{BOLD}[OPS]{RESET}"
|
|
558
573
|
elif sprint:
|
|
559
574
|
badge = f"{V3}{BOLD}[SPRINT]{RESET}" # effort now shows by the model pill
|
|
560
575
|
else:
|
|
@@ -680,7 +695,10 @@ def _render_active_palette(raw):
|
|
|
680
695
|
|
|
681
696
|
box.bottom(tees=tail_tees)
|
|
682
697
|
out = box.render()
|
|
683
|
-
|
|
698
|
+
# AC-38: only `dangerous` (gates-off) keeps the alarm-red full-bar tell —
|
|
699
|
+
# `ops` is advisory-only and must render distinctly from both `arbiter`
|
|
700
|
+
# (no shift) and `dangerous` (redshift), not share dangerous's treatment.
|
|
701
|
+
out = redshift(out) if mode == "dangerous" else out
|
|
684
702
|
# Honor the NO_COLOR convention by stripping SGR from the final render. Do NOT gate on
|
|
685
703
|
# isatty: a Claude Code statusline is intentionally piped, so an isatty test would drop
|
|
686
704
|
# color in normal use. Width math already ignores ANSI, so stripping keeps alignment.
|
|
@@ -68,14 +68,19 @@ ARBITER_SPINNER_VERBS = {
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def plugin_root(opt):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
# The host seam authenticates the package currently executing this hook.
|
|
72
|
+
# --plugin-root remains test/operator corroboration, never an authority
|
|
73
|
+
# able to select a different executable for settings.json.
|
|
74
|
+
authenticated = os.path.realpath(
|
|
75
|
+
os.path.abspath(_hooklib.get_host().plugin_root()))
|
|
76
|
+
if not opt:
|
|
77
|
+
return authenticated
|
|
78
|
+
requested = os.path.realpath(os.path.abspath(opt))
|
|
79
|
+
if requested != authenticated:
|
|
80
|
+
raise SystemExit(
|
|
81
|
+
"ERROR: --plugin-root must match the authenticated executing "
|
|
82
|
+
f"adapter root ({authenticated}); got {requested}.")
|
|
83
|
+
return authenticated
|
|
79
84
|
|
|
80
85
|
|
|
81
86
|
def settings_path(opt):
|
|
@@ -6,7 +6,7 @@ Never bulk-read the whole bundle: a website task pulls the web leaf and never se
|
|
|
6
6
|
|
|
7
7
|
**Scope of authority:** generated, user-facing artifacts only (UI, PR descriptions, CHANGELOG,
|
|
8
8
|
and any report/slide/chart a producer is told to render). This bundle does NOT govern codeArbiter's
|
|
9
|
-
own internal framework docs (`
|
|
9
|
+
own internal framework docs (`arbiter.md`, the `INDEX.md` files, skill/agent bodies), whose
|
|
10
10
|
house style is set elsewhere.
|
|
11
11
|
|
|
12
12
|
## How to use the bundle
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Command-route compatibility
|
|
2
|
+
|
|
3
|
+
A compatibility alias is an installed legacy route with a preferred canonical form. The legacy
|
|
4
|
+
route keeps its argument grammar, confirmation gates, side effects, durable outputs, and host
|
|
5
|
+
availability; its migration notice does not invoke or forward to another host command.
|
|
6
|
+
|
|
7
|
+
The registry permanently declares ca 2.17.0, ca-codex 0.9.0, and ca-pi 0.10.0 as the first-containing
|
|
8
|
+
candidates. Each payload's deprecation clock becomes effective only when GitHub's Release API confirms
|
|
9
|
+
an exact, non-draft Release for that candidate tag and the tag's commit contains both the matching
|
|
10
|
+
registry declaration and matching payload version.
|
|
11
|
+
A tag alone, a draft, unavailable API evidence, or any tag/Release/payload mismatch does not start a
|
|
12
|
+
clock. Published releases ca 2.16.0, ca-codex 0.8.0, and ca-pi 0.9.0 predate this registry and do not
|
|
13
|
+
contain the compatibility metadata. ca retains these routes through every 2.x release, with no removal
|
|
14
|
+
before a separately approved 3.0.0. ca-codex and ca-pi retain them through every later 0.x release,
|
|
15
|
+
with no removal before a separately approved 1.0.0. Passing a version floor never authorizes removal:
|
|
16
|
+
removal needs a new governed decision and fresh compatibility evidence.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!-- codeArbiter — the `dangerous` mode body. Composed as `safety-core.md` + this file
|
|
2
|
+
whenever the mode plane is flipped to `dangerous` (token: `mode --dangerous`). This is a
|
|
3
|
+
general gates-off posture for whatever project the repo holds — it is not scoped to
|
|
4
|
+
codeArbiter's own maintenance and carries no host-specific env-gate. -->
|
|
5
|
+
|
|
6
|
+
# dangerous — gates-off posture
|
|
7
|
+
|
|
8
|
+
`dangerous` suspends orchestration for local, exploratory work on this repo — any repo, not a
|
|
9
|
+
codeArbiter-specific lane. Reach for it when routing, skills, and gate ceremony are in the way of
|
|
10
|
+
something reversible and low-stakes: poking at a script, running the app, trying an approach
|
|
11
|
+
before committing to a plan. It is **not** the required lane for ordinary development — normal
|
|
12
|
+
feature work still flows through the ordinary gated commands and ships via PR.
|
|
13
|
+
|
|
14
|
+
**Gates-off is persona-off, not enforcement-off.** The enforcement hooks under `core/pysrc/` read
|
|
15
|
+
`.codearbiter/CONTEXT.md`'s frontmatter and the repo's own state — never this persona — so nothing
|
|
16
|
+
about being in `dangerous` mode changes what a hook decides. The following still fire, identically
|
|
17
|
+
to every other mode:
|
|
18
|
+
|
|
19
|
+
- **H-01** — no direct write to the default branch, with the `.git/hooks` backstop that closes the
|
|
20
|
+
`--no-verify` escape.
|
|
21
|
+
- **H-02** — no force-push, same backstop, same `--no-verify` closure.
|
|
22
|
+
- **H-05** — the `.codearbiter` audit logs are still append-only.
|
|
23
|
+
- **H-09b / H-10b** — the commit-time secret scan still runs.
|
|
24
|
+
- **H-11** — ADR files are still immutable outside `/adr`.
|
|
25
|
+
- **H-18** — `.codearbiter/CONTEXT.md`'s activation frontmatter is still protected.
|
|
26
|
+
- **H-19** — the `.codearbiter/.markers/` gate tokens are still protected.
|
|
27
|
+
- **H-22** — marker-gated and otherwise protected project state is still protected.
|
|
28
|
+
|
|
29
|
+
What the persona stops carrying is prose-only and none of it is floored by a hook: no routing, no
|
|
30
|
+
skills, no redirect, no command catalog — a plain, direct coding assistant, with the safety-core
|
|
31
|
+
invariants (secrets, irreversible-action confirmation, diagnose-don't-bypass, no silent
|
|
32
|
+
reconciliation) still the floor beneath it.
|
|
33
|
+
|
|
34
|
+
**Blocking questions still surface.** A `[CONFIRM-NN]` is a question whose answer is the user's, and
|
|
35
|
+
that does not change with posture — if anything it matters more here, since nothing else is asking.
|
|
36
|
+
Startup still reports host, stage, the active mode, any open `[CONFIRM-NN]`, and the override count;
|
|
37
|
+
what it drops is the command-facing presentation (the await-a-command trailer, the catalog and
|
|
38
|
+
standup references) that a mode with no commands cannot act on.
|
|
39
|
+
|
|
40
|
+
**The project's own state lives in `.codearbiter/`**, and nothing in this mode reads it for you:
|
|
41
|
+
|
|
42
|
+
- `CONTEXT.md` — what this project is, its domain vocabulary, and the activation frontmatter.
|
|
43
|
+
- `tech-stack.md` — languages, frameworks, and the commands that build, test, and lint this repo.
|
|
44
|
+
- `coding-standards.md` — the conventions code here is expected to match.
|
|
45
|
+
- `security-controls.md` — the boundaries, banned primitives, and secret-handling rules.
|
|
46
|
+
- `open-questions.md` — the unresolved `[CONFIRM-NN]` set.
|
|
47
|
+
|
|
48
|
+
Read whichever the work actually touches. These are facts about the repo, not a dispatch table:
|
|
49
|
+
there is no routing in this mode, and naming a doc here is not naming a command.
|
|
50
|
+
|
|
51
|
+
**Entry and exit are logged**, the same audit-trail obligation as every other bypass: a `MODE:
|
|
52
|
+
dangerous enter` row on entry, a `MODE: dangerous exit` row on exit, appended (never rewritten) to
|
|
53
|
+
`.codearbiter/overrides.log`. The mode is session-scoped: `mode --arbiter` restores orchestration
|
|
54
|
+
explicitly, and a new session restores it implicitly (the mode file is cleared at session start).
|
|
55
|
+
|
|
56
|
+
Even in `dangerous` mode, `overrides.log` itself is never rewritten — the append-only rule has no
|
|
57
|
+
exception for this mode.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<!-- codeArbiter — the `ops` mode body. Composed as `safety-core.md` + this file
|
|
2
|
+
whenever the mode plane is flipped to `ops` (token: `mode --ops`). Ops ships
|
|
3
|
+
advisory-only (user ruling, ADR-0030 position 7): a scoped persona carve-out
|
|
4
|
+
for reversible local runtime work, never a process supervisor — no owned
|
|
5
|
+
PIDs, no readiness probes, no scoped stop, no cross-session recovery. No
|
|
6
|
+
enforcement hook becomes mode-aware; every gate fires identically in every
|
|
7
|
+
mode, in `ops` exactly as in `arbiter` and `dangerous`. -->
|
|
8
|
+
|
|
9
|
+
# ops — advisory runtime carve-out
|
|
10
|
+
|
|
11
|
+
`ops` narrows the persona only, for one purpose: running, watching, or exercising
|
|
12
|
+
this repo's own software locally without a routing detour for every `npm run dev`.
|
|
13
|
+
It is not the required lane for ordinary development; normal feature work still
|
|
14
|
+
flows through the ordinary gated commands and ships via PR.
|
|
15
|
+
|
|
16
|
+
**The carve-out is a discriminator, not an allowlist of binaries** — a binary
|
|
17
|
+
allowlist is unbounded, drifts per project, and says nothing about `npm run
|
|
18
|
+
build && git commit`. What decides is the durable artifact a command leaves
|
|
19
|
+
behind, the same axis the surviving hooks already key on (H-01/H-02 = git
|
|
20
|
+
history, H-03 = the index, H-05/H-11/H-18/H-22 = tracked project state), so
|
|
21
|
+
persona and enforcement draw the same line rather than two different ones:
|
|
22
|
+
|
|
23
|
+
An operation that starts, observes, or exercises a running system and leaves
|
|
24
|
+
no change in tracked files or git history may be performed in-channel, named
|
|
25
|
+
in one line as it is taken. Anything that mutates tracked files, the index,
|
|
26
|
+
git history, or published state stays routed and refused.
|
|
27
|
+
|
|
28
|
+
This narrows §0 and §6 only — arbiter's §0 non-negotiable ("route; never
|
|
29
|
+
implement directly") and §6 user-interaction rule ("no command owns this" is
|
|
30
|
+
a routing gap, not a permission) are the two clauses that would otherwise
|
|
31
|
+
block the common case, because no command owns `npm run dev`. Safety-core's
|
|
32
|
+
§3 hard rules, §2 conflict ladder, and §7 diagnose-don't-bypass discipline
|
|
33
|
+
are unchanged — the carve-out narrows routing, not the floor beneath it.
|
|
34
|
+
|
|
35
|
+
**Refused, always** — irreversible action against anything outside this repo:
|
|
36
|
+
infrastructure teardown, cluster or namespace deletion, package publication,
|
|
37
|
+
live-database migration, volume destruction. None of these leave a
|
|
38
|
+
git-visible trace to review, so none of them qualify for the in-channel
|
|
39
|
+
exception; they route exactly as they would in `arbiter` mode.
|
|
40
|
+
|
|
41
|
+
**The ambiguous cases, resolved rather than left implicit:**
|
|
42
|
+
|
|
43
|
+
- `npm test` — **permitted.** It exercises the suite; a default test
|
|
44
|
+
configuration writes no tracked file.
|
|
45
|
+
- `npm ci --ignore-scripts` — **permitted.** It reinstalls `node_modules`
|
|
46
|
+
(untracked) strictly from the existing lockfile; it never writes
|
|
47
|
+
`package.json` or the lockfile itself — that would be a mutation, and
|
|
48
|
+
mutation is what the discriminator refuses.
|
|
49
|
+
- `npm ci` **without** `--ignore-scripts` — **refused, routed.** It runs each
|
|
50
|
+
dependency's `preinstall`/`install`/`postinstall`/`prepare` scripts, which
|
|
51
|
+
are arbitrary code that can write anywhere, including tracked files. That
|
|
52
|
+
the *command* leaves tracked files alone says nothing about what its
|
|
53
|
+
lifecycle scripts do, and no hook enforces the boundary in this mode — so
|
|
54
|
+
the discriminator cannot be evaluated in advance and the answer is refuse.
|
|
55
|
+
The same reasoning applies to any package manager's install (`pnpm`, `yarn`,
|
|
56
|
+
`uv`, `poetry`, `bundle`): the flag, not the tool, is what makes it
|
|
57
|
+
in-channel.
|
|
58
|
+
- `docker compose up` — **permitted**, for the same reason: it starts a
|
|
59
|
+
running system and, under a default compose file, leaves tracked files
|
|
60
|
+
untouched. A bind mount that writes into a tracked path flips this to
|
|
61
|
+
refused — the discriminator governs the actual artifact produced, not the
|
|
62
|
+
command's name.
|
|
63
|
+
|
|
64
|
+
**Gates-off is persona-off, not enforcement-off**, exactly as in `dangerous`:
|
|
65
|
+
no hook reads this persona, so H-01, H-02, H-03, H-05, H-09b, H-10b, H-11,
|
|
66
|
+
H-18, H-19, and H-22 fire identically to every other mode. The narrowing
|
|
67
|
+
above is advisory, not hook-backed — its compensating control is the audit
|
|
68
|
+
row (`MODE: ops enter`), the same load-bearing replacement AC-11 gives every
|
|
69
|
+
non-arbiter mode.
|
|
70
|
+
|
|
71
|
+
**Blocking questions still surface**, and the override count still reports —
|
|
72
|
+
neither is suppressed in this mode.
|
|
73
|
+
|
|
74
|
+
**The project's own state lives in `.codearbiter/`**, and nothing in this
|
|
75
|
+
mode reads it for you:
|
|
76
|
+
|
|
77
|
+
- `CONTEXT.md` — what this project is, its domain vocabulary, and the
|
|
78
|
+
activation frontmatter.
|
|
79
|
+
- `tech-stack.md` — languages, frameworks, and the commands that build, test,
|
|
80
|
+
and lint this repo.
|
|
81
|
+
- `coding-standards.md` — the conventions code here is expected to match.
|
|
82
|
+
- `security-controls.md` — the boundaries, banned primitives, and
|
|
83
|
+
secret-handling rules.
|
|
84
|
+
- `open-questions.md` — the unresolved `[CONFIRM-NN]` set.
|
|
85
|
+
|
|
86
|
+
Read whichever the work actually touches. These are facts about the repo,
|
|
87
|
+
not a dispatch table: naming a doc here is not naming a command.
|
|
88
|
+
|
|
89
|
+
**Entry and exit are logged**, the same audit-trail obligation as every other
|
|
90
|
+
mode: a `MODE: ops enter` row on entry, a `MODE: ops exit` row on exit,
|
|
91
|
+
appended (never rewritten) to `.codearbiter/overrides.log`. The mode is
|
|
92
|
+
session-scoped: `mode --arbiter` restores orchestration explicitly, and a new
|
|
93
|
+
session restores it implicitly (the mode file is cleared at session start).
|
|
94
|
+
|
|
95
|
+
Even in `ops` mode, `overrides.log` itself is never rewritten — the
|
|
96
|
+
append-only rule has no exception for this mode.
|
|
@@ -36,6 +36,14 @@ file maps those actions to Pi's extension API.
|
|
|
36
36
|
globally; rate-window telemetry is omitted rather than fabricated. The
|
|
37
37
|
governance row renders only when the repository is enabled and affirmatively
|
|
38
38
|
trusted.
|
|
39
|
+
- `/ca-sidebar on|off|toggle|width N` composites an optional right-hand sidebar
|
|
40
|
+
column (session, subagents, workspace, todos panels), defaulting on in
|
|
41
|
+
interactive parents at 120 columns or wider. It relies on Pi render hooks
|
|
42
|
+
that are undocumented in every promoted version, so each install is
|
|
43
|
+
probe-gated at runtime and any failure disposes back to native rendering.
|
|
44
|
+
Workspace and todos panels are trusted-only; the MCP panel renders only when
|
|
45
|
+
Pi exposes servers, which no promoted version does. No sidebar preference
|
|
46
|
+
persists beyond the session.
|
|
39
47
|
- `/ca-prune` selects shared semantic policy. The active Pi session is compacted
|
|
40
48
|
through the native compaction event; codeArbiter does not rewrite Pi session
|
|
41
49
|
JSONL. The private summarizer uses the hardened child runner with zero tools.
|
|
@@ -54,7 +62,8 @@ file maps those actions to Pi's extension API.
|
|
|
54
62
|
- Run `/ca-doctor` to inspect the active package path, canonical Pi CLI and
|
|
55
63
|
package origin, command ownership, supported-version expansion fingerprints,
|
|
56
64
|
Python/core/bridge health, child fingerprint, final mutator wrappers, footer
|
|
57
|
-
initialization, background-manager health, and the
|
|
65
|
+
initialization, sidebar availability, background-manager health, and the
|
|
66
|
+
H-03 wrapper self-test.
|
|
58
67
|
- Unverified background cleanup makes the manager unhealthy, blocks later
|
|
59
68
|
launches, and directs the operator to `/ca-doctor`. Doctor never includes job
|
|
60
69
|
labels or IDs, commands, environment data, or output.
|
|
@@ -24,7 +24,9 @@ Run it? Its own gates still apply.
|
|
|
24
24
|
|
|
25
25
|
Use this — not tier 1 — whenever the command is irreversible or gate-bypassing, however clear the
|
|
26
26
|
intent: `/ca-override`, merge to the default branch, branch or worktree deletion, release and tag
|
|
27
|
-
publication
|
|
27
|
+
publication. There the confirmation is the gate, not friction. (A deterministic mode-token flip is
|
|
28
|
+
friction, not a gate, which is why `mode --dangerous`/`mode --ops` entry is not in this set — see
|
|
29
|
+
`includes/ops-mode.md` and `includes/dangerous-mode.md`.)
|
|
28
30
|
|
|
29
31
|
## Tier 3 — genuinely unclear
|
|
30
32
|
|
|
@@ -67,3 +69,12 @@ A missing owner is a **routing gap**, not an override case. When no command owns
|
|
|
67
69
|
so and surface the gap — never steer the user toward `/ca-override` to get past a coverage hole.
|
|
68
70
|
That substitution is exactly what issue #308 recorded: a routine post-merge cleanup routed first to
|
|
69
71
|
`/ca-chore`, which does not accept it, and then to `/ca-override`, which exists to be rare.
|
|
72
|
+
|
|
73
|
+
## Exception — local runtime work
|
|
74
|
+
|
|
75
|
+
`npm run dev` and its kin have no owning command; in `arbiter` mode that is the routing gap above,
|
|
76
|
+
surfaced in one line. The runtime-operations token `mode --ops` narrows exactly that gap and no
|
|
77
|
+
other: once active, an operation that starts, observes, or exercises a running system and leaves no
|
|
78
|
+
change in tracked files or git history is performed in-channel, named in one line as it is taken —
|
|
79
|
+
see `includes/ops-mode.md`. Anything that mutates tracked files, the index, git history, or
|
|
80
|
+
published state is still a routing gap, `ops` or not.
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# Routing table
|
|
2
2
|
|
|
3
3
|
Loaded on a scope-touch or `/command`, not every turn. This table is the authoritative trigger→route
|
|
4
|
-
surface: it answers *what to invoke or route given a trigger
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
surface and destructive-operation registry: it answers *what to invoke or route given a trigger* and
|
|
5
|
+
*which operations always require tier-2 confirmation*; for *what doc to read before touching a scope*,
|
|
6
|
+
use `reference-map.md`. Follow the primary route; the gate is a hard stop, not a suggestion. A command
|
|
7
|
+
is **invoked**; the orchestrator **routes** to a skill; a skill **dispatches** an agent.
|
|
7
8
|
Routing to a skill means loading its body from `<plugin-root>/routines/<name>/SKILL.md` — a route
|
|
8
9
|
cell names the skill; this path convention locates it. That resolution never depends on the host's
|
|
9
10
|
skill registry: a chain-internal skill hidden from the registry (`disable-model-invocation`) is
|
|
10
11
|
reached the same way.
|
|
11
12
|
|
|
13
|
+
## Destructive operations (tier-2 regardless of cue)
|
|
14
|
+
|
|
15
|
+
- Logged bypass (`/override`)
|
|
16
|
+
- Merge to the default branch
|
|
17
|
+
- Branch or worktree deletion
|
|
18
|
+
- Release and tag publication
|
|
19
|
+
|
|
12
20
|
| Invocation cue | Primary route | Also dispatch | Hard gate |
|
|
13
21
|
|---|---|---|---|
|
|
14
22
|
| New feature | `/feature` Step 0 triage → full lane `brainstorming` → `writing-plans` → `executing-plans` → `tdd`, or logged small lane straight to `tdd` | `backend-`/`frontend-`/`infra-author` | No spec, no code; no code before `tdd` Phase 1; small lane only on all triage criteria, logged to `triage.log` |
|
|
@@ -21,8 +29,8 @@ reached the same way.
|
|
|
21
29
|
| Commit | `/commit` → `commit-gate` | — | No commit without all nine gates green |
|
|
22
30
|
| Open a PR / finish a branch | `/pr` → `finishing-a-development-branch` | reviewer fleet per path; PR-body prose applies `anti-slop-design` (`core` + `medium-documents` §7.A.1) | PR only; no direct-to-default, no force-push |
|
|
23
31
|
| Watch a PR's CI / babysit checks | `/watch` → detached `gh pr checks --watch` | on-red diagnose (propose\|branch) | Never auto-merges; green → notify + offer; merge-to-default routes through the hard gate; no poll loop |
|
|
24
|
-
| Code review of the diff | `/review` → `dispatching-parallel-agents` | reviewer fleet → `finding-triage` → `
|
|
25
|
-
| Periodic sweep | `/checkpoint` → `dispatching-parallel-agents` | reviewer fleet → triage → aggregator | Surfaces a triaged report; not a promotion gate |
|
|
32
|
+
| Code review of the diff | `/review` → `dispatching-parallel-agents` | reviewer fleet → `finding-triage` → `verdict-aggregator` | BLOCK on any CRITICAL/HIGH |
|
|
33
|
+
| Periodic sweep | `/checkpoint` → `dispatching-parallel-agents` | reviewer fleet → finding-triage → read-only verdict; then explicit `checkpoint-aggregator` persistence | Surfaces and persists a triaged report; not a promotion gate |
|
|
26
34
|
| Governance record for a window | `/audit` | — | Read-only; never overwrites a packet; audit lines quoted verbatim |
|
|
27
35
|
| Release / version tag | `/release` → `release` skill | `commit-gate` (release commit); CHANGELOG prose applies `anti-slop-design` (`core` §3.A/§3.B) | No tag on a red suite; tag not pushed unbidden |
|
|
28
36
|
| Code uses crypto / hashing / signing / TLS / random | `crypto-compliance` skill | `auth-crypto-reviewer` | BLOCK on any banned primitive |
|
|
@@ -41,6 +49,7 @@ reached the same way.
|
|
|
41
49
|
| Sitting down to code / repo hygiene cleanup | `/ca-standup` → orchestrator git actions | — | ff-only pull on a clean tree; each branch/worktree delete confirmed individually; stash/dirty/un-pushed report-only; never touch the default branch |
|
|
42
50
|
| Finish an already-merged branch (post-merge cleanup) | `/ca-cleanup` → `post-merge-cleanup` | — | Ancestry of the FETCHED default proven before any delete; unique/unclassifiable artifacts never discarded without a per-item confirmation; `--ff-only` and `branch -d` only; remote branch untouched; never routes to `/ca-override` |
|
|
43
51
|
| Add / start / done a backlog task | `/ca-task` → `hooks/taskwrite.py` (the pure `_taskboardlib` transforms) | — | Board written only through the writer; `start` always stamps a date; `done` requires in-progress; never delete to complete |
|
|
52
|
+
| Local runtime work (run, watch, exercise this repo's own app) | `mode --ops` → narrows the persona per `includes/ops-mode.md`, no skill/agent dispatch | — | Advisory only, no hook becomes mode-aware; permitted only when the operation starts, observes, or exercises and leaves no change in tracked files or git history — anything that mutates tracked files, the index, git history, or published state still routes normally |
|
|
44
53
|
| Promote a workflow's un-actioned follow-ups | `includes/harvest.md` (run at a terminal step) → `/ca-task` (work) / `open-questions.md` (decision) | — | Batch-confirm interactive; SMARTS-logged auto under `/sprint`; dedup by `(from <origin>)`; a blocking decision escalates, never demoted |
|
|
45
54
|
| Zero-onboarding dry-run of the reviewer fleet | `/ca-preview` (no skill; reuses `includes/review-matrix.md` to predict reviewers) | — | Read-only; writes nothing, stages nothing, `git status` unchanged; no `/ca-init` or `.codearbiter/` required; empty diff exits clean |
|
|
46
55
|
| Manual drift audit of provenance-tracked docs (bypass / merge / external edit case) | `/ca-context-check` → `context-check` skill | — | Read-only unless user selects re-scout or re-baseline; MUST NOT commit on its own; not the daily loop — commit-gate auto-heal is the routine path |
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<!-- codeArbiter — safety core. Prepended to whichever mode body is active (arbiter,
|
|
2
|
+
dangerous, ops) at injection time; never injected alone. -->
|
|
3
|
+
|
|
4
|
+
# Safety core
|
|
5
|
+
|
|
6
|
+
**Precedence.** This file is prepended to every mode body and binds over it: no mode body may
|
|
7
|
+
weaken, omit, or override a clause stated here.
|
|
8
|
+
|
|
9
|
+
**Section numbers are a public interface.** The enforcement hooks fire identically in every
|
|
10
|
+
mode and their block messages cite a `§N` from this file directly, so the numbering below must
|
|
11
|
+
not change when this file is edited — a citation that stops resolving is a defect, not a style
|
|
12
|
+
choice. Stated here in the visible body rather than in a comment: this file is read by agents,
|
|
13
|
+
and an instruction that changes behaviour must be where the reader can see it.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## §2 — Conflict hierarchy
|
|
18
|
+
|
|
19
|
+
When rules pull apart, resolve in this order; if unresolvable, invoke `/conflict` — never guess:
|
|
20
|
+
1. Security & correctness of the audit trail — 2. Correctness & data integrity —
|
|
21
|
+
3. Maintainability & reviewability — 4. Performance — 5. Developer velocity.
|
|
22
|
+
Cite the level of any non-obvious tradeoff in the PR description.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## §3 — Hard rules (always enforced)
|
|
27
|
+
|
|
28
|
+
- MUST NOT store a raw secret in repo, log, container image, or prompt. The hook floor covers
|
|
29
|
+
only commit time (H-09b/H-10b): nothing floors a secret written to a log, an untracked file,
|
|
30
|
+
or a prompt — `pre-write.py` classifies by path, never content. In a gates-off mode this
|
|
31
|
+
sentence is the only remaining protection.
|
|
32
|
+
- MUST NOT write directly to the default branch or force-push. All changes via branch/PR.
|
|
33
|
+
- MUST NOT author an ADR except via `/adr`, with user attribution.
|
|
34
|
+
- MUST NOT silently reconcile a conflict — invoke `/conflict`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## §5 — Scope-touch review
|
|
39
|
+
|
|
40
|
+
Before acting on a scope-touch (auth/crypto/secrets, dependencies, migrations, telemetry,
|
|
41
|
+
decisions), the governing `.codearbiter/*.md` doc is read first and routed to its owning
|
|
42
|
+
skill/agent — a changed dependency manifest is reviewed before it is committed.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## §6 — Irreversible actions
|
|
47
|
+
|
|
48
|
+
ADR files under `.codearbiter/decisions/` are immutable history once accepted; editing or
|
|
49
|
+
deleting one outside `/adr` is prohibited, marker or not.
|
|
50
|
+
|
|
51
|
+
The irreversible-action set draws a confirmation even when intent is obvious, because the
|
|
52
|
+
confirmation is the gate, not friction: merge to the default branch, branch or worktree
|
|
53
|
+
deletion, release and tag publication, and the logged bypass itself (`/ca-override`).
|
|
54
|
+
|
|
55
|
+
A parameter is yours to decide only when it is reversible, has one sensible answer, and is
|
|
56
|
+
recorded where the user will review it — an uncertain classification is a fork, and forks are
|
|
57
|
+
asked.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## §7 — Override, and gates that look wrong
|
|
62
|
+
|
|
63
|
+
The `.codearbiter` audit logs (`overrides.log`, `triage.log`, `sprint-log.md`,
|
|
64
|
+
`gate-events.log`, `decisions/decision-log.md`) are append-only: MUST NOT truncate, overwrite,
|
|
65
|
+
or rewrite one — append with a single Edit or `>>`, never a bulk rewrite.
|
|
66
|
+
|
|
67
|
+
**A gate that looks wrong is diagnosed, not bypassed.** The instrument is the suspect, not the
|
|
68
|
+
rule: reproduce the block, read what the guard actually keyed on, name the defect. Until
|
|
69
|
+
diagnosed, the gate stands. A confirmed false positive is a bug filed through its lane;
|
|
70
|
+
`/override` remains for the judged exception, and its log line says which of the two it was.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## What survives every mode
|
|
75
|
+
|
|
76
|
+
Even with every gate off, this residual set holds:
|
|
77
|
+
|
|
78
|
+
- The secrets prohibition above has no mode exception.
|
|
79
|
+
- The §6 irreversible-action set is never taken without the confirmation it requires.
|
|
80
|
+
- A conflict is surfaced, never silently reconciled.
|
|
81
|
+
- The §2 conflict hierarchy still orders any tradeoff made unsupervised.
|
|
82
|
+
- The §7 diagnose-don't-bypass discipline still governs any guard actually encountered.
|
|
83
|
+
- State is read, not remembered — a claim about now uses an instrument run now.
|
|
84
|
+
|
|
85
|
+
The rules bind by what they protect, not by their spelling: a path that satisfies a rule's
|
|
86
|
+
letter while defeating its protection is a violation with extra steps.
|
|
@@ -20,7 +20,7 @@ the three `plans/` artifacts; `CONTEXT.md` and `open-questions.md` (including it
|
|
|
20
20
|
Deferred-decisions sections) constrain at their recorded level. Load index-first: consult the ADR
|
|
21
21
|
index (`decision-log.md` or the `decisions/` filename listing) and plan section headings only;
|
|
22
22
|
load a body only after the index names it relevant; never bulk-read `plans/` or `decisions/`
|
|
23
|
-
(
|
|
23
|
+
(arbiter.md §3's no-bulk-reads rule).
|
|
24
24
|
|
|
25
25
|
Three outcomes:
|
|
26
26
|
|
|
@@ -18,7 +18,7 @@ Skill bodies load on routing only. This index is the surface scan; never bulk-re
|
|
|
18
18
|
| [writing-plans](writing-plans/SKILL.md) | `/feature`, `/sprint` (after the spec) | Decomposes an approved spec into small tasks, each with a path + a verification that maps to a `tdd` obligation; writes `plans/<slug>.md` with bijective criterion↔task coverage. |
|
|
19
19
|
| [executing-plans](executing-plans/SKILL.md) | `/feature` | Checkpoint coordinator — groups tasks into batches, delegates each to `subagent-driven-development` (fresh author agent per task, full review chain), stops for user acknowledgement between batches. |
|
|
20
20
|
| [subagent-driven-development](subagent-driven-development/SKILL.md) | `/sprint` (engine), `executing-plans` (batch scope) | Fresh subagent per task → spec-compliance then quality review → fresh-run verification; accepts only on proof. Hard-stops on `tdd` BLOCK, security CRITICAL, `[CONFIRM-NN]`. |
|
|
21
|
-
| [dispatching-parallel-agents](dispatching-parallel-agents/SKILL.md) | `subagent-driven-development`, `/sprint`, parallel `/review` | Reusable fan-out primitive: bound concurrency, collect, dedupe, funnel through `finding-triage`→`
|
|
21
|
+
| [dispatching-parallel-agents](dispatching-parallel-agents/SKILL.md) | `subagent-driven-development`, `/sprint`, parallel `/review` | Reusable fan-out primitive: bound concurrency, collect, dedupe, funnel through `finding-triage`→`verdict-aggregator`. Results unused until the read-only funnel runs. |
|
|
22
22
|
| [finishing-a-development-branch](finishing-a-development-branch/SKILL.md) | `/feature`, `/sprint` (terminal) | The terminal step after `commit-gate`: open-PR / merge-via-PR / discard. No direct-to-main, no force-push; `/sprint` auto-selects open-PR and never merges. |
|
|
23
23
|
| [using-git-worktrees](using-git-worktrees/SKILL.md) | `subagent-driven-development`, `dispatching-parallel-agents` (opt-in) | OPTIONAL per-unit filesystem isolation for parallel work; integrates accepted units back onto the caller's working branch for its single `commit-gate` + finish. Never the default path. |
|
|
24
24
|
| [secret-handling](secret-handling/SKILL.md) | changed code reads/writes/passes a secret | The secret-source gate: identify → source → sinks/persistence. Secrets only from the approved store in `security-controls.md`; never in source, log, error, telemetry, image, or LLM prompt. Dispatches `auth-crypto-reviewer`. |
|
|
@@ -30,7 +30,7 @@ Gate: the existing ADRs are indexed by stem and, for `/adr`, the next number is
|
|
|
30
30
|
|
|
31
31
|
Confirm the decision content with the user — context, the decision itself, alternatives, consequences. MUST NOT fill these from inference. Surface any unknown as an inline `[CONFIRM-NN]` placeholder; do not resolve it by guessing.
|
|
32
32
|
|
|
33
|
-
**Drop the authoring marker first.** The `pre-write`/`pre-edit` hooks block any write to `.codearbiter/decisions/NNNN-*.md` unless a fresh authoring marker is present — that block is the mechanism enforcing "ADRs only via `/adr`" (
|
|
33
|
+
**Drop the authoring marker first.** The `pre-write`/`pre-edit` hooks block any write to `.codearbiter/decisions/NNNN-*.md` unless a fresh authoring marker is present — that block is the mechanism enforcing "ADRs only via `/adr`" (arbiter.md §3), so the sanctioned path must arm it itself. Immediately before writing, create the marker at the path the hooks check (project root = git top level):
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
mkdir -p "$(git rev-parse --show-toplevel)/.codearbiter/.markers"
|
|
@@ -57,9 +57,59 @@ rm -f "$(git rev-parse --show-toplevel)/.codearbiter/.markers/adr-authoring-acti
|
|
|
57
57
|
|
|
58
58
|
Gate: the ADR file is written with a real `decided-by` user attribution, numbered without a gap, and its log entry is appended. An ADR with no user attribution, or authored as the disposition of a finding, does not pass — STOP.
|
|
59
59
|
|
|
60
|
+
### Accepted/Planned binding
|
|
61
|
+
|
|
62
|
+
`accepted` means **Accepted/Planned**. It records the user's governance decision; it does not claim
|
|
63
|
+
that any obligation is Implemented or Verified. When the user explicitly authorizes acceptance:
|
|
64
|
+
|
|
65
|
+
1. Change only the ADR's status fields to accepted. Derive stable, stem-scoped obligations from every
|
|
66
|
+
normative clause in its immutable record, bind each obligation to exact ADR
|
|
67
|
+
text, and obtain independent review that the sealed obligation set is complete.
|
|
68
|
+
2. Route through `commit-gate` to commit the accepted ADR and decision-log append. Do not add the
|
|
69
|
+
acceptance binding to that commit: its `source_commit` cannot truthfully name a commit that does
|
|
70
|
+
not exist yet.
|
|
71
|
+
3. From that exact commit, hash the committed Git blob bytes and the separately canonicalized
|
|
72
|
+
immutable record: strict UTF-8 with LF-normalized line endings, containing the complete ADR while
|
|
73
|
+
replacing only the recognized status value in the strictly parsed frontmatter `status:` field and
|
|
74
|
+
`## Status` section with fixed sentinels. The two values must agree. All remaining Status prose,
|
|
75
|
+
including approval attribution, stays bound alongside title, date, `decided-by`, supersession,
|
|
76
|
+
governed paths, H1, and every other section. Malformed or duplicate frontmatter, status, or
|
|
77
|
+
headings fail closed. Append one
|
|
78
|
+
`acceptance` event to
|
|
79
|
+
`<project-root>/.codearbiter/decisions/adr-lifecycle.jsonl`, then persist that acceptance binding
|
|
80
|
+
in a subsequent commit. The event uses schema `adr-lifecycle/v1` and records `adr` (full stem),
|
|
81
|
+
`recorded_at`, `source_commit`, `blob_sha256`, `body_sha256`, `obligations`,
|
|
82
|
+
`obligations_sha256`, and `obligations_sealed: true`. A second acceptance or baseline binding for
|
|
83
|
+
the same stem is invalid.
|
|
84
|
+
|
|
85
|
+
The lifecycle ledger is append-only. A legacy accepted ADR receives a `baseline` with no fabricated
|
|
86
|
+
acceptance commit, an `observed_commit` whose Git blob is rechecked as the migration snapshot, an
|
|
87
|
+
empty or incrementally mapped obligation list, and
|
|
88
|
+
`obligations_sealed: false`; it remains Accepted/Planned. Later delivery evidence appends records:
|
|
89
|
+
`implemented` binds one declared obligation to a source commit and relevant input digests;
|
|
90
|
+
`verified` additionally binds a unique event ID, explicit proof contract, repository-scoped claim,
|
|
91
|
+
producer, command/workflow identity, timezone-aware observation and expiry times, and the same current
|
|
92
|
+
inputs. Evidence paths and digests are recomputed from the named Git commit, never trusted from the
|
|
93
|
+
caller. A later uniquely identified event may renew expired or changed-input evidence; an append-only
|
|
94
|
+
invalidation event may withdraw a prior evidence event. Only a complete,
|
|
95
|
+
sealed obligation set with current implementation inputs and fresh verification inputs derives
|
|
96
|
+
Implemented or Verified. Changed inputs invalidate the derived state; history is never rewritten.
|
|
97
|
+
|
|
98
|
+
After acceptance, do not edit any bound ADR content. A later user-authorized stored status transition
|
|
99
|
+
may change only the recognized status value in the strictly parsed frontmatter `status:` and
|
|
100
|
+
`## Status`; approval prose remains immutable. Supersession remains a forward reference in the new
|
|
101
|
+
ADR. The acceptance commit retains the exact original blob while the immutable-record digest proves
|
|
102
|
+
every other byte-equivalent field did not change.
|
|
103
|
+
|
|
60
104
|
## Phase 3 — Status (/adr-status) · gate: BLOCK
|
|
61
105
|
|
|
62
|
-
Read-only. For each ADR (or the `--adr N` target), report: stem, title, status,
|
|
106
|
+
Read-only. For each ADR (or the `--adr N` target), report: stem, title, stored governance status,
|
|
107
|
+
derived delivery state, date, and supersession state. Read `adr-lifecycle.jsonl` when present. Display
|
|
108
|
+
stored `accepted` as **Accepted/Planned**. Display Implemented or Verified only when every obligation
|
|
109
|
+
in a sealed binding has the required current, input-bound evidence; otherwise name the narrow reason
|
|
110
|
+
(unsealed, incomplete, stale, expired, or mismatched) and do not promote the ADR. Repository evidence
|
|
111
|
+
never implies live-host, publication, support, legal, or other external truth. Find supersession by
|
|
112
|
+
scanning forward for any later ADR whose `supersedes:` **resolves to** it.
|
|
63
113
|
|
|
64
114
|
Resolve a `supersedes:` value like this, and never guess:
|
|
65
115
|
|
|
@@ -75,7 +125,7 @@ If a supersession candidate contradicts an `accepted` ADR with no clear directio
|
|
|
75
125
|
## ADR Status — YYYY-MM-DD
|
|
76
126
|
|
|
77
127
|
### Active
|
|
78
|
-
- ADR-NNNN-<slug> — <title> — <status> (<date>)
|
|
128
|
+
- ADR-NNNN-<slug> — <title> — governance: <status>; delivery: <Accepted/Planned | Implemented | Verified> (<date>)
|
|
79
129
|
|
|
80
130
|
### Superseded
|
|
81
131
|
- ADR-NNNN-<slug> — <title> — superseded by ADR-MMMM-<slug>
|
|
@@ -97,6 +147,8 @@ Gate: every indexed ADR appears with its current status and supersession state;
|
|
|
97
147
|
- MUST NOT record a decision the user did not explicitly make. "Use your best judgment," "I trust you" are declined.
|
|
98
148
|
- MUST NOT resolve a `[CONFIRM-NN]` placeholder by guessing. Surface it and stop.
|
|
99
149
|
- MUST NOT advance an ADR's status without explicit user instruction.
|
|
150
|
+
- MUST NOT report accepted as Implemented or Verified without complete, sealed, current lifecycle evidence.
|
|
151
|
+
- MUST NOT rewrite or truncate a committed `adr-lifecycle.jsonl`, create a second binding, or fabricate legacy acceptance evidence.
|
|
100
152
|
- MUST NOT edit a prior ADR or a prior decision-log entry to add a back-reference — supersession is a forward-only chain; append a new record whose `supersedes:` names the prior one.
|
|
101
153
|
- **The never-edit rule protects decision CONTENT, not identifiers.** Rewriting what was decided corrupts the record; disambiguating *which document a pointer names* repairs it. Maintainer ruling, 2026-07-25: *"the never edit rule is meant to prevent this situation, not prevent this situation from being fixed."* So a correction that is provably identifier-only — a `supersedes:` value changed from a number to the stem it already meant — is permissible, and nothing else about the file is. Any such correction MUST be a single-line diff that alters not one word of any decision, MUST be visible in its own commit, and still requires the maintainer-armed `adr-authoring-active` marker. MUST NOT touch Context, Decision, Alternatives, Consequences, Risks, `status:`, `date:`, `decided-by:`, or `title:` under this allowance.
|
|
102
154
|
- MUST NOT number an ADR with a gap, and MUST NOT reuse a number an existing stem already holds — a shared number makes every bare reference to it ambiguous.
|
|
@@ -55,8 +55,16 @@ governs: <optional, comma-separated path globs this decision constrains — e.g.
|
|
|
55
55
|
mirrors it for human readers. Keep the two in agreement.
|
|
56
56
|
- **Status lifecycle:** `proposed → accepted → superseded | rejected`. `decompose` authors Layer 4
|
|
57
57
|
ADRs as **`status: draft`** during the interview and promotes each to `status: accepted` at its
|
|
58
|
-
Phase 5
|
|
58
|
+
Phase 5 in one sanctioned status edit that changes both the frontmatter `status:` field and the `## Status` value,
|
|
59
|
+
without changing any other body content. Status transitions otherwise
|
|
59
60
|
require explicit user instruction; never advance status on the skill's own judgment.
|
|
61
|
+
- **`accepted` means Accepted/Planned.** It records an approved governance decision and does not imply
|
|
62
|
+
implementation. Implemented and Verified are derived delivery states from the separate append-only
|
|
63
|
+
`adr-lifecycle.jsonl`; they are never written into ADR frontmatter. After acceptance, the decision
|
|
64
|
+
record is immutable except for the recognized, agreeing status value in the strictly parsed
|
|
65
|
+
frontmatter `status:` field and `## Status` section. Approval attribution and all other Status prose
|
|
66
|
+
remain bound with title, date, `decided-by`, supersession, governed paths, H1, and every other
|
|
67
|
+
section. A later explicit status transition changes only those recognized status values.
|
|
60
68
|
- **`decided-by:`** names the user who made the decision — real attribution, never inferred.
|
|
61
69
|
- **`supersedes:`** names the prior ADR's full filename stem — `supersedes:
|
|
62
70
|
0014-githook-shim-dropin-fail-closed`, not `supersedes: 0014` — or `none`. A bare number is
|
|
@@ -12,7 +12,7 @@ Spec the project before a line of code exists. Routed to at greenfield startup,
|
|
|
12
12
|
Run these ordered checks. Each passes silently or hard-stops with a routing action — never guess:
|
|
13
13
|
|
|
14
14
|
1. Read `<project-root>/.codearbiter/CONTEXT.md`. If it already carries the `<!--INITIALIZED-->` body marker on its own line, STOP — context exists. Route to normal operation.
|
|
15
|
-
2. Scan for meaningful source code: any file outside `.git/`, `.codearbiter/`, `.claude/`, `
|
|
15
|
+
2. Scan for meaningful source code: any file outside `.git/`, `.codearbiter/`, `.claude/`, `arbiter.md`, `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE`, `.gitignore`, `.gitmodules`, and standard tooling dotfiles. If any exist, STOP and route to `/create-context`.
|
|
16
16
|
3. Confirm `<project-root>/.codearbiter/` exists and is writable. If not, surface the gap and STOP.
|
|
17
17
|
|
|
18
18
|
All three pass → proceed to Phase 1. Later phases consume this pass-status; they do not re-run it.
|