bigpowers 2.90.2 → 2.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/.agents/skills/build-epic/SKILL.md +1 -1
  2. package/.agents/skills/commit-message/SKILL.md +1 -0
  3. package/.agents/skills/release-branch/SKILL.md +4 -5
  4. package/.agents/skills/run-planning/SKILL.md +6 -1
  5. package/.agents/skills/verify-work/SKILL.md +2 -1
  6. package/.cline/skills/build-epic/SKILL.md +1 -1
  7. package/.cline/skills/commit-message/SKILL.md +1 -0
  8. package/.cline/skills/release-branch/SKILL.md +4 -5
  9. package/.cline/skills/run-planning/SKILL.md +6 -1
  10. package/.cline/skills/verify-work/SKILL.md +2 -1
  11. package/.codebuddy/skills/build-epic/SKILL.md +1 -1
  12. package/.codebuddy/skills/commit-message/SKILL.md +1 -0
  13. package/.codebuddy/skills/release-branch/SKILL.md +4 -5
  14. package/.codebuddy/skills/run-planning/SKILL.md +6 -1
  15. package/.codebuddy/skills/verify-work/SKILL.md +2 -1
  16. package/.codex/skills/build-epic/SKILL.md +1 -1
  17. package/.codex/skills/commit-message/SKILL.md +1 -0
  18. package/.codex/skills/release-branch/SKILL.md +4 -5
  19. package/.codex/skills/run-planning/SKILL.md +6 -1
  20. package/.codex/skills/verify-work/SKILL.md +2 -1
  21. package/.copilot/skills/build-epic/SKILL.md +1 -1
  22. package/.copilot/skills/commit-message/SKILL.md +1 -0
  23. package/.copilot/skills/release-branch/SKILL.md +4 -5
  24. package/.copilot/skills/run-planning/SKILL.md +6 -1
  25. package/.copilot/skills/verify-work/SKILL.md +2 -1
  26. package/.cursor/rules/build-epic.mdc +1 -1
  27. package/.cursor/rules/commit-message.mdc +1 -0
  28. package/.cursor/rules/release-branch.mdc +4 -5
  29. package/.cursor/rules/run-planning.mdc +6 -1
  30. package/.cursor/rules/verify-work.mdc +2 -1
  31. package/.gemini/extensions/bigpowers/commands/prompts/build-epic.md +1 -1
  32. package/.gemini/extensions/bigpowers/commands/prompts/commit-message.md +1 -0
  33. package/.gemini/extensions/bigpowers/commands/prompts/release-branch.md +4 -5
  34. package/.gemini/extensions/bigpowers/commands/prompts/run-planning.md +6 -1
  35. package/.gemini/extensions/bigpowers/commands/prompts/verify-work.md +2 -1
  36. package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
  37. package/.gemini/extensions/bigpowers/skills/build-epic/SKILL.md +1 -1
  38. package/.gemini/extensions/bigpowers/skills/commit-message/SKILL.md +1 -0
  39. package/.gemini/extensions/bigpowers/skills/release-branch/SKILL.md +4 -5
  40. package/.gemini/extensions/bigpowers/skills/run-planning/SKILL.md +6 -1
  41. package/.gemini/extensions/bigpowers/skills/verify-work/SKILL.md +2 -1
  42. package/.hermes/skills/build-epic/SKILL.md +1 -1
  43. package/.hermes/skills/commit-message/SKILL.md +1 -0
  44. package/.hermes/skills/release-branch/SKILL.md +4 -5
  45. package/.hermes/skills/run-planning/SKILL.md +6 -1
  46. package/.hermes/skills/verify-work/SKILL.md +2 -1
  47. package/.kilocode/rules/build-epic.md +1 -1
  48. package/.kilocode/rules/commit-message.md +1 -0
  49. package/.kilocode/rules/release-branch.md +4 -5
  50. package/.kilocode/rules/run-planning.md +6 -1
  51. package/.kilocode/rules/verify-work.md +2 -1
  52. package/.mimocode/skills/build-epic/SKILL.md +1 -1
  53. package/.mimocode/skills/commit-message/SKILL.md +1 -0
  54. package/.mimocode/skills/release-branch/SKILL.md +4 -5
  55. package/.mimocode/skills/run-planning/SKILL.md +6 -1
  56. package/.mimocode/skills/verify-work/SKILL.md +2 -1
  57. package/.opencode/skills/build-epic/SKILL.md +1 -1
  58. package/.opencode/skills/commit-message/SKILL.md +1 -0
  59. package/.opencode/skills/release-branch/SKILL.md +4 -5
  60. package/.opencode/skills/run-planning/SKILL.md +6 -1
  61. package/.opencode/skills/verify-work/SKILL.md +2 -1
  62. package/.pi/package.json +1 -1
  63. package/.pi/prompts/build-epic.md +1 -1
  64. package/.pi/prompts/commit-message.md +1 -0
  65. package/.pi/prompts/release-branch.md +4 -5
  66. package/.pi/prompts/run-planning.md +6 -1
  67. package/.pi/prompts/verify-work.md +2 -1
  68. package/.pi/skills/build-epic/SKILL.md +1 -1
  69. package/.pi/skills/commit-message/SKILL.md +1 -0
  70. package/.pi/skills/release-branch/SKILL.md +4 -5
  71. package/.pi/skills/run-planning/SKILL.md +6 -1
  72. package/.pi/skills/verify-work/SKILL.md +2 -1
  73. package/.qwen/skills/build-epic/SKILL.md +1 -1
  74. package/.qwen/skills/commit-message/SKILL.md +1 -0
  75. package/.qwen/skills/release-branch/SKILL.md +4 -5
  76. package/.qwen/skills/run-planning/SKILL.md +6 -1
  77. package/.qwen/skills/verify-work/SKILL.md +2 -1
  78. package/.trae/skills/build-epic/SKILL.md +1 -1
  79. package/.trae/skills/commit-message/SKILL.md +1 -0
  80. package/.trae/skills/release-branch/SKILL.md +4 -5
  81. package/.trae/skills/run-planning/SKILL.md +6 -1
  82. package/.trae/skills/verify-work/SKILL.md +2 -1
  83. package/.windsurf/rules/build-epic.md +1 -1
  84. package/.windsurf/rules/commit-message.md +1 -0
  85. package/.windsurf/rules/release-branch.md +4 -5
  86. package/.windsurf/rules/run-planning.md +6 -1
  87. package/.windsurf/rules/verify-work.md +2 -1
  88. package/.zcode/skills/build-epic/SKILL.md +1 -1
  89. package/.zcode/skills/commit-message/SKILL.md +1 -0
  90. package/.zcode/skills/release-branch/SKILL.md +4 -5
  91. package/.zcode/skills/run-planning/SKILL.md +6 -1
  92. package/.zcode/skills/verify-work/SKILL.md +2 -1
  93. package/CHANGELOG.md +7 -0
  94. package/hooks/pre-tool-use.sh +23 -1
  95. package/package.json +2 -1
  96. package/scripts/convert-legado.sh +1 -0
  97. package/scripts/lib/__pycache__/atomic_write.cpython-310.pyc +0 -0
  98. package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
  99. package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
  100. package/scripts/lib/__pycache__/srp_engine_okf.cpython-310.pyc +0 -0
  101. package/scripts/lib/assert-epic-gate.sh +32 -0
  102. package/scripts/lib/assert-planning-artifacts.sh +65 -0
  103. package/scripts/test-generated-path-hook.sh +57 -0
  104. package/scripts/test-test-lock-hook.sh +100 -0
  105. package/scripts/validate-doctrine.sh +4 -4
  106. package/scripts/verify-adversarial.sh +146 -0
  107. package/scripts/verify-tdd-red-commit.sh +39 -1
  108. package/skills/build-epic/SKILL.md +1 -1
  109. package/skills/commit-message/SKILL.md +1 -0
  110. package/skills/release-branch/SKILL.md +4 -5
  111. package/skills/run-planning/SKILL.md +6 -1
  112. package/skills/verify-work/SKILL.md +2 -1
  113. package/skills-lock.json +5 -5
  114. package/tests/test-artifact-assertions.sh +86 -0
  115. package/tests/test-verify-adversarial.sh +27 -0
@@ -56,13 +56,12 @@ git log main...HEAD --format="%B" | grep -qiE 'co[- ]authored[- ]by' && echo "
56
56
  ### 2a. Security gate
57
57
 
58
58
  - [ ] `specs/security/REVIEW.md` exists and is fresh (matches current branch diff)
59
- - [ ] No unresolved HIGH findings with confidence ≥ 8 (or all documented in `specs/security/EXCEPTIONS.md` with sign-off rationale)
60
59
 
61
- If REVIEW.md is missing or stale → run `security-review` inline. Findings block the merge unless documented in EXCEPTIONS.md.
60
+ ### 2b. Audit-code & Traceability gates (HARD GATES — Issue #182)
62
61
 
63
- ### 2b. Traceability gate
64
-
65
- Run `gate-trace` before merge. FAIL blocks merge; CONCERNS requires explicit override in `specs/state.yaml` (`traceability_override: CONCERNS accepted, reason: <explanation>`). WAIVED if no matrix available.
62
+ - [ ] Run `bash scripts/lib/assert-epic-gate.sh release` (asserts `specs/AUDIT_LATEST.md` passed; blocks release on failure).
63
+ - [ ] No unresolved HIGH security findings with confidence ≥ 8 (or documented in `specs/security/EXCEPTIONS.md`).
64
+ - [ ] Run `gate-trace`. FAIL blocks merge; CONCERNS requires explicit override in `specs/state.yaml`. WAIVED if no matrix available.
66
65
 
67
66
  > **Adversarial refute framing (e45s32):** The final pre-merge check is **refute, not rubber-stamp**. Before declaring ready, actively try to disprove traceability completeness — missing story tags, absent verify evidence, stale security review. Only proceed when refutation fails.
68
67
 
@@ -62,7 +62,12 @@ if written:
62
62
  - `plan-release` — sequence epics by WSJF
63
63
  - `slice-tasks` — cut vertical slices
64
64
 
65
- 4. **Update status** — On successful completion, set `status: done` for that workflow key in `planning-status.yaml`.
65
+ 4. **Mechanical artifact assertion before status update (HARD GATE — Issue #183):**
66
+ Before marking any workflow key `status: done`, run:
67
+ ```bash
68
+ bash scripts/lib/assert-planning-artifacts.sh <workflow-key>
69
+ ```
70
+ If the artifact assertion fails, do NOT advance status. Remediate missing output files before continuing. On success, set `status: done` for that workflow key in `planning-status.yaml`.
66
71
 
67
72
  5. **Advance** — Set `state.yaml` `active_flow: planning` while in this chain. When all required keys are done, set `handoff.next_skill` to `plan-work`.
68
73
 
@@ -62,7 +62,8 @@ Review answers "is the code good?"; Verify answers "does the built thing do what
62
62
  5a. **Blind-spot check** — run `bash scripts/check-blind-spots.sh`. This detects structural quality gaps (verify-gap, test-gap, stale-tag, etc.) beyond percentage coverage. If any HIGH-severity findings exist → **block the verify-work PASS gate**. Findings are written to `specs/blind-spots.json`. MEDIUM/LOW findings warn but don't block.
63
63
  5a2. **Completeness critic (e45s05)** — `bash scripts/lib/completeness-critic.sh`. **BLOCKER** aborts merge gate; WARNING → gaps loop; FILLED = evidence only.
64
64
 
65
- 5b. **NFR Evidence Gate** (P0 only) — Produces go/no-go output on three dimensions: Performance (response time, throughput), Reliability (error rate, recovery), and Operability (logging, health checks). Reads thresholds from `specs/tech-architecture/eNN-TEST_PLAN_LATEST.md` and writes evidence as OKF verification-report bundles to `specs/verifications/NFR-eNNsYY.json`. FAIL on any dimension blocks the gate.
65
+ 5b. **NFR Evidence Gate** (P0 only) — Go/no-go on Performance, Reliability, Operability against `specs/tech-architecture/eNN-TEST_PLAN_LATEST.md`. Evidence written to `specs/verifications/NFR-eNNsYY.json`. FAIL blocks gate.
66
+ 5c. **Adversarial Defect Mutation Gate (HARD GATE — e86 / ab-20)** — `bash scripts/verify-adversarial.sh`. Boundary fuzzing & parameter injection against modified interfaces. Crashes or failures block PASS gate; evidence in `specs/verifications/ADVERSARIAL_LATEST.yaml`.
66
67
  6. **Step-by-step UAT** (skip if P2/P3) — one user-observable action at a time.
67
68
  7. **Gaps loop** — failures → log → `plan-work` → re-verify. Unaddressed HIGH findings from step 5 feed into this loop alongside other quality gaps.
68
69
 
@@ -66,7 +66,7 @@ Orchestrates the **build** flow for a single epic: survey → plan tasks → kic
66
66
  After step 5 (verify-work) completes successfully, step 6 runs `audit-code` automatically in `--gate` mode:
67
67
 
68
68
  1. **Run audit:** Invoke `audit-code --gate` on the complete diff for this story.
69
- 2. **Pass (exit 0):** All checklist sections pass → advance to step 7 (commit-message). Record `epic_cycle.audit_result: pass` in `state.yaml`.
69
+ 2. **Pass (exit 0):** All checklist sections pass → run `bash scripts/lib/assert-epic-gate.sh advance` to mechanically confirm the audit pass, then advance to step 7 (commit-message). Record `epic_cycle.audit_result: pass` in `state.yaml`.
70
70
  3. **Fail (exit 1):** One or more checklist sections fail → **reset `epic_cycle.current_step` to `4`** (develop-tdd) and add the failing section IDs to `completed_steps` as `"1,2,3,4,5,6(fail: ...)"`. Record `epic_cycle.audit_result: fail` in `state.yaml`. Do NOT advance past step 6 until audit passes.
71
71
  4. **Audit artifact:** Full audit report saved to `specs/verifications/AUDIT-<epic>-<story>.md` regardless of pass/fail, for reviewer traceability.
72
72
  5. **Enforce F.I.R.S.T:** After audit-code passes, run `enforce-first --quick` on new/modified tests. Append F.I.R.S.T violations (if any) to the audit report. Failing F.I.R.S.T criteria trigger the same loop-back to step 4.
@@ -36,6 +36,7 @@ description: "Reviews working-tree changes, then drafts a Conventional Commits t
36
36
 
37
37
  ## Checklist before finalizing
38
38
 
39
+ - [ ] **Audit pass confirmed (HARD GATE — Issue #182):** In active epic flow, `bash scripts/lib/assert-epic-gate.sh commit` passes.
39
40
  - [ ] Type matches the **dominant** user-visible outcome (`feat` vs `fix` vs `perf`, etc.).
40
41
  - [ ] **Scope** is a short noun in parentheses if it helps (e.g. `fix(api): …`).
41
42
  - [ ] Breaking changes are explicit (`!` and/or `BREAKING CHANGE:` in the body/footer).
@@ -56,13 +56,12 @@ git log main...HEAD --format="%B" | grep -qiE 'co[- ]authored[- ]by' && echo "
56
56
  ### 2a. Security gate
57
57
 
58
58
  - [ ] `specs/security/REVIEW.md` exists and is fresh (matches current branch diff)
59
- - [ ] No unresolved HIGH findings with confidence ≥ 8 (or all documented in `specs/security/EXCEPTIONS.md` with sign-off rationale)
60
59
 
61
- If REVIEW.md is missing or stale → run `security-review` inline. Findings block the merge unless documented in EXCEPTIONS.md.
60
+ ### 2b. Audit-code & Traceability gates (HARD GATES — Issue #182)
62
61
 
63
- ### 2b. Traceability gate
64
-
65
- Run `gate-trace` before merge. FAIL blocks merge; CONCERNS requires explicit override in `specs/state.yaml` (`traceability_override: CONCERNS accepted, reason: <explanation>`). WAIVED if no matrix available.
62
+ - [ ] Run `bash scripts/lib/assert-epic-gate.sh release` (asserts `specs/AUDIT_LATEST.md` passed; blocks release on failure).
63
+ - [ ] No unresolved HIGH security findings with confidence ≥ 8 (or documented in `specs/security/EXCEPTIONS.md`).
64
+ - [ ] Run `gate-trace`. FAIL blocks merge; CONCERNS requires explicit override in `specs/state.yaml`. WAIVED if no matrix available.
66
65
 
67
66
  > **Adversarial refute framing (e45s32):** The final pre-merge check is **refute, not rubber-stamp**. Before declaring ready, actively try to disprove traceability completeness — missing story tags, absent verify evidence, stale security review. Only proceed when refutation fails.
68
67
 
@@ -62,7 +62,12 @@ if written:
62
62
  - `plan-release` — sequence epics by WSJF
63
63
  - `slice-tasks` — cut vertical slices
64
64
 
65
- 4. **Update status** — On successful completion, set `status: done` for that workflow key in `planning-status.yaml`.
65
+ 4. **Mechanical artifact assertion before status update (HARD GATE — Issue #183):**
66
+ Before marking any workflow key `status: done`, run:
67
+ ```bash
68
+ bash scripts/lib/assert-planning-artifacts.sh <workflow-key>
69
+ ```
70
+ If the artifact assertion fails, do NOT advance status. Remediate missing output files before continuing. On success, set `status: done` for that workflow key in `planning-status.yaml`.
66
71
 
67
72
  5. **Advance** — Set `state.yaml` `active_flow: planning` while in this chain. When all required keys are done, set `handoff.next_skill` to `plan-work`.
68
73
 
@@ -62,7 +62,8 @@ Review answers "is the code good?"; Verify answers "does the built thing do what
62
62
  5a. **Blind-spot check** — run `bash scripts/check-blind-spots.sh`. This detects structural quality gaps (verify-gap, test-gap, stale-tag, etc.) beyond percentage coverage. If any HIGH-severity findings exist → **block the verify-work PASS gate**. Findings are written to `specs/blind-spots.json`. MEDIUM/LOW findings warn but don't block.
63
63
  5a2. **Completeness critic (e45s05)** — `bash scripts/lib/completeness-critic.sh`. **BLOCKER** aborts merge gate; WARNING → gaps loop; FILLED = evidence only.
64
64
 
65
- 5b. **NFR Evidence Gate** (P0 only) — Produces go/no-go output on three dimensions: Performance (response time, throughput), Reliability (error rate, recovery), and Operability (logging, health checks). Reads thresholds from `specs/tech-architecture/eNN-TEST_PLAN_LATEST.md` and writes evidence as OKF verification-report bundles to `specs/verifications/NFR-eNNsYY.json`. FAIL on any dimension blocks the gate.
65
+ 5b. **NFR Evidence Gate** (P0 only) — Go/no-go on Performance, Reliability, Operability against `specs/tech-architecture/eNN-TEST_PLAN_LATEST.md`. Evidence written to `specs/verifications/NFR-eNNsYY.json`. FAIL blocks gate.
66
+ 5c. **Adversarial Defect Mutation Gate (HARD GATE — e86 / ab-20)** — `bash scripts/verify-adversarial.sh`. Boundary fuzzing & parameter injection against modified interfaces. Crashes or failures block PASS gate; evidence in `specs/verifications/ADVERSARIAL_LATEST.yaml`.
66
67
  6. **Step-by-step UAT** (skip if P2/P3) — one user-observable action at a time.
67
68
  7. **Gaps loop** — failures → log → `plan-work` → re-verify. Unaddressed HIGH findings from step 5 feed into this loop alongside other quality gaps.
68
69
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [2.91.0](https://github.com/danielvm-git/bigpowers/compare/v2.90.2...v2.91.0) (2026-11-03)
2
+
3
+
4
+ ### Features
5
+
6
+ * **hooks:** enforce mechanical gates for red-commit, artifacts, and adversarial mutation (e86) ([#191](https://github.com/danielvm-git/bigpowers/issues/191)) ([7a1b090](https://github.com/danielvm-git/bigpowers/commit/7a1b0907aa20962ee71e825c4996aa80c213be3f))
7
+
1
8
  ## [2.90.2](https://github.com/danielvm-git/bigpowers/compare/v2.90.1...v2.90.2) (2026-11-03)
2
9
 
3
10
 
@@ -42,8 +42,30 @@ deny() {
42
42
  # Read input from stdin
43
43
  INPUT=$(cat)
44
44
 
45
- # Extract command using jq (standard in bigpowers)
45
+ # Extract command and file path using jq (standard in bigpowers)
46
46
  COMMAND=$(echo "$INPUT" | jq -r '.command // .tool_input.command // empty')
47
+ FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // .tool_input.file_path // .path // .tool_input.path // .target_file // .tool_input.target_file // empty')
48
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool // .name // .tool_name // empty')
49
+
50
+ # 0. Check file editing rules
51
+ if [ -n "$FILE_PATH" ]; then
52
+ # e86s01: Generated paths are blocked at edit time
53
+ if [[ "$FILE_PATH" =~ ^\.cursor/rules/ ]] || [[ "$FILE_PATH" =~ ^\.gemini/extensions/ ]] || [[ "$FILE_PATH" =~ ^\.windsurf/ ]]; then
54
+ deny "BLOCKED: Generated file '$FILE_PATH' cannot be edited directly. Edit the source SKILL.md instead and run bash scripts/sync-skills.sh."
55
+ fi
56
+
57
+ # e86s02: Tests are locked during fix_bug flow after RED commit
58
+ if [[ "$FILE_PATH" =~ (^|/)test/ ]] || [[ "$FILE_PATH" =~ (^|/)tests/ ]] || [[ "$FILE_PATH" =~ \.test\.[^/]+$ ]] || [[ "$FILE_PATH" =~ _test\.[^/]+$ ]] || [[ "$FILE_PATH" =~ (^|/)test_[^/]+$ ]]; then
59
+ if [ -f specs/state.yaml ] && grep -qE "active_flow:[[:space:]]*fix_bug" specs/state.yaml 2>/dev/null; then
60
+ if git rev-parse --git-dir >/dev/null 2>&1; then
61
+ recent_log=$(git log -n 5 --format=%s 2>/dev/null || true)
62
+ if echo "$recent_log" | grep -qE '^test(\(.+\))?:'; then
63
+ deny "BLOCKED: Test files ($FILE_PATH) are locked during fix_bug after the RED commit. Write the fix in production code (src/), not in tests."
64
+ fi
65
+ fi
66
+ fi
67
+ fi
68
+ fi
47
69
 
48
70
  if [ -z "$COMMAND" ]; then
49
71
  [ "$MODE" = "gemini" ] && echo '{"decision":"allow"}'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.90.2",
3
+ "version": "2.91.0",
4
4
  "description": "73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -68,6 +68,7 @@
68
68
  "zod": "^3.24.2"
69
69
  },
70
70
  "devDependencies": {
71
+ "@ctxo/lang-typescript": "^0.11.0",
71
72
  "@semantic-release/changelog": "^6.0.3",
72
73
  "@semantic-release/exec": "^7.1.0",
73
74
  "@semantic-release/git": "^10.0.1",
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # convert-legado.sh — RELEASE-PLAN.md + SCOPE.md → YAML layout (one-time migration helper)
3
+ # bp-manual-utility: legacy MD spec converter (pre-e44 migration)
3
4
  set -euo pipefail
4
5
  source "$(dirname "${BASH_SOURCE[0]}")/lib/python-env.sh"
5
6
  SPECS="$REPO_ROOT/specs"
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # story: e86 / GH #182
3
+ # Mechanical audit-code pass gate before commit / release.
4
+ set -euo pipefail
5
+
6
+ STAGE="${1:-commit}"
7
+
8
+ AUDIT_PASSED=false
9
+
10
+ # Check specs/AUDIT_LATEST.md
11
+ if [[ -f specs/AUDIT_LATEST.md ]]; then
12
+ if grep -qiE "VERDICT:[[:space:]]*PASS|RESULT:[[:space:]]*PASS" specs/AUDIT_LATEST.md; then
13
+ AUDIT_PASSED=true
14
+ fi
15
+ fi
16
+
17
+ # Check specs/execution-status.yaml
18
+ if [[ "$AUDIT_PASSED" == "false" ]] && [[ -f specs/execution-status.yaml ]]; then
19
+ if grep -qE "audit_passed:[[:space:]]*true" specs/execution-status.yaml 2>/dev/null; then
20
+ AUDIT_PASSED=true
21
+ fi
22
+ fi
23
+
24
+ if [[ "$AUDIT_PASSED" == "false" ]]; then
25
+ echo "ERROR [$STAGE]: audit-code check is missing or did not pass (Issue #182)." >&2
26
+ echo "Remediation: Run audit-code and ensure all checks pass before advancing to $STAGE." >&2
27
+ echo "If audit fails, loop back to develop-tdd to address findings." >&2
28
+ exit 1
29
+ fi
30
+
31
+ echo "PASS [$STAGE]: audit-code verified."
32
+ exit 0
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bash
2
+ # story: e86 / GH #183
3
+ # Mechanical artifact existence assertion for run-planning workflow steps.
4
+ set -euo pipefail
5
+
6
+ STEP="${1:-}"
7
+
8
+ if [[ -z "$STEP" ]]; then
9
+ echo "Usage: bash scripts/lib/assert-planning-artifacts.sh <step_name>" >&2
10
+ exit 1
11
+ fi
12
+
13
+ case "$STEP" in
14
+ survey-context)
15
+ if [[ ! -f specs/tech-architecture/tech-stack.md ]] && [[ ! -f specs/state.yaml ]]; then
16
+ echo "ERROR [survey-context]: Neither specs/tech-architecture/tech-stack.md nor specs/state.yaml exists." >&2
17
+ exit 1
18
+ fi
19
+ ;;
20
+
21
+ scope-work)
22
+ if [[ ! -f specs/product/SCOPE_LATEST.yaml ]] || [[ ! -s specs/product/SCOPE_LATEST.yaml ]]; then
23
+ echo "ERROR [scope-work]: specs/product/SCOPE_LATEST.yaml is missing or empty." >&2
24
+ exit 1
25
+ fi
26
+ ;;
27
+
28
+ research-first)
29
+ # Research can record in specs/product/PRIOR_ART.md or directly in specs/
30
+ if [[ ! -f specs/product/PRIOR_ART.md ]] && [[ ! -f specs/product/SCOPE_LATEST.yaml ]]; then
31
+ echo "ERROR [research-first]: Neither specs/product/PRIOR_ART.md nor specs/product/SCOPE_LATEST.yaml found." >&2
32
+ exit 1
33
+ fi
34
+ ;;
35
+
36
+ elaborate-spec)
37
+ if [[ ! -f specs/planning-context.yaml ]] && [[ ! -f specs/product/SCOPE_LATEST.yaml ]]; then
38
+ echo "ERROR [elaborate-spec]: specs/planning-context.yaml is missing." >&2
39
+ exit 1
40
+ fi
41
+ ;;
42
+
43
+ plan-release)
44
+ if [[ ! -f specs/release-plan.yaml ]] || [[ ! -s specs/release-plan.yaml ]]; then
45
+ echo "ERROR [plan-release]: specs/release-plan.yaml is missing or empty." >&2
46
+ exit 1
47
+ fi
48
+ ;;
49
+
50
+ slice-tasks)
51
+ # Assert at least one task file exists in specs/epics/
52
+ if ! find specs/epics -name "*-tasks.yaml" 2>/dev/null | grep -q .; then
53
+ echo "ERROR [slice-tasks]: No tasks files (*-tasks.yaml) found in specs/epics/." >&2
54
+ exit 1
55
+ fi
56
+ ;;
57
+
58
+ *)
59
+ echo "WARNING: Unknown step '$STEP' — skipping artifact assertion" >&2
60
+ exit 0
61
+ ;;
62
+ esac
63
+
64
+ echo "PASS: Planning artifacts asserted for step '$STEP'."
65
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # story: e86s01
3
+ # Test suite for generated-path edit blocking in pre-tool-use.sh.
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
8
+ HOOK="$REPO_ROOT/hooks/pre-tool-use.sh"
9
+
10
+ pass_count=0
11
+ fail_count=0
12
+
13
+ assert_denied() {
14
+ local label="$1"
15
+ local input="$2"
16
+ local output exit_code=0
17
+ output=$(echo "$input" | bash "$HOOK" 2>&1) || exit_code=$?
18
+ if [ "$exit_code" -ne 0 ] || echo "$output" | grep -q '"decision"[[:space:]]*:[[:space:]]*"deny"'; then
19
+ echo " PASS $label"
20
+ pass_count=$((pass_count + 1))
21
+ else
22
+ echo " FAIL $label (expected deny, got allow: $output)"
23
+ fail_count=$((fail_count + 1))
24
+ fi
25
+ }
26
+
27
+ assert_allowed() {
28
+ local label="$1"
29
+ local input="$2"
30
+ local output exit_code=0
31
+ output=$(echo "$input" | bash "$HOOK" 2>&1) || exit_code=$?
32
+ if [ "$exit_code" -eq 0 ] && ! echo "$output" | grep -q '"decision"[[:space:]]*:[[:space:]]*"deny"'; then
33
+ echo " PASS $label"
34
+ pass_count=$((pass_count + 1))
35
+ else
36
+ echo " FAIL $label (expected allow, got deny: $output)"
37
+ fail_count=$((fail_count + 1))
38
+ fi
39
+ }
40
+
41
+ echo "=== Generated Path Hook Tests (e86s01) ==="
42
+
43
+ assert_denied "Deny direct edit to .cursor/rules" \
44
+ '{"tool": "Edit", "file_path": ".cursor/rules/skill.mdc"}'
45
+
46
+ assert_denied "Deny direct edit to .gemini/extensions" \
47
+ '{"tool": "Write", "file_path": ".gemini/extensions/bigpowers/skills/skill.md"}'
48
+
49
+ assert_allowed "Allow edit to source skills/ directory" \
50
+ '{"tool": "Edit", "file_path": "skills/plan-work/SKILL.md"}'
51
+
52
+ echo ""
53
+ echo "=== Summary: $pass_count passed, $fail_count failed ==="
54
+ if [ "$fail_count" -gt 0 ]; then
55
+ exit 1
56
+ fi
57
+ exit 0
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ # story: e86s02
3
+ # Test suite for test-lock hook and RED-commit verification in pre-tool-use.sh.
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
8
+ HOOK="$REPO_ROOT/hooks/pre-tool-use.sh"
9
+
10
+ pass_count=0
11
+ fail_count=0
12
+
13
+ assert_denied() {
14
+ local label="$1"
15
+ local input="$2"
16
+ local expected_substr="${3:-}"
17
+ local output exit_code=0
18
+ output=$(echo "$input" | bash "$HOOK" 2>&1) || exit_code=$?
19
+ if [ "$exit_code" -ne 0 ] || echo "$output" | grep -q '"decision"[[:space:]]*:[[:space:]]*"deny"'; then
20
+ if [ -n "$expected_substr" ] && ! echo "$output" | grep -qi "$expected_substr"; then
21
+ echo " FAIL $label (expected to contain '$expected_substr', got: $output)"
22
+ fail_count=$((fail_count + 1))
23
+ else
24
+ echo " PASS $label"
25
+ pass_count=$((pass_count + 1))
26
+ fi
27
+ else
28
+ echo " FAIL $label (expected deny, but was allowed: $output)"
29
+ fail_count=$((fail_count + 1))
30
+ fi
31
+ }
32
+
33
+ assert_allowed() {
34
+ local label="$1"
35
+ local input="$2"
36
+ local output exit_code=0
37
+ output=$(echo "$input" | bash "$HOOK" 2>&1) || exit_code=$?
38
+ if [ "$exit_code" -eq 0 ] && ! echo "$output" | grep -q '"decision"[[:space:]]*:[[:space:]]*"deny"'; then
39
+ echo " PASS $label"
40
+ pass_count=$((pass_count + 1))
41
+ else
42
+ echo " FAIL $label (expected allow, but was denied: $output)"
43
+ fail_count=$((fail_count + 1))
44
+ fi
45
+ }
46
+
47
+ echo "=== Test Locking Hook Tests (e86s02) ==="
48
+
49
+ TMP_DIR=$(mktemp -d)
50
+ trap 'rm -rf "$TMP_DIR"' EXIT
51
+
52
+ cd "$TMP_DIR"
53
+ git init -q
54
+ git config user.email "test@bigpowers.local"
55
+ git config user.name "test"
56
+ mkdir -p specs test src
57
+
58
+ cat > specs/state.yaml <<'YAML'
59
+ active_flow: fix_bug
60
+ YAML
61
+
62
+ echo "initial" > src/app.js
63
+ git add .
64
+ git commit -q -m "feat(app): initial commit"
65
+
66
+ # 1. Without a RED commit yet during fix_bug, test edits should be allowed (to author the RED test)
67
+ assert_allowed "Test file edit allowed when authoring RED test" \
68
+ '{"tool": "Edit", "file_path": "test/app.test.js"}'
69
+
70
+ # 2. Commit a RED test
71
+ echo "test red" > test/app.test.js
72
+ git add test/app.test.js
73
+ git commit -q -m "test(app): red test reproducing defect"
74
+
75
+ # 3. Now that a RED commit exists and active_flow is fix_bug, test edits must be LOCKED
76
+ assert_denied "Test file edit denied after RED commit in fix_bug" \
77
+ '{"tool": "Edit", "file_path": "test/app.test.js"}' \
78
+ "locked"
79
+
80
+ assert_denied "Test file write denied after RED commit in fix_bug" \
81
+ '{"tool": "Write", "file_path": "tests/test_other.py"}' \
82
+ "locked"
83
+
84
+ # 4. Source file edits must remain allowed
85
+ assert_allowed "Source file edit allowed after RED commit in fix_bug" \
86
+ '{"tool": "Edit", "file_path": "src/app.js"}'
87
+
88
+ # 5. When active_flow is not fix_bug, test edits are allowed
89
+ cat > specs/state.yaml <<'YAML'
90
+ active_flow: build_epic
91
+ YAML
92
+ assert_allowed "Test file edit allowed when active_flow is not fix_bug" \
93
+ '{"tool": "Edit", "file_path": "test/app.test.js"}'
94
+
95
+ echo ""
96
+ echo "=== Summary: $pass_count passed, $fail_count failed ==="
97
+ if [ "$fail_count" -gt 0 ]; then
98
+ exit 1
99
+ fi
100
+ exit 0
@@ -79,7 +79,7 @@ fi
79
79
  # specs/verifications/reports/ (generated reports), PLAN-evolve-structure.md
80
80
  # (migration plan that documents the before→after paths as historical record).
81
81
  echo "--- [Epic 2] canonical specs/ subpaths ---"
82
- LEGACY_SPECS=$(grep -rE "specs/(requirements|plans|audit)\b" \
82
+ LEGACY_SPECS=$(grep -rE --exclude-dir=.claude --exclude-dir=.venv "specs/(requirements|plans|audit)\b" \
83
83
  --include="*.md" --include="*.sh" --include="*.yaml" --include="*.yml" --include="*.json" . \
84
84
  2>/dev/null \
85
85
  | grep -v "specs/archive\|specs/epics/archive\|docs/archive/\|\.git\|node_modules\|\.gemini\|\.cursor" \
@@ -89,7 +89,7 @@ if [[ "$LEGACY_SPECS" -eq 0 ]]; then
89
89
  doctrine_pass "no legacy specs/ subpaths (requirements/, plans/, audit/)"
90
90
  else
91
91
  doctrine_fail "legacy specs/ subpaths found ($LEGACY_SPECS occurrences)"
92
- grep -rE "specs/(requirements|plans|audit)\b" \
92
+ grep -rE --exclude-dir=.claude --exclude-dir=.venv "specs/(requirements|plans|audit)\b" \
93
93
  --include="*.md" --include="*.sh" --include="*.yaml" --include="*.yml" --include="*.json" . \
94
94
  2>/dev/null \
95
95
  | grep -v "specs/archive\|specs/epics/archive\|docs/archive/\|\.git\|node_modules\|\.gemini\|\.cursor" \
@@ -99,14 +99,14 @@ fi
99
99
 
100
100
  # ── Epic 3: BCP slop guard ────────────────────────────────────────────────────
101
101
  echo "--- [Epic 3] BCP terminology ---"
102
- BCP_SLOP=$(grep -ri "build commit point" . 2>/dev/null \
102
+ BCP_SLOP=$(grep -ri --exclude-dir=.claude --exclude-dir=.venv --exclude-dir=.git --exclude-dir=node_modules "build commit point" . 2>/dev/null \
103
103
  | grep -v "specs/archive\|\.git\|node_modules\|\.gemini\|\.cursor\|validate-doctrine\.sh" \
104
104
  | wc -l | tr -d ' ') || true
105
105
  if [[ "$BCP_SLOP" -eq 0 ]]; then
106
106
  doctrine_pass "no 'Build Commit Point' slop — BCP = Business Complexity Points"
107
107
  else
108
108
  doctrine_fail "'Build Commit Point' found ($BCP_SLOP hits) — canonical term is 'Business Complexity Points'; see docs/references/bcp.md"
109
- grep -ri "build commit point" . 2>/dev/null \
109
+ grep -ri --exclude-dir=.claude --exclude-dir=.venv --exclude-dir=.git --exclude-dir=node_modules "build commit point" . 2>/dev/null \
110
110
  | grep -v "specs/archive\|\.git\|node_modules\|\.gemini\|\.cursor\|validate-doctrine\.sh" | sed -n '1,5p' >&2
111
111
  fi
112
112
 
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ # story: e86
3
+ # Adversarial Defect Mutation & Boundary Fuzzing Gate.
4
+ # Evaluates code changes against null/undefined, empty collections, extreme bounds,
5
+ # and unexpected types before UAT sign-off.
6
+ set -euo pipefail
7
+
8
+ REPO_ROOT="$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null)" || {
9
+ echo "verify-adversarial: not in a git repository" >&2
10
+ exit 1
11
+ }
12
+ cd "$REPO_ROOT"
13
+
14
+ self_test() {
15
+ local tmp
16
+ tmp=$(mktemp -d)
17
+ trap 'rm -rf "$tmp"' RETURN
18
+
19
+ cd "$tmp"
20
+ git init -q
21
+ git config user.email "adversarial@bigpowers.local"
22
+ git config user.name "adversarial"
23
+
24
+ mkdir -p src test
25
+ cat > package.json <<'JSON'
26
+ {"name":"adversarial-fixture","scripts":{"test":"node test/calc.test.js"}}
27
+ JSON
28
+
29
+ # Vulnerable implementation (crashes on undefined brand or null line item, like ab-20)
30
+ cat > src/calc.js <<'JS'
31
+ function applyDiscount(item) {
32
+ // Bug: crashes if item.brand is undefined or item is null
33
+ return item.brand.toUpperCase() === "ACME" ? 0.9 : 1.0;
34
+ }
35
+ module.exports = { applyDiscount };
36
+ JS
37
+
38
+ cat > test/calc.test.js <<'JS'
39
+ const assert = require('assert');
40
+ const { applyDiscount } = require('../src/calc');
41
+ assert.strictEqual(applyDiscount({ brand: 'Acme' }), 0.9);
42
+ assert.strictEqual(applyDiscount({ brand: 'Other' }), 1.0);
43
+ console.log("Acceptance tests PASS");
44
+ JS
45
+
46
+ git add .
47
+ git commit -q -m "feat: initial feature with hidden boundary flaw"
48
+
49
+ # Run self-test probe: probe with { brand: undefined } and {}
50
+ local probe_fail=false
51
+ node -e "
52
+ const { applyDiscount } = require('./src/calc');
53
+ try {
54
+ applyDiscount({});
55
+ } catch (e) {
56
+ process.exit(1);
57
+ }
58
+ " 2>/dev/null || probe_fail=true
59
+
60
+ if [ "$probe_fail" = false ]; then
61
+ echo "FAIL: Expected vulnerable fixture to crash on missing brand"
62
+ return 1
63
+ fi
64
+ echo "PASS: Vulnerable fixture correctly triggered defect on boundary probe"
65
+
66
+ # Now harden the fixture
67
+ cat > src/calc.js <<'JS'
68
+ function applyDiscount(item) {
69
+ if (!item || !item.brand) return 1.0;
70
+ return item.brand.toUpperCase() === "ACME" ? 0.9 : 1.0;
71
+ }
72
+ module.exports = { applyDiscount };
73
+ JS
74
+
75
+ local probe_pass=true
76
+ node -e "
77
+ const { applyDiscount } = require('./src/calc');
78
+ try {
79
+ applyDiscount({});
80
+ applyDiscount(null);
81
+ applyDiscount({ brand: undefined });
82
+ } catch (e) {
83
+ process.exit(1);
84
+ }
85
+ " 2>/dev/null || probe_pass=false
86
+
87
+ if [ "$probe_pass" = false ]; then
88
+ echo "FAIL: Hardened fixture should survive boundary probe"
89
+ return 1
90
+ fi
91
+ echo "PASS: Hardened fixture survived boundary probes"
92
+ echo "verify-adversarial: self-test OK"
93
+ return 0
94
+ }
95
+
96
+ if [[ "${1:-}" == "--self-test" ]]; then
97
+ self_test
98
+ exit $?
99
+ fi
100
+
101
+ echo "=== Adversarial Defect Mutation Gate ==="
102
+
103
+ # 1. Identify changed source files
104
+ CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null || git status --porcelain | awk '{print $2}')
105
+ if [[ -z "$CHANGED_FILES" ]] && git rev-parse HEAD~1 >/dev/null 2>&1; then
106
+ CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || true)
107
+ fi
108
+
109
+ DEFECTS_FOUND=0
110
+ mkdir -p specs/verifications
111
+
112
+ # 2. Check for common unsafe property access patterns on JS/TS files
113
+ for file in $CHANGED_FILES; do
114
+ if [[ "$file" =~ \.(js|ts|mjs)$ ]] && [[ -f "$file" ]] && [[ ! "$file" =~ (^|/)test/ ]] && [[ ! "$file" =~ \.test\. ]]; then
115
+ # Look for unsafe chained property access like item.brand.toLowerCase() or item.prop.subprop
116
+ if grep -nE '\.[a-zA-Z0-9_]+\.(toLowerCase|toUpperCase|trim|length|slice)\(' "$file" | grep -v '\?\.' >/dev/null 2>&1; then
117
+ echo "WARNING [boundary-mutation]: Potential unhandled undefined access without optional chaining in $file:"
118
+ grep -nE '\.[a-zA-Z0-9_]+\.(toLowerCase|toUpperCase|trim|length|slice)\(' "$file" | grep -v '\?\.' | head -n 5
119
+ fi
120
+ fi
121
+ done
122
+
123
+ # 3. If the repository defines an adversarial test script, execute it
124
+ if [[ -f package.json ]] && grep -q '"test:adversarial"' package.json 2>/dev/null; then
125
+ echo "Running project-defined adversarial test suite (npm run test:adversarial)..."
126
+ if ! npm run test:adversarial; then
127
+ DEFECTS_FOUND=$((DEFECTS_FOUND + 1))
128
+ fi
129
+ fi
130
+
131
+ # 4. Write evidence artifact
132
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
133
+ cat > specs/verifications/ADVERSARIAL_LATEST.yaml <<YAML
134
+ verified_at: "$TIMESTAMP"
135
+ gate: verify-adversarial
136
+ defects_found: $DEFECTS_FOUND
137
+ passed: $( [ "$DEFECTS_FOUND" -eq 0 ] && echo "true" || echo "false" )
138
+ YAML
139
+
140
+ if [ "$DEFECTS_FOUND" -gt 0 ]; then
141
+ echo "FAIL: Adversarial mutation gate found $DEFECTS_FOUND defects. Harden edge cases before declaring UAT green." >&2
142
+ exit 1
143
+ fi
144
+
145
+ echo "PASS: Adversarial defect mutation gate passed."
146
+ exit 0