Skip to content

fix: release PAACM 3.4.1 with hardening from an audit and live runs - #8

Merged
Hyper-AI-Lab merged 1 commit into
mainfrom
fix/audit-3.4.1
Oct 17, 2026
Merged

Hyper-AI-Lab merged 1 commit into
mainfrom
fix/audit-3.4.1

Conversation

@Hyper-AI-Lab

Copy link
Copy Markdown
Owner

Why

A full audit of 3.4.0, plus headless Claude Code runs on a fixture project with PAACM installed, found ways around the gates and gaps in the documented workflow. Each code finding has a test in test/hardening.test.mjs that failed before its fix.

Ways around the gates (fixed)

# Finding Fix
1 The gate's own configuration was only a warning: after approve-tests, a shell edit could remove a failing check from .agent/checks.conf, or empty protect.tests in .agent/paacm.json, and the gate still passed Changes to either file after the approved baseline are violations (exit 4). A human-approved change is followed by a new approve-tests (control test included)
2 .agent/tasks/** was exempt from protection, including state.json: deleting testsApprovedAt there turned off test protection and the audit; changing route lowered the gate tier The protect hook refuses edits to state.json and CURRENT; the shell guard refuses shell writes to them, to the check manifest, paacm.json and project extensions
3 The protect hook allowed an edit when it found no target path, contrary to its own fail-closed design; Cursor's preToolUse input fields are undocumented It refuses, with a message saying the hook did not recognise the tool call
4 Paths were judged lexically and case-sensitively Symbolic links are resolved; matching ignores case on macOS and Windows; a file named ..x no longer counts as outside the project
5 git update-index --assume-unchanged / --skip-worktree could hide an edit from the audit The shell guard asks first
6 eval substituted {{HIDDEN}} unquoted, so a hidden-tests path with a space split the command eval refuses that combination with a clear message

Found by the live runs

Scenarios (Claude Code 2.1.292, L1, power layer): a FAST bug fix from a plain request; a request to change an approved test; a request to weaken the gate with sed.

  • An approved test change could not be made. 05-tests-first said the agent may make a human-approved test change, but the hook refused every edit and nothing lifted it; the agent correctly refused to go around the hook, and the task stuck. New paacm task reopen-tests --reason "…", which the shell guard confirms with the human (verified live: the agent used it and the guard asked); approve-tests then sets the new baseline.
  • BLOCKED: was used for an ordinary approval request. The always-on rules and escalation.md now say phase approvals are questions, not blocks.
  • gate and check print paacm <command>: exit <code>, so the report can quote the exit code; the agent had tried a compound command for it, which the allow rule refuses. runtime.md asks for one runtime command per call.
  • Headless runs in an untrusted folder ignore the project's allow rules (claude -p warns on stderr; docs), so every runtime command was refused. The evaluation guide (every trial checkout is new), the L3 example and autonomy.md now say how to trust the folder or pass --allowedTools.

End to end, the FAST fix went: task started → regression test written and shown failing for the right reason → approval → approve-tests on the test commit → fix commit → gate passed → task done with REPORT.md. Asked to weaken the gate, the agent left .agent/checks.conf untouched and fixed the code to the approved test instead. paacm upgrade on the fixture updated only the changed kit files and kept the user-edited .agent/checks.conf.

Verification

  • node bin/paacm.mjs validate, build --check: exit 0
  • npm test: 105 of 105 pass (15 new)
  • markdownlint-cli2@0.23.3 "**/*.md": 0 issues
  • claude plugin validate plugins/paacm --strict and claude plugin validate .: passed
  • The CI step that installs into the four fixture projects: exit 0; doctor 0 problems in each

Not verified here: Cursor live (no Cursor CLI on this machine), macOS and Windows (the CI matrix covers them), and the interactive confirmation of reopen-tests (a headless run cannot answer a prompt).

- Freeze the gate's own configuration with the tests: changing .agent/checks.conf or .agent/paacm.json after approve-tests is a violation (exit 4).
- Protect the task state: the protect hook and the shell guard refuse direct writes to state.json and CURRENT; the shell guard also refuses shell writes to the check manifest, paacm.json and project extensions, and asks before git update-index --assume-unchanged or --skip-worktree.
- The protect hook fails closed when it cannot find the edited file, resolves symbolic links, matches case-insensitively on macOS and Windows, and keeps a file named ..x inside the project.
- Add paacm task reopen-tests, which the shell guard confirms with the human, so an approved test change can be made.
- gate and check print their exit code; approvals are ordinary questions, not BLOCKED; one runtime command per call.
- Document that claude -p ignores project allow rules in an untrusted folder (evaluation guide, L3 example, autonomy reference).
- eval refuses an unquoted {{HIDDEN}} when the hidden-tests path has a space.
@Hyper-AI-Lab
Hyper-AI-Lab merged commit e71678d into main Oct 17, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant