Commit Graph
499 Commits
Author SHA1 Message Date
Bastien Chanot 5e19419981 job4: SPEC-06 config-protection-payload-matrix
New T18-T20 in lib/tests/config-protection.test.sh (+4 assertions,
20→24). T18 Write payload, T19 MultiEdit payload → both exit 2 (pass
trivially today — the extraction is tool-name-agnostic — but lock
against a future narrowing to Edit-only; stated honestly, per report).
T20 sentinel containing ONLY whitespace bytes (" \n\t", not literally
empty) → exit 2 AND consumed — exercises config-protection.sh:44-46's
`grep -q '[^[:space:]]'` check specifically, which the pre-existing
T17 (zero-byte file) doesn't reach. Closes J4-07 (WEAK): every payload
in this suite said "Edit", so a future Edit-only narrowing (or a
weaker sentinel-emptiness check) would have failed open with no red.

DOUBLY GATED per report §3.5 (edits config-protection's own test) +
user's stated exception (STOP and show the exact draft before writing,
even though the formal AUTHORIZATION line said AUTHORIZED) — drafted
inline, user confirmed "proceed as drafted" before the sentinel/edit.

Mutations (lean scratch copy — only hooks/config-protection.sh + this
test file, not the whole repo/.git), one at a time, each reverted
before the next:
- T18/T19: gated the file_path extraction on `tool_name == "Edit"`
  (python3 tool_name check + if/else) → both red alone, everything
  else (incl. T1-T17) unaffected.
- T20: swapped the whitespace-aware `grep -q '[^[:space:]]'` for
  `[ -n "$reason" ]` (byte-count only) → T20 reds alone; T17 (the
  zero-byte case) stays green either way, confirming T20 tests
  something T17 structurally cannot.
GREEN: real repo unmutated, 24/24 passed, shellcheck clean.
2026-07-06 19:32:02 +02:00
Bastien Chanot ceb3f63fa2 job4: SPEC-11 prune-suite-repo-skill-source
skills/prune-memory/tests/run-deterministic.sh:11 default changed from
$HOME/.claude/skills/prune-memory/SKILL.md to $HERE/../SKILL.md (kept
the ${SKILL:-…} env override; reordered HERE's definition before it,
since the new default references $HERE). Closes J4-11 (FIXTURE-DRIFT):
the suite sourced the INSTALLED path, safe today only because
~/.claude/skills/prune-memory is a symlinked directory back to this
repo — if an install ever materializes real copies instead of
symlinking, the suite would silently test the wrong (stale) artifact
while the shipped SKILL.md drifts unnoticed.

Behavior identical today (verified: symlink resolves to the same
inode, `diff` confirms byte-identical content).
GREEN: real repo, suite still all GREEN (RED-1/2/5/6/7).
Red demo (lean scratch copy — skills/prune-memory/{SKILL.md,tests/
run-deterministic.sh} only): moved $HERE/../SKILL.md away → loud
`grep`/`awk: cannot open ... No such file or directory` errors, exit
1, RED-2/RED-5 flip status — proves the new default is genuinely what
gets read, not a silent fallback.
2026-07-06 19:24:01 +02:00
Bastien Chanot f033defa9d job4: SPEC-07 doc-shape-removed-envelope
New S11-S13 in lib/tests/run-doc-shape.sh (+4 assertions, 19→23) +
truncate_last_n() helper (removes exactly N lines from the END of a
committed file — pure removal, 0 added lines, no heading, so the
ADDED-envelope and heading checks at doc-shape.sh:70/78 can't fire
first). Baseline = 40 plain committed lines, then truncated. S11
remove exactly 20 (== default DOC_SHAPE_MAX_REMOVED, `-gt` boundary)
→ within (0). S12 remove 30 → exceeds (1), stderr names the path. S13
DOC_SHAPE_MAX_REMOVED=5 override + 6-line removal → exceeds (1).
Closes J4-08 (WEAK): the REMOVED branch was never driven over
threshold by any existing case (S4 only removes 2 lines) — a
regression here mislabels a large doc deletion MINOR and doc-syncer's
auto-commit flow would swallow it silently (the exact RISK-1 BDR-040's
oracle exists for).

Mutation (lean scratch copy — only doc-shape.sh + run-doc-shape.sh,
not the whole repo/.git): changed `-gt "$DOC_SHAPE_MAX_REMOVED"` to
`-gt 2000` (doc-shape.sh:82). RED: S12 fails both assertions (30
removed no longer exceeds) and S13 fails (the hardcoded literal also
kills the env-override contract — DOC_SHAPE_MAX_REMOVED=5 no longer
has any effect). S11 stays green (20 removed was always within,
mutation-invariant). 3/3 reds land exactly where expected.
GREEN: real repo unmutated, 23/23 passed, shellcheck clean.
2026-07-06 19:22:52 +02:00
Bastien Chanot 42fc2e6acb job4: SPEC-03 curated-config-guard
New lib/tests/curated-config-guard.test.sh (+4 assertions). Extracts
restore_curated_configs() from install-plugins.sh AT TEST RUNTIME via
awk '/^restore_curated_configs\(\) \{/,\/^\}/' (verified single-
occurrence, column-0 closing brace) so drift in the real script
propagates into the test instead of testing a frozen copy. Harness
defines GUARDED_CONFIGS/CFG_SNAPSHOT/REPO/info() itself (the array
literal at install-plugins.sh:41 is outside the extracted range).
Sandbox REPO with the 3 fake guarded files + a pre-populated
CFG_SNAPSHOT; mutates CLAUDE.md only (simulated installer drift);
asserts: mutated file restored byte-identical (cmp -s), the other two
guarded files' content unchanged (not touched by the restore loop),
snapshot dir removed. Closes J4-03 (CRITICAL): the guard against
graphify's installer clobbering CLAUDE.md/settings.json had zero test
coverage.

Mutation (copy of install-plugins.sh, lean scratch — only that one
file, not the whole repo/.git): inverted the cmp condition
(`! cmp -s` → `cmp -s`) at the line the report names. RED: T1 fails
(the mutated file no longer gets restored — the inverted condition
only copies when already identical, a no-op, and skips restoration
exactly when it's needed). T2/T3/T4 stay green, confirming the
mutation is localized to the restore path.
GREEN: real repo unmutated, PASS=4 FAIL=0, shellcheck clean.
2026-07-06 19:20:25 +02:00
Bastien Chanot 9b1fb92d89 job4: SPEC-09 retire-real-state-snapshot
Deleted T4e + its coupled echo note in lib/tests/run-reconcile.sh and
the fixtures/real-state.snapshot it read — superseded by SPEC-08's T7,
which actually DRIVES the tree_clean/pushed/msg_committed oracles
instead of miming them via a static line-count regex. Closes J4-09
(WEAK+drift): T4e only counted fixture line-suffixes matching
`=(true|resolved|present)$`; the snapshot itself was stale
(BLK-009=open contradicted blockers-snapshot.md's already-resolved
status) and unowned, and the drift was inert (`=open` doesn't even
match the count regex) — the assertion could never have caught
anything.

Updated skills/reconcile/SKILL.md:53's hardcoded "20/20" claim to the
new total (unguarded file, same logical step, ordered after SPEC-08+
SPEC-10 per the report).

grep -c 'real-state.snapshot' lib/tests/run-reconcile.sh == 0
(verified). No red demo (deletion, per spec) — gate is the green run
+ that grep. GREEN: 25/25 passed, shellcheck clean.
2026-07-06 19:17:23 +02:00
Bastien Chanot fb749f4e30 job4: SPEC-10 decisions-snapshot-fixture
New lib/tests/fixtures/decisions-snapshot.md (neutral name, LRN-077
style): carries a --help token (so reconcile_contradiction_candidates
still surfaces the BDR-001 ⇄ --help-chantier candidate against
todo-snapshot.md), a "one-line ticket" line, and representative
OUT-OF-SCOPE/DEFERRED/follow-up context. T3 and T5 in run-reconcile.sh
now read this fixture instead of the LIVE $MEM/decisions.md; deleted
the $MEM variable definition and its stale comment. Closes J4-10
(FIXTURE-DRIFT): T3/T5 were the last live-registry reads in this
suite (T2 was fixed in job3-B1) — any legitimate prune/reword of the
real decisions.md would have reded the suite for a reason unrelated
to the reconcile engine itself.

grep -c '$MEM' lib/tests/run-reconcile.sh == 0 (verified).
GREEN: real repo, 26/26 passed (all 4 T3 markers + T5 candidate found
via the fixture).
Red demo (per spec — no code mutation, this is a fixture-substitution
spec): lean scratch copy, pointed T3's decisions-arg at /dev/null
transiently → "one-line ticket" (the only marker living solely in the
decisions-side fixture, not in todo-snapshot.md) goes missing, RED;
the other 3 markers stay green (satisfied by todo-snapshot.md alone).
Proves the assertions actually read the fixture rather than passing
vacuously.
2026-07-06 19:16:04 +02:00
Bastien Chanot 12c0d1d9fd job4: SPEC-08 oracle-sandbox
New T7 block in lib/tests/run-reconcile.sh (+6 assertions, 20→26):
a throwaway git repo under mktemp with a LOCAL BARE origin drives the
3 previously-unexercised oracles live: tree_clean (dirty→rc≠0, clean→
rc0), pushed (pushed to origin FIRST so origin/main exists — else
rev-list is vacuously empty — then rc0 when synced, rc≠0 once 1 ahead),
msg_committed (rc0 for a present commit message, rc≠0 for an absent
one). Closes J4-12 (DEGRADED, prerequisite of SPEC-09/10): these 3
oracles backed report-only /reconcile output with zero test coverage —
a silent inversion would mis-report open-work state.

Mutation (lean scratch copy — only lib/reconcile.sh + lib/tests/
run-reconcile.sh + its fixtures + .claude/memory/decisions.md, not the
whole repo/.git, per the /tmp-exhaustion lesson from SPEC-01/02/04):
inverted tree_clean's rc (`-z` → `-n` on the porcelain-status check;
the report's literal "--quiet → negated" wording doesn't match this
function's actual `[ -z ... ]` shape, so applied the equivalent
semantic inversion). RED: both T7a assertions fail (dirty reads as
clean and vice versa); T7b/T7c stay green, confirming the mutation is
localized. (T6a/b/c red in the lean copy too, expected — no real git
history / skills dir there — unrelated to the mutation.)
GREEN: real repo unmutated, 26/26 passed (T7 included).
2026-07-06 19:12:48 +02:00
Bastien Chanot c8e91e8924 job4: SPEC-05 init-identity-precheck-zero-mutation
New T15 block in lib/gitflow-test.sh (+7 assertions, 83→90): fresh git
init sandbox with NO identity (GIT_CONFIG_GLOBAL=/dev/null
GIT_CONFIG_SYSTEM=/dev/null, git 2.53 supports the override) →
gitflow_init must return rc 1 AND leave zero mutation: no develop
branch, unborn HEAD, hooksPath unset, nothing staged, no .gitignore/
.githooks written. Closes J4-06 (WEAK): every test repo up to now set
an identity first, so this precheck never fired.

Mutation (lean scratch copy — only lib/gitflow.sh + lib/gitflow-test.sh
+ templates/gitignore/standard.gitignore, not the whole repo/.git, to
avoid repeating the /tmp exhaustion from the SPEC-01/02/04 full-repo
copies): deleted the identity precheck (gitflow.sh:178-179). RED: 3/7
T15 assertions fail — "nothing staged", "no .gitignore written", "no
.githooks written" — while rc stays 1 and HEAD stays unborn (git itself
still refuses the identity-less commit). This is the half-applied-init
failure mode named in the finding (BLK-012 class): same exit code, but
now via a partial mutation instead of a clean upfront refusal — exactly
why the spec pins zero-mutation checks beyond rc alone.
GREEN: real repo unmutated, 90/90 passed (T15 included).
2026-07-06 19:10:27 +02:00
Bastien Chanot 70d47957c6 job4: SPEC-04 hook-exemption-matrix
New T14 block in lib/gitflow-test.sh (+3 assertions, 80→83), direct
.githooks/pre-commit invocation (T10-style): T14a mixed code+.claude
staged together on main → BLOCKED (whitelist must not let code ride
along .claude/). T14b MERGE_HEAD present + code staged on main →
exit 0 (conflict-resolution commit exemption, gitflow.sh:222). T14c
hook installed+activated BEFORE the first commit (gitflow_install_hook,
not gitflow_init's deferred activation) → root commit still succeeds
(gitflow.sh:221). Closes J4-05 (WEAK): these 3 exemption paths were
untested — a whitelist regression, or the root/merge exemptions
breaking, would have been silent.

Mutations (scratch copy, applied via Bash/sed — not Edit/Write, avoids
tripping config-protection's path-suffix guard on lib/gitflow.sh for a
throwaway file that's never committed), one at a time, each reverted
before the next:
- T14c: deleted the root-commit guard (gitflow.sh:221,
  `git rev-parse --verify -q HEAD ... || exit 0`) → T14c reds alone.
- T14b: deleted the MERGE_HEAD guard (gitflow.sh:222) → T14b reds alone.
- T14a: report's candidate mutation ("remove grep -v '^\.claude/'")
  self-corrects (still blocks mixed, via the inverted over-blocking
  direction — doesn't red). Used the pinned alternative instead:
  `head -1` → `head -0` in the whitelist check (gitflow.sh:230),
  neutering the non-empty test so every protected-branch commit is
  wrongly allowed. T14a reds, plus (expected, same root cause) the
  pre-existing T3 "block direct code on main" and T10 DRIFT(main)/
  DRIFT(develop) also red — consistent with a whitelist regression
  of this shape being a broad, not narrow, break.
GREEN: real repo unmutated, 83/83 passed (T14a/b/c included).
2026-07-06 18:59:44 +02:00
Bastien Chanot 55fad4b7e9 job4: SPEC-02 gitflow-finish-release-fanout
New T13 block in lib/gitflow-test.sh (+9 assertions, 71→80):
T13a release finish → main gets the commit, develop gets it via
merge-back, release branch deleted. T13b two open releases + a
finished hotfix → hotfix commit present in BOTH release branches.
T13c bugfix finish → develop only, main untouched, branch deleted.
Closes J4-02 (CRITICAL): a half-landed release (main-only or
develop-only) or a mis-based bugfix finish was invisible to the
only test suite that exercises gitflow_finish's fan-out.

Mutation (scratch copy, applied via Bash/perl — not Edit/Write, so
config-protection's path-suffix guard on lib/gitflow.sh isn't
tripped for a throwaway file that's never committed): deleted the
develop merge-back line in gitflow_finish's release arm
(gitflow.sh:122-125). RED: T13a fails 3/3 (rc 5 — _gitflow_delete
refuses because develop never got the merge, so the branch isn't
fully merged; develop missing the commit; branch not deleted).
GREEN: real repo unmutated, 80/80 passed (T13a/b/c included).
2026-07-06 18:48:29 +02:00
Bastien Chanot b0e050630c job4: SPEC-01 make-test-includes-all-suites
Makefile test target now loops lib/tests/run-*.sh in addition to
*.test.sh + gitflow-test.sh, special-casing run-release-candidate.sh
with RC_WORK=$(mktemp -d) RC_TAG=1. Closes J4-01 (CRITICAL): the 5
run-*.sh suites (memory-commit 13, doc-commit 32, doc-shape 19,
reconcile 20, release 5/5) were excluded from the repo's only
aggregate gate.

Mutation (scratch copy, never the working tree): dropped the
`-- "${changed[@]}"` pathspec from lib/memory-commit.sh:86's commit
call. RED: run-deterministic.sh T2 fails (pre-staged dangling code
gets embarked instead of staying staged), make test exits 2.
GREEN: real repo unmutated, make test exits 0, all suites incl. the
5 previously-excluded ones (RESULT: 13/32/19 passed, 20 GREEN, 5
GREEN RC_TAG=1).
2026-07-06 18:45:46 +02:00
Bastien Chanot 9a178d3aba Merge chore/job3-fixes into develop 2026-07-06 17:42:39 +02:00
Bastien Chanot 64f2e59a36 job3: capitalize B1 resolution — journal follow-up (sentinel authorized, suite 20/20) 2026-07-06 17:36:33 +02:00
Bastien Chanot b6d8e79a2d job3: B1 reconcile T2 — hermetic fixture, not the live registry
skills/reconcile/SKILL.md:53 claimed "20/20, shellcheck clean" but the
suite read .claude/memory/blockers.md live, so closing BLK-009
(d1e7423) turned T2b/T2c red for a correct reason unrelated to the
engine. Froze a post-BLK-009 snapshot (lib/tests/fixtures/
blockers-snapshot.md) and pointed T2 at it instead of the live file —
same approach the other T1/T3/T4 fixtures already use. Updated T2b/T2c
expectations to match (BLK-009 resolved, open = {001,003}). Suite is
back to 20/20 GREEN, shellcheck clean, matching the skill's own claim.
2026-07-06 17:35:52 +02:00
Bastien Chanot 2028023359 job3: capitalize execution — EVAL-018 + LRN-105 + journal close
EVAL-018: job3 shipped, 46/46 findings verified, 20/23 fixes applied
(B1 blocked on sentinel scope, D2-D5+B6 skipped by decision), zero
residual on final re-sweep. LRN-105: explorer subagents need an
explicit ban on executing the subject-under-test's own CLI, not just
"read-only" framing (caught mid-run: a subagent ran `graphify .`).
2026-07-06 17:19:53 +02:00
Bastien Chanot e42a77cb1b job3: R12 SETTINGS.md — document auto mode (BDR-004, this config's default) 2026-07-06 17:00:03 +02:00
Bastien Chanot 41395ac4fd job3: R14 project-CLAUDE template — /onboard is a generator too 2026-07-06 16:59:43 +02:00
Bastien Chanot 5b461e53d5 job3: R13 memory templates — English labels + caveman rule (BDR-009) 2026-07-06 16:59:31 +02:00
Bastien Chanot f0aa4e7679 job3: R8 session-start banner — make doctor, not /health 2026-07-06 16:58:38 +02:00
Bastien Chanot b47bfe2747 job3: R8+R9+R10+R15 README — health/doctor split, make test row, skills-perso scope, tour row 2026-07-06 16:57:00 +02:00
Bastien Chanot 0dbf08df0a job3: R1-R5+R7+R11 README — tree comments + install-log + ctx7 anonymous claims 2026-07-06 16:56:28 +02:00
Bastien Chanot 466357e3ec job3: C4+C5 depth-matrix — drop mismatched score-weight/envelope sections, point to canonical specs
Both sections never matched any component (envelope §1-§9 vs the real
§0-§15 structure; 8-axis integer weights vs the agents' 7/4 and 6/5
percentage-weight scoring). Kept what this file legitimately owns: the
depth-decision matrix and the sibling-skill dedup rules.
2026-07-06 16:55:47 +02:00
Bastien Chanot 067987e81b job3: C8 web-validate — drop false CLAUDE.md attribution 2026-07-06 16:55:17 +02:00
Bastien Chanot af6203f048 job3: C7 seo — automation-options rule sourced from agents' spec, not CLAUDE.md 2026-07-06 16:55:17 +02:00
Bastien Chanot 2848ff0b77 job3: C6 harden — drop false CLAUDE.md attribution, own-policy framing 2026-07-06 16:55:17 +02:00
Bastien Chanot 215bc2d6b4 job3: C1+C2 client-handover — 6-chapter structure (BDR-013) 2026-07-06 16:54:57 +02:00
Bastien Chanot 8db9850818 job3: B3 close — STEP 5B in pipeline enumeration 2026-07-06 16:54:33 +02:00
Bastien Chanot 127202fc2f job3: B4+B5 gitflow — chore/* in branch model + finish table 2026-07-06 16:54:22 +02:00
Bastien Chanot 28ce7325dd job3: B2 profile — document actual plugin/MCP toggling (BDR-008) 2026-07-06 16:54:08 +02:00
Bastien Chanot 16a5a26cc9 job3: A3 init-project STEP 5 — match scaffolder's real deliverables (agents/scaffolder.md:52) 2026-07-06 16:53:57 +02:00
Bastien Chanot 95883a0fd1 job3: A1+A2+A8 init-project — remove broken graphify light pass, --out flag, step count
- A1: delete STEP 5d (graphify --mode quick doesn't exist in the CLI; the
  command always failed, masked by `|| true` — STEP 10's full pass already
  covers the graph)
- A2: STEP 10 full-pass build flag --output -> --out
- A8: pipeline is 12 steps (STEP 0-11), not 11; header template unchanged
  (N/11 correctly denotes the max index of a 0-indexed 12-step sequence)
2026-07-06 16:53:11 +02:00
Bastien Chanot f7d9a10d67 job3: A2+A4+A5+A6+A7 onboard body fixes
- A2: graphify build flag --output -> --out
- A4: ROADMAP xref points to the real /onboard add gsd path, not a nonexistent STEP 9 decision
- A5: exact gitflow init commit message (matches lib/gitflow.sh:163)
- A6: bare skill names (design-review, browse) — no gstack: namespace exists
- A7: eval pattern for recommend_anim_install_cmd (the function only echoes; must eval its output)
2026-07-06 16:52:10 +02:00
Bastien Chanot 86914a9549 job3: C3 geo-analyzer standalone report path -> .claude/audits/ (completes 7b57b2e) 2026-07-06 16:51:10 +02:00
Bastien Chanot d34b52e4c7 job3: D1 deploy skill — ~/.claude paths for lib+templates 2026-07-06 16:50:58 +02:00
Bastien Chanot d43d8131e5 job3: D6+D7+D9 supersede BDR-038 (BDR-054) + banner historical deploy docs
BDR-038 recorded NEXT.sh file + AskUserQuestion hand-back as the /deploy
design; 52f6678 removed both (LRN-102: pre-tool-call text may never render)
with no superseding decision. BDR-054 regularizes it. One-line banners on
docs/plans/2026-06-27-deploy-skill.md and docs/specs/2026-06-27-deploy-skill-design.md
point to the shipped behavior; historical body left untouched.
2026-07-06 16:50:17 +02:00
Bastien Chanot defc26ccfe Merge chore/ctx7-single-surface into develop 2026-07-06 12:40:53 +02:00
Bastien Chanot e737f41355 job2 tail: capitalize BDR-053 (ctx7 single surface) + journal close 2026-07-06 12:40:46 +02:00
Bastien Chanot dd39193377 chore(ctx7): single surface — installer purges context7.md rule, find-docs skill kept (BDR-053, job1-F10/job2-F13) 2026-07-06 12:40:46 +02:00
Bastien Chanot 964c5ddd4d Merge chore/job2-fixes into develop 2026-07-06 12:34:10 +02:00
Bastien Chanot 2ea21c25ba job2: capitalize execution — LRN-104 (oracle contract + no-runner) + journal 2026-07-06 12:34:03 +02:00
Bastien Chanot 6a3b197009 job2: F7 residual — onboard/plugin-check argument-hint quoting (2nd pre-existing strict-YAML error per file) 2026-07-06 12:29:05 +02:00
Bastien Chanot 898b61c005 job2: F16 remove runtime-dead gitflow-migrate.sh 2026-07-06 12:25:13 +02:00
Bastien Chanot 96deea100f job2: F2 deny find -exec (arbitrary-exec mirror) 2026-07-06 12:21:56 +02:00
Bastien Chanot 5c05d6796e job2: F1 cp/mv allow→ask (shell-level guardrail overwrite path closed) 2026-07-06 12:21:38 +02:00
Bastien Chanot 35e9bff443 job2: F14 graphify description leads with graphify-out precondition 2026-07-06 12:20:47 +02:00
Bastien Chanot 54db7eeff6 job2: F13 tour description 698→~500 chars 2026-07-06 12:20:35 +02:00
Bastien Chanot f1aa1ee766 job2: F7+F15 refactor/status — quoting + routing boundaries 2026-07-06 12:20:22 +02:00
Bastien Chanot b40c702ada job2: F7 strict-YAML frontmatters (quote descriptions/argument-hints, drop stray version field) 2026-07-06 12:20:02 +02:00
Bastien Chanot 30c5803453 job2: F6+F12 status-reporter — version path + subordination 2026-07-06 12:19:16 +02:00
Bastien Chanot 18c1b327c3 job2: F12 subordinate 6 agent descriptions to their skills 2026-07-06 12:19:08 +02:00