Repository navigation
Conversation
Template v0.7.0 lands two upstream decisions. ADR 0013 replaces the hooks' undocumented hard `jq` dependency with `.agents/hooks/hook-input.sh`, a payload reader that falls back to `python3` and reports distinct exit codes (3 no parser, 4 bad payload) so each hook picks its own failure posture: PreToolUse denies with the install remedy, Stop and PostToolUse skip with a visible non-zero exit. ADR 0012 cuts the question set from 24 to 13, merges the four role playbook skills into their subagent files, collapses the SPIKE/EXPLORER/PLAN-REVISION markers into one HANDBACK(kind)/RESULT(kind) convention, and stops generating the example work unit and the verify skill. Eleven answers drop out of `.copier-answers.yml`; nothing re-prompts. `.mcp.json`, the Playwright block in `.opencode/opencode.jsonc`, and all five real work units are untouched — `scripts/verify.sh` and `scripts/fmt-file.sh` joined `_skip_if_exists`, so the working formatter survives the update. Four of the five rejected hunks are resolved in favour of upstream. The repo's own jq handling goes: on a jq-less host its PreToolUse path exited 0, letting every command through unchecked, where upstream denies. The round caps added by hand to `architect.md` and `product-owner.md` are now upstream text. Three local hunks are re-applied: - `block-destructive.sh` keeps this repo's matcher. Upstream's single substring grep denies any command that merely quotes a pattern, which blocked two legitimate tool calls during the v0.6.0 adoption. The header now says so, and `copier update` should keep rejecting that hunk. - `PreToolUse` stays anchored to `^Bash$`. Unanchored, it also matches BashOutput and KillShell, whose payloads carry no `.tool_input.command` — and upstream's fail-closed empty-command branch would deny them. - The OpenCode formatter keeps `bash` (not `sh`): `fmt-file.sh` uses `set -o pipefail` and `[[ ]]`, so dash aborts on line 1 and silently formats nothing. `AGENTS.md` keeps the concrete license and squash-merge lines that v0.7.0 replaces with a `_Fill in:` marker and strategy-generic prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`_skip_if_exists` protects these files by design, so `copier update` left them describing a harness that no longer exists: `.agents/README.md` pointed at three sub-READMEs and four playbook skills the update deleted, and `harness-usage.md` still taught the SPIKE-REQUEST marker and the verify skill. Copier's pattern list is matched at any depth, so `README.md` covers every README in the tree, not just the root one. All four were unmodified renders of the previous template version, so they are replaced with the v0.7.0 renders and the local content re-applied: the Browser automation (MCP) section returns to `harness-usage.md`, and `tool-bootstrap.md`'s two `_Fill in:` markers are answered from `pyproject.toml` and the CI matrix rather than left as scaffold. `development/style.md` is deliberately not synced: v0.7.0 replaces its concrete squash-merge rule with strategy-generic prose, and this repo squash-merges. Three register rows record that call and the two other judgements this update needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hook-wiring guards asserted on inline `jq` calls that v0.7.0 removed, so they described plumbing the repo no longer has. They now assert what the new wiring depends on: that all three input-consuming hooks read through `hook-input.sh`, and that each branches on its exit 3 with the posture its role requires — PreToolUse denies, PostToolUse and Stop skip visibly. A hook that ignores exit 3 inherits whatever the surrounding shell does with an empty read, which is exactly how the guard once allowed everything. `TestHookInput` covers the reader itself, including the case no unit test would otherwise reach: that the `python3` fallback returns byte-identical output to `jq`. A backend that disagrees makes the guard's behaviour depend on which machine it runs on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
egparedes
added a commit
that referenced
this pull request
Aug 3, 2026
…-3 stack Evaluated the unowned competing stack (PRs #11/#13, branches ao/ulv-3/template-v0.6.0 and ao/ulv-3/template-v0.7.0) and ported its unique work: - tests/test_harness_config.py: hook-wiring pins, the hook-input.sh backend contract, block-destructive coverage, skill guard (all pass against this branch's implementations unchanged) - .agents/hooks/block-destructive.sh: quote-aware, command-position matcher with a divergence header for future copier updates - scripts/fmt-file.sh: real ruff --force-exclude implementation replacing the template no-op; opencode.jsonc runs it under bash - .claude/settings.json: PreToolUse matcher anchored to ^Bash$ - development/glossary.md: seeded domain vocabulary - development/adr/0010 + decision-register rows: relocation decision recorded (adapted to this PR's actual migration mechanics and to the retained dependency-needs-ADR rule); register marker scoped to report.md lines - src/ulv/model.py docstring no longer points into the unpublished development/ tree; README, tool-bootstrap, PR template refreshed; initial-project-description.md to development/ Rejected: their reverts of this branch's review fixes (hook-input.sh hardening, Stop-hook doc honesty, role-file contradiction fixes, verify skill) and their adoption of the softer dependency-ADR bar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Stacked on #11 — targets
ao/ulv-3/template-v0.6.0, so this diff is thev0.6.0 → v0.7.0 delta only. Merge #11 first.
Why this stacks on #11 rather than restarting from
mainv0.7.0 requires the
docs/→development/relocation #11 performed: it isan upstream breaking change, and the new
_skip_if_existslist protectsdevelopment/*.mdpaths only. Starting frommainwould mean redoing 77 filesof relocation to reach the same place. Exactly one commit of #11 is superseded
(
ea24433, the hook repair), and that is a reconcile, not a reason to discardthe rest — see "Rejected hunks" below.
What v0.7.0 is
Two upstream ADRs:
undocumented hard
jqdependency that degraded differently and silently ateach of three call sites. New
.agents/hooks/hook-input.shparses withjq,falls back to
python3(probed by running it — stock macOS ships a stubthat passes
command -v), and returns distinct exit codes (3 = no parser,4 = bad payload) so each hook picks a posture: PreToolUse denies with the
install remedy, Stop and PostToolUse skip with a visible non-zero exit.
skills merge into their subagent files; the
SPIKE-/EXPLORER-/PLAN-REVISIONmarkers collapse into oneHANDBACK(kind)/RESULT(kind)convention; the example work unit, the
verifyskill, the three.agents/sub-READMEs, and the cursor/mcp modules stop being generated.
Eleven answers drop out of
.copier-answers.yml; nothing re-prompts.What survived untouched
.mcp.json, the Playwright block in.opencode/opencode.jsonc, all five realwork units, and
scripts/fmt-file.sh— the last because v0.7.0 addedscripts/verify.shandscripts/fmt-file.shto_skip_if_exists, so theworking formatter from #11 is now protected rather than at risk.
Rejected hunks and how each was resolved
copier update --conflict rejproduced five. Two were resolved in favour ofupstream outright:
.claude/settings.jsonarchitect.md,product-owner.mdThree local hunks are re-applied on top:
block-destructive.shkeeps this repo's matcher. Upstream's singlesubstring
grepdenies any command that merely quotes a pattern — enoughto block an
rgsearch of the file itself, and it blocked two legitimatetool calls during the v0.6.0 adoption. The header now records the divergence
and says to keep rejecting that hunk; behaviour stays pinned by 23 tests.
This is the one deliberate, recurring cost in this PR: one
.rejpertemplate release on a 60-line file.
PreToolUsestays anchored to^Bash$. Unanchored it also matchesBashOutput/KillShell, whose payloads carry no.tool_input.command— andupstream's new fail-closed empty-command branch would deny them. Verified:
a BashOutput-shaped payload returns rc=2 through the unanchored wiring.
bash, notsh.fmt-file.shusesset -o pipefailand[[ ]]; under dash it aborts on line 1 and formatsnothing, silently, because a formatter's exit code is not surfaced.
AGENTS.mdalso keeps its concrete license and squash-merge lines, whichv0.7.0 replaces with a
_Fill in:marker and strategy-generic prose.Preserved-doc drift
_skip_if_existsis matched at any depth, soREADME.mdcovers every READMEin the tree. That left
.agents/README.mdpointing at three sub-READMEs andfour playbook skills the update had just deleted, and
harness-usage.mdstillteaching
SPIKE-REQUEST:and theverifyskill. All four affected files wereunmodified renders of the previous version, so they are replaced with the
v0.7.0 renders and the local content re-applied — the Browser automation (MCP)
section returns to
harness-usage.md, andtool-bootstrap.md's two_Fill in:markers are answered frompyproject.tomland the CI matrix.development/style.mdis deliberately not synced (see register row2026-07-template-v0.7.0.3).Verification
make verifygreen: 376 tests, lint, format-check, docs build.tests/test_harness_config.py: 33 → 45 tests. The hook-wiring guards movedfrom asserting on inline
jqto asserting thehook-input.shcontract andeach hook's exit-3 posture; new
TestHookInputcovers the reader, includingthat the
python3fallback is byte-identical tojq— a backend thatdisagrees makes the guard machine-dependent.
PreToolUsecommand string, with and withoutjqon PATH: benign allowed, destructive denied, quoted mention allowed.Follow-up worth considering (not in this PR)
Three deny mechanisms still overlap and only one is the improved matcher:
.claude/settings.json'spermissions.denyprefix rules and.opencode/opencode.jsonc's substring globs remain the old style, so theyover-block relative to the hook. Either loosen them to match or upstream the
matcher to the template — the latter would also end the recurring reject.
🤖 Generated with Claude Code