Repository navigation
Description
Problem
Part of #588. Split APM hook reconciliation out of #330 as one reviewable PR. The repository task and published consumer helper currently repeat the same cleanup before APM installation; moving this behavior to Go must preserve settings outside that cleanup.
Evidence on origin/main at a0347a715fab580ece45edfcb3d13509620459a8: the published helper is .devtools/scripts/apm-sync.sh:1, the consumer task and subsequent audit are .devtools/tasks/apm.toml:2, and the repository task and post-audit are mise.toml:827. Behavioral evidence is tests/tasks/adoption.bats:75, tests/tasks/adoption.bats:110, and tests/tools/masked-status.bats:28.
Acceptance criteria
- AC1: Reconciliation removes only the existing cleanup keys:
hooksfrom Claude settings,apmfrom agent hooks, andhooksfrom Codex hooks, preserving all sibling settings. - AC2: A settings document left empty by cleanup is removed, while an absent settings document remains absent until installation needs it.
- AC3: The three legacy APM hook sidecars are absent before installation starts.
- AC4: Each existing APM sync task performs installation once and audits a successful deployment through its existing audit interface.
- AC5: A failed settings operation, installation, or audit fails the invoking task without running later stages or concealing the failure status.
- AC6: Non-functional: repeated reconciliation with unchanged inputs produces the same effective settings and deployment outcome as the frozen shell.
- AC7: Every existing consumer-facing sync entry point ends as a thin shell wrapper that probes the Go subcommand and falls back to the frozen shell when that capability is unavailable throughout N-2 compatibility.
- AC8: Non-functional: supported fixtures preserve status, separate output streams, and final files on Linux, macOS, and Windows.
Scope and non-goals
Include both repository and consumer task behavior, existing helper invocation, all three harness documents and sidecars, unrelated settings, empty documents, absent documents, APM install, and each task's existing audit invocation. The exact current cleanup boundaries are .devtools/scripts/apm-sync.sh:3, .devtools/scripts/apm-sync.sh:9, .devtools/scripts/apm-sync.sh:15, and .devtools/scripts/apm-sync.sh:21. Cover the real local-package adoption case in tests/tasks/adoption.bats:110 without fetching an external package.
Non-goals: changing which hook keys are cleared, introducing new hook ownership rules, replacing APM or the Python audit helper, changing lockfile/deployment policy, rewriting preapproval, or making cleanup/install/audit one rollback transaction. Existing later-stage failures may leave earlier cleanup applied; do not promise restoration the shell does not perform. The unrelated prose adoption and helper-copy assertions in tests/tasks/adoption.bats:37 and tests/tasks/adoption.bats:104 remain unchanged.
Approach
Establish failing reconciliation tests before switching either entry point to Go. Compare both current entry points and the replacement with identical JSON and controlled install/audit outcomes. Preserve the existing consumer task and helper interface. Required thin wrappers probe before touching settings, respect the selected tool version, retain only the frozen N-2 fallback, and do not rerun a failed Go reconciliation through shell.
Dependencies
- Port the pins, vendoring, preapproval and APM sync task logic #330 tracks the task family; pin updating continues to call the existing APM sync task interface.
- Replace complex shell with tested Go, starting with gates and merging #588 supplies the migration requirements.
- Ship the Go binaries as release assets installed through mise's github backend #333 tracks binary distribution; unchanged N-2 consumers must retain a working sync path.
Size
S. One PR for the duplicated reconciliation behavior, existing task wiring, parity coverage, and compatible dispatch.
Verify plan
Write and observe the named replacement tests fail before changing dispatch or removing shell behavior. Use frozen-shell comparisons as the parity oracle; no live protected action is needed.
- AC1, AC2: Failing tests first for each harness document with owned keys plus siblings, owned keys only, no owned key, an empty document, and a missing file. Compare semantic preservation and observable file results with the frozen implementation; port
tests/tasks/adoption.bats:75. - AC3, AC4: Failing tests first with all legacy sidecars and a recorded install/audit trace; assert cleanup precedes exactly one install and each existing task retains its audit. Retain the local-package deploy/audit adoption case from
tests/tasks/adoption.bats:110. - AC5: Failing tests first for malformed/unreadable settings and failures during cleanup, install, and audit. Preserve the failed-inspection regression in
tests/tools/masked-status.bats:28, including plausible partial output, no install after inspection failure, and retained earlier side effects rather than invented rollback. - AC6, AC8: Compare frozen shell and replacement status, stdout, stderr, and final files for both entry points, then repeat successful sync to detect accumulating obsolete hooks. Run Go behavior tests on Linux, macOS, and Windows, with shell wiring tests where Bash is available.
- AC7: Failing wrapper tests first for present, missing, old, and unsupported binaries, consumer-selected version precedence, and no fallback after mutation starts. Run targeted adoption tests,
mise run lintandmise run test, and relevant payload checks; retire only migrated Bats assertions.
Definition of done: D1–D10 apply.
Risks
Cleanup currently removes whole named keys; narrowing or broadening that behavior during the port would change hook ownership. A failed install can leave settings cleared. The replacement must preserve unrelated permissions and local hook settings and cannot use a failed Go run as grounds to execute the mutating shell again.
Operator authority
host or harness config change: The migrated command edits the existing harness hook settings covered by .devtools/scripts/apm-sync.sh:3 and mise.toml:833. Operational use retains the existing authorization for APM reconciliation; changing protected repository harness settings requires the existing owner review process. This issue authorizes no expansion of cleanup scope or configuration policy.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Work type
Projects
- StatusShow more project fieldsBacklog
Activity
tbhb-agent commentedon Oct 26, 2026
Refinement: READY — #800 clearly specifies requirements, parity and N-2 fallback criteria, verification, and authority for moving APM hook reconciliation to Go
Model: gemini-4-argon-high
Checked
tbhb-dev/devtools#800(title, body, labels, parent#330, and 0 comments) and referenced issues#330,#588, and#333againstorigin/mainata0347a715fab580ece45edfcb3d13509620459a8..devtools/scripts/apm-sync.sh:1(22 lines) is the published consumer helper that deletes.hooksfrom.claude/settings.json(.devtools/scripts/apm-sync.sh:3),.apmfrom.agents/hooks.json(.devtools/scripts/apm-sync.sh:9), and.hooksfrom.codex/hooks.json(.devtools/scripts/apm-sync.sh:15), removes empty settings files, deletes.claude/apm-hooks.json,.agents/apm-hooks.json, and.codex/apm-hooks.json(.devtools/scripts/apm-sync.sh:21), and runsapm install(.devtools/scripts/apm-sync.sh:22)..devtools/tasks/apm.toml:2defines["devtools:apm-sync"], runningbash "$DEVTOOLS_DIR/scripts/apm-sync.sh"(.devtools/tasks/apm.toml:5) followed byuv run --script "$DEVTOOLS_DIR/scripts/apm_audit.py"(.devtools/tasks/apm.toml:6).mise.toml:827defines[tasks.apm-sync]withdepends_post = ["lint-apm"](mise.toml:829) and duplicates the same cleanup andapm installsequence (mise.toml:833throughmise.toml:852).tests/tasks/adoption.bats:75tests preserving sibling settings and removing obsolete hooks and sidecars duringdevtools:apm-sync.tests/tasks/adoption.bats:110tests real local-package APM deploy and audit in a clean consumer, whiletests/tasks/adoption.bats:37andtests/tasks/adoption.bats:104cover unrelated prose adoption and helper-copy checks named as non-goals.tests/tools/masked-status.bats:28tests thatjqinspection failures exit with status 23, preserve existing settings files, and preventapm installin both.devtools/scripts/apm-sync.shandmise.tomlapm-sync.AC1–AC8): Each criterion states one observable, testable outcome free of implementation detail (no prescribed internal Go package layout, file structure, or library choice), including non-functional criteria for idempotence/parity (AC6), N-2 thin-wrapper fallback (AC7, matchingdocs/compatibility.md:3), and cross-platform stream/status/file parity on Linux, macOS, and Windows (AC8).apm install, existing audit wiring, and failure semantics without inventing rollback transactions. SizeSfits one PR (21 source lines in.devtools/scripts/apm-sync.sh:1and 21 source lines inmise.toml:827, matchingdocs/script-inventory.md:47anddocs/script-inventory.md:215).#330(parent task-family port;.devtools/tasks/pins.toml:195callsmise run devtools:apm-sync),#588(shell-to-Go migration epic), and#333(release binary assets, mitigated for N-2 consumers byAC7fallback) are real, open issues with accurately classified relationships.AC1–AC8) to concrete failing-first tests, parity oracle comparisons against the frozen shell, and existing regression cases (tests/tasks/adoption.bats:75,tests/tasks/adoption.bats:110,tests/tools/masked-status.bats:28)..agents/agent-skills.conf:4,.agents/agent-skills.conf:8, and.github/CODEOWNERS:6(.claude/settings.jsonis ingolden_paths/constitutional_paths/owner_paths, andmise.tomland.devtools/tasks/are ingate_config_paths).Gaps
None
Split
None; size
Sfits one pull request.Definition of done
The definition of done for #800 (Feature). Tick each item and give its evidence. (Ticked boxes below indicate planning coverage in the issue requirements and verification plan, not completed implementation work.)
## Verify planmapsAC1–AC8to concrete failing-first tests, frozen-shell parity checks, Bats ports (tests/tasks/adoption.bats:75,tests/tasks/adoption.bats:110,tests/tools/masked-status.bats:28),mise run lint, andmise run test, and declaresDefinition of done: D1–D10 apply.## Verify plandeclaresDefinition of done: D1–D10 apply.and## Operator authorityretains repository review requirements.## Verify planrequiresmise run lint,mise run test, cross-platform Go behavior tests (AC8), and payload checks (AC7), and## Operator authorityplusDefinition of done: D1–D10 apply.require owner review for protected paths (.agents/agent-skills.conf:4,.agents/agent-skills.conf:8).check-leaks.shwith the repository's private-strings list, and the leak review of each post. — Planning coverage:## Verify plandeclaresDefinition of done: D1–D10 apply.and runs repository lint gates (mise run lint).## Verify plandeclaresDefinition of done: D1–D10 apply.docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why. — Planning coverage:AC7and## Approachpreserve the documented consumer task and helper interfaces (README.md:164,.devtools/tasks/apm.toml:2,.devtools/scripts/apm-sync.sh:1), and## Verify plandeclaresDefinition of done: D1–D10 apply.provenance:agent-assistedlabel. — Planning coverage:## Verify plandeclaresDefinition of done: D1–D10 apply.## Sizescopes the work toS(one PR covering.devtools/scripts/apm-sync.sh:1,.devtools/tasks/apm.toml:2,mise.toml:827, and parity tests), and## Verify plandeclaresDefinition of done: D1–D10 apply.## Dependencieslinks#330,#588, and#333, and## Verify plandeclaresDefinition of done: D1–D10 apply.Status:comment names the merge commit. — Planning coverage:## Problemand## Sizescope#800as one reviewable PR closing the issue, and## Verify plandeclaresDefinition of done: D1–D10 apply.Round: 1 of 3
fix(apm): match the shell when cleanup fails
test(apm): check the sync command without an installer
feat(apm): move hook reconciliation to Go
fix(apm): match the shell when cleanup fails
test(apm): check the sync command without an installer
fix(apm): preserve shell status and blank settings behavior