v0.9.6 batch — unblock CI, harden release (#18), truth-pass + injection-defense fix - #19
Merged
Conversation
Wave 0 of the v1.0 GA execution plan.
C1: rule-parity guardrail was red because core/rules/ holds 20 files but the
tree diagram hardcoded 19. Replace the hardcoded count in
runtimes/claude/CLAUDE.md with a {{RULE_COUNT}} placeholder that compile.py
fills from the live core/rules/*.md count, so it can never drift again.
Strengthen test-rule-parity.sh to assert the placeholder is intact and to
read the resolved count from the compiled artifact. Sync prose counts in
CLAUDE.md and README.md (19 -> 20).
C3: bump deprecated CI actions to Node-24 runtimes across all 4 workflows
(checkout v4->v5, setup-python v5->v6, github-script v7->v8).
Guardrails: rule-parity now 6/6; all 14 suites pass; compile --check clean.
Separate verifier + retro agents found three issues in the first Wave 0 pass:
1. Semantic bug: {{RULE_COUNT}} counted ALL core/rules/*.md files but is
labelled "Auto-loading behavioral rules". It would silently overcount the
day a rule sets autoload:false. Derive the count from the autoload-filtered
set instead (same population as {{AUTOLOAD_RULES}}), and count the same way
in the test.
2. Missed truth-pass surfaces: CONTRIBUTING.md still said 19; README's rules
table had only 19 rows under a "20" header (model-roles row missing).
Fixed both; README table now lists all 20 rules.
3. Guardrail coverage: rule-parity only watched the compiled tree diagram.
Extend it to assert every "N auto-load(ed|ing) behavioral rules" claim in
CLAUDE.md / README.md / CONTRIBUTING.md matches the real count, so these
hand-authored surfaces can't drift unnoticed.
rule-parity now 9/9; all 14 suites pass; compile --check clean.
Four learnings from the agent-to-agent retro on the CI-unblock wave: derive-from-meaning not proxy; guardrails only cover what they read; when test edits are legitimate; green PR != proven on untriggered paths.
Wave 1 of the v1.0 GA execution plan. release.sh could exit 0 after pushing the signed tag without ever creating the GitHub release page (same failure class as F-001 — a command that "succeeds" without doing the thing). Two fixes: - Build the gh flags as an array so an absent --draft cannot word-split into a silent argument the way the old unquoted $DRAFT_FLAG could. - After `gh release create`, assert the page exists via `gh release view --json url`; treat a missing release as a hard failure and print the exact recovery command. The "published" message now only prints after verification. - Drop the dead TITLE variable; refresh the --dry-run plan. Add tests/release-tooling/test-release-verify.sh (RED-first regression guard: asserts the gh release view check follows creation, no unquoted $DRAFT_FLAG, success message ordering, + shellcheck) and register it in the guardrails matrix. shellcheck runs in CI (preinstalled on ubuntu-latest). Release tooling test 3/3; all 15 fixture suites pass; bash -n clean.
Independent verification caught that the first regression test had false confidence: its `grep | head -1` matched the --dry-run echo lines, not the real command logic, so it passed even with the entire fix reverted (the exact "green that doesn't mean what you think" trap that birthed the #18/F-001 bug class). Replace the fragile ordering greps with a behavioral harness: mock git/gh/ python3 on PATH, run release.sh end-to-end, and assert the exit code for the #18 symptom (gh release create "succeeds" but no page exists -> must exit non-zero), the empty-URL case, and the happy path. Keep anchored static checks (no unquoted $DRAFT_FLAG; a real non-echo `gh release view` exists) + shellcheck. Verified the mutation check: reverting the verification block now makes the test fail 3/5 (previously it stayed green). 5/5 with the fix; all suites pass.
…rify agent, injection-defense fixes
Wave 2 of the v1.0 GA execution plan (C5, D1, B3, D3 Phase 0/1).
C5 (CONTRIBUTING): "three CI checks" was stale — there are four workflows now
(compile-drift, schema-check, rule-boundary-check, guardrails). List all four,
and document the community-PR strategy (merge-as-is + co-authored refactor
follow-up) that previously lived only in maintainer memory.
D1 (model-roles): add a concrete capability-tier table mapping role shapes to
the current lineup — Architect: Opus 4.8 / gpt-5.5; Editor: Sonnet 4.6 /
gpt-5.x-codex; Fast: Haiku 4.5 / gpt-5.x-codex-mini — while keeping the
guidance-not-enforcement framing and "prefer latest in each tier".
B3 (Codex verify sub-agent): ADD's 4th role (verify) was missing from the Codex
sub-agent set. Add runtimes/codex/agents/verify.toml (workspace-write, high
reasoning — it runs gates and may write test artifacts) and the two hardcoded
compile.py enumerations that omitted it. Extend the codex-native-skills test to
assert verify.toml. Now emits 5 agents; suite 58/58.
D3 (injection-defense, Phase 0/1):
- SECURITY FIX: the unicode-tag-block regex was a broken byte character-class
([\xF3\xA0\x80\x80-\xF3\xA0\x87\xBF]) whose dominant 0x80-0xF3 range matched
almost any multibyte UTF-8 — ~100% false positives (em-dashes, arrows, box
characters; 652/652 sampled events were benign, 0 real attacks). Replace with
(?:\xF3\xA0[\x80\x81][\x80-\xBF]){3,}, which matches exactly U+E0000-E007F as
the description always claimed. Verified: AC-028 (real tag-channel attack)
still fires; AC-029 benign control passes; injection suite 11/11.
- BUG: the JSONL audit writer used pretty-printing `jq -n`, producing multi-line
records that aren't valid JSONL and interleave/corrupt under concurrent hook
runs. Switch both writers to `jq -cn` (compact, single-line, atomic append).
- HYGIENE: the runtime audit trail .add/security/injection-events.jsonl was
being committed. Gitignore .add/security/ and untrack it; document the
local-only audit trail in SECURITY.md's threat model.
All 15 fixture suites pass; compile --check clean.
…tive bug Wave 2 verification (retro L-3) found the injection suite couldn't detect a revert of the D3 regex fix: the only negative control was pure ASCII, which never tripped the OLD broken regex either, so a regression would stay green. Add benign-multibyte.json — dense with the exact byte classes that caused the ~100% false-positive rate (em-dash, arrow, box-drawing, ≥, ✓, ✅, CJK, emoji) — and assert it does NOT fire. Mutation-verified: reverting the regex to the old 0x80-0xF3 byte-class turns this test red (11/12); the fixed regex is 12/12. Applies the Wave 1 learning (L-041): a regression test must be able to fail.
VERSION 0.9.5 -> 0.9.6; .add/config.json; README badge; migrations.json plugin_version + a 0.9.5->0.9.6 hop (recording the hop so the migration chain stays intact — skipping it has silently broken the chain before). CHANGELOG [0.9.6] section authored from the Wave 0-2 work. Recompiled: plugin.json and all [ADD vX.Y.Z] / skill-heading substitutions now read v0.9.6. compile --check clean; validators pass; all 15 suites green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Waves 0–2 of the v1.0 GA execution plan. Turns the red
maingreen, fixes the release tooling, and completes the v0.9.6 truth-pass — including a real injection-defense bug.Each wave was executed under ADD's own SDLC: RED-first tests where applicable, an independent verifier agent + an agent-to-agent retro per wave, learnings captured to
.add/learnings.json(L-035…L-046).Wave 0 — unblock
main{{RULE_COUNT}}so it can't drift again;rule-paritystrengthened to cover all prose surfaces (9/9).checkout v5/setup-python v6/github-script v8across all 4 workflows (Node-20 deprecation).Wave 1 — release tooling
release.shcan no longer exit 0 without publishing — array flags + a post-creategh release viewassertion with a recovery command. Behavioral, mutation-verified regression test.Wave 2 — v0.9.6 truth-pass
model-roles.mdcapability-tier table (Opus 4.8 / Sonnet 4.6 / Haiku 4.5; gpt-5.5 / gpt-5.x-codex).verifysub-agent (verify.toml+ 2 compile.py enumerations + test); 58/58.unicode-tag-blockregex was a broken byte-class with a ~100% false-positive rate (652/652 sampled events benign, 0 attacks). Replaced with a precise pattern (verified to still catch the real attack, AC-028). JSONL audit writer →jq -cn(atomic)..add/security/gitignored + untracked. Mutation-verified benign-multibyte regression fixture added.All 15 fixture suites pass;
compile --checkclean. Version bump to v0.9.6 lands in a follow-up commit on this branch.🤖 Generated with Claude Code