Skip to content

MAP: executable validation criteria + fix Claude Code hooks#78

Open
azalio wants to merge 5 commits intomainfrom
research/preconditions-postconditions
Open

MAP: executable validation criteria + fix Claude Code hooks#78
azalio wants to merge 5 commits intomainfrom
research/preconditions-postconditions

Conversation

@azalio
Copy link
Owner

@azalio azalio commented Feb 15, 2026

Summary:

  • Tighten task decomposition + enforcement so each validation criterion is testable, anchored (VC1/VC2/…), and mapped to concrete tests.
  • Update Actor/Monitor templates to require VC→test coverage evidence and treat Monitor valid=false as a hard stop unless user explicitly defers.
  • Fix Claude Code hook setup to match docs: move hooks into .claude/settings.json, remove settings.hooks.json, and use hookSpecificOutput.{hookEventName,additionalContext}/permissionDecision outputs to avoid duplicate hook firing.

Why:

  • Reduce ambiguous acceptance criteria and make quality gates executable.
  • Prevent silent deferrals of Monitor-blocking issues.
  • Eliminate double hook execution and hook schema errors.

Summary by CodeRabbit

  • Documentation

    • Validation criteria standardized (VC1/VCn), require concrete anchors and at least one automated test per criterion; test naming guidance and evidence formats added.
    • Contract/monitoring guidance now enforces test-coverage evidence alongside code evidence and hard-stop semantics for unresolved critical issues.
    • Task-decomposition templates tightened for traceable, testable acceptance criteria.
  • Chores

    • Hook configuration consolidated into a single settings location.
    • Hooks and related tooling now emit standardized JSON allow/deny payloads and unified reminder/context fields instead of non-zero exits.

Copilot AI review requested due to automatic review settings February 15, 2026 21:43
@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

Adds VC-prefixed validation criteria with mandatory test mappings and test-coverage tracking, standardizes hook outputs to structured JSON (hookEventName, additionalContext, permissionDecision/permissionDecisionReason) with exit code 0, and consolidates hook registration into .claude/settings.json. Templates and tests updated to match the new schemas.

Changes

Cohort / File(s) Summary
Validation & Contract Templates
src/mapify_cli/templates/agents/actor.md, src/mapify_cli/templates/agents/monitor.md, src/mapify_cli/templates/agents/task-decomposer.md, src/mapify_cli/templates/commands/map-debate.md, src/mapify_cli/templates/commands/map-efficient.md
Introduce VC-prefixed validation criteria (VC1, VC2, ...), require concrete anchors (endpoint/function + file path), mandate mapping VCn → tests, add deterministic test naming guidance, and extend contract evidence with test_coverage/code_evidence/test_evidence fields.
Monitor/Decision Logic (docs/templates)
.claude/agents/monitor.md, src/mapify_cli/templates/agents/monitor.md
Add Test Coverage Rule: verify both code-path evidence and test coverage per VCn; include validation_criteria_test_coverage in evidence JSON; treat missing coverage as testability issues or hard blockers for security_critical items.
Hook Scripts (behavior/output shape)
.claude/hooks/block-dangerous.sh, .claude/hooks/block-secrets.py, .claude/hooks/post-edit-reminder.py, .claude/hooks/ralph-context-pruner.py, .claude/hooks/safety-guardrails.py, .claude/hooks/workflow-context-injector.py, .claude/hooks/workflow-gate.py, src/mapify_cli/templates/hooks/*
Unify denial signaling to stdout JSON under hookSpecificOutput with hookEventName, permissionDecision: "deny", and permissionDecisionReason; rename appended_text → additionalContext for reminders; introduce deny() helpers where applicable; all paths exit 0 and return empty JSON for allow paths.
Hook Configuration & Templates
.claude/settings.hooks.json (deleted), .claude/settings.json, src/mapify_cli/templates/settings.json, src/mapify_cli/templates/settings.hooks.json (removed)
Remove legacy settings.hooks.json; add hooks section to .claude/settings.json (PreToolUse/PostToolUse/PreCompact/Stop) and update templates to reflect the new registration location.
Runtime & CLI changes
src/mapify_cli/__init__.py, scripts/sync-templates.sh
Stop merging template hooks into local settings; warn when local settings.json contains hooks; adjust template sync to use .claude/settings.json and rsync skills.
Docs & References
CLAUDE.md, CHANGELOG.md, IMPLEMENTATION_SUMMARY.md, docs/USAGE.md, .claude/skills/README.md, src/mapify_cli/templates/CLAUDE.md, src/mapify_cli/templates/skills/README.md, src/mapify_cli/templates/references/workflow-state-schema.md
Replace references to settings.hooks.json with .claude/settings.json, add MAP workflow hard-stop guidance, and update docs/templates to the new hook config path.
Hook Tests
tests/hooks/test_block_dangerous.py, tests/hooks/test_block_secrets.py, tests/test_workflow_context_injector.py, tests/test_workflow_gate.py
Adapt tests to new stdout JSON denial format: add helpers _parse_stdout/_assert_denied/_assert_allowed, assert exit code 0 with structured payloads or empty JSON for allowed flows; update output key names (e.g., additionalContext).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Actor
participant Repository
participant TestRunner
participant Monitor
participant Hook
Actor->>Repository: Submit code + tests (VCn-labeled)
Repository->>TestRunner: Run tests (include vc tests)
TestRunner-->>Repository: test results (PASS/FAIL)
Repository->>Monitor: Provide code_evidence + test_coverage + test_evidence
Monitor-->>Hook: validation result (valid / issues / hard-stop)
Hook-->>Actor: emit hookSpecificOutput JSON (hookEventName, permissionDecision/permissionDecisionReason) and exit 0

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 VC hops numbered, tests in tow,
I map each claim where verifiers go,
Hooks now whisper JSON, neat and kind,
Settings united — old fragments left behind.

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main changes: executable validation criteria (VC-based testing) and Claude Code hook fixes (settings consolidation and output schema updates).
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch research/preconditions-postconditions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the MAP Framework's validation and hook infrastructure by introducing executable validation criteria with stable identifiers (VC1, VC2, etc.) and fixing Claude Code hook configuration to match the official documentation. The changes improve quality gates by making acceptance criteria testable and traceable, while eliminating duplicate hook execution issues.

Changes:

  • Migrated hooks from settings.hooks.json to settings.json and updated hook output schema to use permissionDecision/additionalContext
  • Added VC-prefixed validation criteria with mandatory test mapping across task-decomposer, actor, and monitor agents
  • Enforced hard-stop semantics for Monitor valid=false results

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.claude/settings.json, src/mapify_cli/templates/settings.json Added hooks configuration section; removed separate settings.hooks.json
.claude/settings.hooks.json, src/mapify_cli/templates/settings.hooks.json Deleted (hooks now in settings.json)
.claude/hooks/.py, src/mapify_cli/templates/hooks/.py Updated to use hookSpecificOutput schema with hookEventName, permissionDecision, additionalContext
.claude/hooks/block-dangerous.sh, src/mapify_cli/templates/hooks/block-dangerous.sh Updated to use jq for JSON output and new permission decision schema
.claude/agents/task-decomposer.md, src/mapify_cli/templates/agents/task-decomposer.md Added VC prefix requirement, concrete anchors, and test mapping for validation criteria
.claude/agents/monitor.md, src/mapify_cli/templates/agents/monitor.md Added test coverage verification, hard-stop semantics, and VC→test evidence requirements
.claude/agents/actor.md, src/mapify_cli/templates/agents/actor.md Added validation criteria coverage section and mandatory VC→test mapping
.claude/commands/.md, src/mapify_cli/templates/commands/.md Added VC formatting requirements and test mapping instructions
src/mapify_cli/init.py Removed settings.hooks.json from config files list; added warning for hooks in settings.local.json
scripts/sync-templates.sh Removed settings.hooks.json from sync command
docs/USAGE.md, CLAUDE.md, CHANGELOG.md, etc. Updated documentation references from settings.hooks.json to settings.json
.claude/skills/README.md, src/mapify_cli/templates/skills/README.md Updated hook configuration references
.claude/references/workflow-state-schema.md, src/mapify_cli/templates/references/workflow-state-schema.md Updated hook registration file reference

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 14 to 20
#
# EXIT CODES:
# 0 - Allow command execution
# 2 - Block command execution (dangerous command detected)
# 0 + permissionDecision=deny - Block command execution (preferred)
#
# TESTING:
# echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | bash block-dangerous.sh
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TESTING comment in the file header says "Expected: Exit code 2" but the implementation now uses exit code 0 with permissionDecision=deny. Update line 21 in the TESTING section to reflect the new behavior: "Expected: Exit code 0 with permissionDecision=deny in JSON output".

Copilot uses AI. Check for mistakes.
Comment on lines 14 to 20
#
# EXIT CODES:
# 0 - Allow command execution
# 2 - Block command execution (dangerous command detected)
# 0 + permissionDecision=deny - Block command execution (preferred)
#
# TESTING:
# echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | bash block-dangerous.sh
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TESTING comment in the file header says "Expected: Exit code 2" but the implementation now uses exit code 0 with permissionDecision=deny. Update line 21 in the TESTING section to reflect the new behavior: "Expected: Exit code 0 with permissionDecision=deny in JSON output".

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +116
"Stop": [
{
"description": "Lightweight quality gates - only critical issues",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/end-of-turn.sh",
"timeout": 30,
"description": "Auto-fixes silently, only reports secrets/syntax errors"
}
]
}
]
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Stop hook configuration is missing a "matcher" field. In the deleted settings.hooks.json file (line 60), the Stop hook had "matcher": "*". All other hook event types (PreToolUse, PostToolUse, PreCompact) have matcher fields. The Stop hook should also have a matcher field for consistency and to ensure it triggers correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +116
"Stop": [
{
"description": "Lightweight quality gates - only critical issues",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/end-of-turn.sh",
"timeout": 30,
"description": "Auto-fixes silently, only reports secrets/syntax errors"
}
]
}
]
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Stop hook configuration is missing a "matcher" field. In the deleted settings.hooks.json file (line 60), the Stop hook had "matcher": "*". All other hook event types (PreToolUse, PostToolUse, PreCompact) have matcher fields. The Stop hook should also have a matcher field for consistency and to ensure it triggers correctly.

Copilot uses AI. Check for mistakes.
Comment on lines 56 to 63
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Blocked: rm -rf is prohibited (recursive force delete can cause irreversible data loss)"
}
}'
exit 0
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script now uses jq -n to generate JSON output for denial responses, but there's no fallback if jq is not available. While lines 31-32 have fallback logic for parsing input (using || echo ""), the output generation (lines 56-62, 68-74, etc.) will fail if jq is not installed. Consider adding a check at the beginning of the script to ensure jq is available, or provide a fallback mechanism for JSON output generation.

Copilot uses AI. Check for mistakes.
Comment on lines 56 to 63
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Blocked: rm -rf is prohibited (recursive force delete can cause irreversible data loss)"
}
}'
exit 0
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script now uses jq -n to generate JSON output for denial responses, but there's no fallback if jq is not available. While lines 31-32 have fallback logic for parsing input (using || echo ""), the output generation (lines 56-62, 68-74, etc.) will fail if jq is not installed. Consider adding a check at the beginning of the script to ensure jq is available, or provide a fallback mechanism for JSON output generation.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/mapify_cli/templates/hooks/block-secrets.py (1)

28-29: ⚠️ Potential issue | 🟡 Minor

Stale TESTING doc: still references exit code 2 and stderr.

Line 25 was updated to document the new permissionDecision=deny behavior, but the TESTING example on line 29 still says # Expected: Exit code 2, JSON error on stderr. This should be updated to reflect exit code 0 and JSON on stdout.

📝 Proposed fix
-  # Expected: Exit code 2, JSON error on stderr
+  # Expected: Exit code 0, JSON on stdout with permissionDecision=deny
.claude/hooks/block-secrets.py (1)

28-29: ⚠️ Potential issue | 🟡 Minor

Same stale TESTING comment as the template copy.

Line 29 still references exit code 2 / stderr. Same fix as noted in src/mapify_cli/templates/hooks/block-secrets.py.

.claude/hooks/block-dangerous.sh (1)

20-21: ⚠️ Potential issue | 🟡 Minor

Stale TESTING doc: still says "Expected: Exit code 2".

Same issue as block-secrets — the example expectation wasn't updated to match the new exit-0-with-deny-payload behavior.

📝 Proposed fix
-#   # Expected: Exit code 2
+#   # Expected: Exit code 0, JSON on stdout with permissionDecision=deny
IMPLEMENTATION_SUMMARY.md (1)

148-166: ⚠️ Potential issue | 🟡 Minor

File tree at line 151 incorrectly nests settings.json under hooks/.

The tree shows settings.json as a child of .claude/hooks/, but it actually lives at .claude/settings.json. This is misleading for readers.

 .claude/
 ├── hooks/
 │   ├── workflow-context-injector.py (NEW)
-│   └── settings.json (UPDATED)
 ├── commands/
 │   ├── map-efficient.md (OPTIMIZED - 995→394 lines)
 │   └── map-efficient.md.backup (BACKUP)
+├── settings.json (UPDATED)
src/mapify_cli/templates/hooks/block-dangerous.sh (1)

20-22: ⚠️ Potential issue | 🟡 Minor

Stale test expectation in header comment.

Line 21 still says # Expected: Exit code 2, but the script now exits 0 with a permissionDecision=deny payload on all denial paths. This contradicts the updated EXIT CODES section on line 17.

📝 Proposed fix
 # TESTING:
 #   echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | bash block-dangerous.sh
-#   # Expected: Exit code 2
+#   # Expected: Exit code 0 with permissionDecision=deny in JSON output
🤖 Fix all issues with AI agents
In @.claude/agents/actor.md:
- Around line 403-408: The fenced code block in the "Validation Criteria
Coverage" example in .claude/agents/actor.md is missing a language tag causing
markdownlint MD040; update the triple-backtick fence that wraps the VC1 example
(the block starting with "VC1: <criterion text>") to include a language
identifier such as text (i.e. change ``` to ```text) so the block is
language-tagged and MD040 is satisfied.

In @.claude/hooks/block-dangerous.sh:
- Line 55: The check against dangerous rm flags on the COMMAND string requires
whitespace after the flags and therefore misses cases like "rm -rf.hidden" or
"rm -rf0dir"; update the grep regex used in the rm checks (the pattern currently
'rm\s+(-rf|-fr)\s' and the similar pattern later) to remove the mandatory
trailing \s so it matches flags regardless of the next character, e.g. use
'rm\s+(-rf|-fr)' (or an equivalent pattern that does not require a trailing
space) in the grep -qE calls referencing COMMAND to ensure paths immediately
adjacent to the flags are caught.
🧹 Nitpick comments (3)
src/mapify_cli/templates/hooks/workflow-gate.py (1)

26-31: Minor: test expectation could be more precise.

The testing comment describes both outcomes but doesn't show how to distinguish them. Consider showing the expected JSON for each case.

📝 Suggested improvement
 TESTING:
   echo '{"tool_name": "Edit", "tool_input": {"file_path": "test.py"}}' | python3 workflow-gate.py
-  # Expected: Exit code 0, allow (no workflow state) OR deny with reason (if workflow active + missing steps)
+  # Expected (no workflow state): Exit 0, stdout: {}
+  # Expected (workflow active, missing steps): Exit 0, stdout: {"hookSpecificOutput": {"permissionDecision": "deny", ...}}
.claude/agents/monitor.md (1)

2526-2529: Optional: slightly more formal wording for the hard‑stop reminder.

✍️ Suggested tweak
-- If you set `valid=false`, the workflow MUST fix the issues before proceeding.
+- If you set `valid=false`, the workflow MUST resolve the issues before proceeding.
src/mapify_cli/templates/agents/monitor.md (1)

2526-2529: Optional: slightly more formal wording for the hard‑stop reminder.

✍️ Suggested tweak
-- If you set `valid=false`, the workflow MUST fix the issues before proceeding.
+- If you set `valid=false`, the workflow MUST resolve the issues before proceeding.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
tests/test_workflow_gate.py (1)

26-41: Helpers are well-structured; consider extracting shared utilities.

The _parse_stdout / _assert_denied pattern is now duplicated across three test files (test_block_dangerous.py, test_block_secrets.py, and here). For test code this is tolerable, but a shared tests/helpers/hook_assertions.py module would reduce the maintenance surface when the payload schema evolves again.

Note the subtle API difference: here _assert_denied accepts raw stdout and returns reason, while the other two accept a pre-parsed payload and return None. This inconsistency could trip up contributors.

tests/hooks/test_block_secrets.py (1)

70-73: Minor inconsistency: allowed-path tests don't all verify empty stdout.

test_criterion_3_legitimate_allowed only checks exit_code == 0 but doesn't assert the stdout payload is empty ({}), unlike the denial tests that fully validate the JSON structure. Other test files (e.g., test_block_dangerous.py line 79, 85) do assert _parse_stdout(stdout) == {} for allowed paths. Consider adding that here for consistency.

tests/hooks/test_block_dangerous.py (1)

30-43: Exact duplicate of _parse_stdout / _assert_denied from test_block_secrets.py.

Same observation as in test_workflow_gate.py — these helpers are copy-pasted across test modules. The only difference is the optional parameter name (expected_substring here vs expected_file_fragment in test_block_secrets.py), but the logic is identical. Fine for now, but worth consolidating if the payload schema changes again.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@azalio
Copy link
Owner Author

azalio commented Feb 15, 2026

Addressed review feedback:

  • Updated stale TESTING comments for new hook behavior (exit 0 + stdout JSON permissionDecision=deny) in block-dangerous/block-secrets + templates.
  • Fixed markdownlint MD040 in actor docs by adding fenced code languages.
  • Hardened rm -rf detection in block-dangerous.sh to catch edge cases (e.g., flags adjacent to path) and added jq/python3 fallback for JSON output.
  • Clarified workflow-gate TESTING comment and fixed IMPLEMENTATION_SUMMARY.md tree.

Note: I didn’t add a matcher to Stop hooks because Claude Code docs state Stop ignores matchers (adding one would be cosmetic only).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
IMPLEMENTATION_SUMMARY.md (1)

176-182: ⚠️ Potential issue | 🟡 Minor

Ensure user‑facing docs are updated before release.

The summary still lists a TODO for docs/USAGE.md. Given the hook path + workflow behavior changes, please update README/USAGE (or confirm they were updated elsewhere) before shipping.

Based on learnings: When changing user-facing behavior, update relevant documentation: README.md (quick-start), docs/USAGE.md (workflows and CLI usage), and docs/ARCHITECTURE.md (system design / agents).

tests/hooks/test_block_secrets.py (1)

208-212: ⚠️ Potential issue | 🟠 Major

Allowed-file tests no longer distinguish "allow" from "deny" after the exit-code change.

Since denials now also exit 0, asserting only exit_code == 0 doesn't verify the file was actually allowed. If the hook regresses and starts blocking id_rsa.pub or license.key, these tests will still pass.

test_criterion_3_legitimate_allowed (line 74) already does it correctly with assert _parse_stdout(stdout) == {}. Apply the same pattern here.

Proposed fix (example for one class — apply to all three)
     def test_public_keys_allowed(self, filename):
-        exit_code, _, stderr = run_hook("Read", filename)
-        assert (
-            exit_code == 0
-        ), f"Public key {filename} should be ALLOWED, got exit {exit_code}. stderr: {stderr}"
+        exit_code, stdout, stderr = run_hook("Read", filename)
+        assert exit_code == 0, f"Public key {filename} should be ALLOWED. stderr: {stderr}"
+        assert _parse_stdout(stdout) == {}, f"Public key {filename} should be ALLOWED but got denial: {stdout}"
     def test_generic_key_files_allowed(self, filename):
-        exit_code, _, stderr = run_hook("Read", filename)
-        assert (
-            exit_code == 0
-        ), f"Generic key file {filename} should be ALLOWED, got exit {exit_code}. stderr: {stderr}"
+        exit_code, stdout, stderr = run_hook("Read", filename)
+        assert exit_code == 0, f"Generic key file {filename} should be ALLOWED. stderr: {stderr}"
+        assert _parse_stdout(stdout) == {}, f"Generic key file {filename} should be ALLOWED but got denial: {stdout}"
     def test_normal_files_allowed(self, filename):
-        exit_code, _, _ = run_hook("Read", filename)
-        assert exit_code == 0, f"{filename} should be allowed"
+        exit_code, stdout, _ = run_hook("Read", filename)
+        assert exit_code == 0, f"{filename} should be allowed"
+        assert _parse_stdout(stdout) == {}, f"{filename} should be allowed but got denial: {stdout}"

Also applies to: 228-232, 253-255

🤖 Fix all issues with AI agents
In `@src/mapify_cli/templates/hooks/workflow-gate.py`:
- Around line 151-155: Update the docstring that lists "ENFORCEMENT RULES" to
reflect the actual behavior when workflow state is missing: currently the code
path in the workflow gate (the state is None check in the module/function
handling workflow gating) allows edits (it prints "{}" and exits 0), so change
the bullet that claims missing workflow_state blocks edits to instead state that
missing workflow_state allows edits (i.e., fail-open). Locate the docstring near
the top of src/mapify_cli/templates/hooks/workflow-gate.py that contains the
"ENFORCEMENT RULES" bullets and edit the text to match the actual behavior
implemented by the state is None branch.
🧹 Nitpick comments (1)
src/mapify_cli/templates/hooks/block-dangerous.sh (1)

133-142: git push --force checks use $COMMAND (case-sensitive) while rm checks use $COMMAND_LOWER.

This is intentional since git subcommands are case-sensitive, but worth a brief inline comment to prevent a future contributor from "fixing" this inconsistency.

Comment on lines 151 to 155
if state is None:
# No workflow state = not in MAP workflow mode, allow
# (This prevents breaking non-MAP work)
print(json.dumps({"decision": "approve"}))
print("{}")
sys.exit(0)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update docstring: missing workflow_state now allows edits.

The behavior at Line 151–155 fail‑opens when the state file is missing, but the header still claims it blocks. Please align the “ENFORCEMENT RULES” bullets.

✏️ Suggested docstring fix
-  - Blocks Edit/Write/MultiEdit if workflow_state.json is missing
+  - Allows Edit/Write/MultiEdit if workflow_state.json is missing (fail-open outside MAP)
🤖 Prompt for AI Agents
In `@src/mapify_cli/templates/hooks/workflow-gate.py` around lines 151 - 155,
Update the docstring that lists "ENFORCEMENT RULES" to reflect the actual
behavior when workflow state is missing: currently the code path in the workflow
gate (the state is None check in the module/function handling workflow gating)
allows edits (it prints "{}" and exits 0), so change the bullet that claims
missing workflow_state blocks edits to instead state that missing workflow_state
allows edits (i.e., fail-open). Locate the docstring near the top of
src/mapify_cli/templates/hooks/workflow-gate.py that contains the "ENFORCEMENT
RULES" bullets and edit the text to match the actual behavior implemented by the
state is None branch.

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