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.
- package/.agents/skills/build-epic/SKILL.md +1 -1
- package/.agents/skills/commit-message/SKILL.md +1 -0
- package/.agents/skills/release-branch/SKILL.md +4 -5
- package/.agents/skills/run-planning/SKILL.md +6 -1
- package/.agents/skills/verify-work/SKILL.md +2 -1
- package/.cline/skills/build-epic/SKILL.md +1 -1
- package/.cline/skills/commit-message/SKILL.md +1 -0
- package/.cline/skills/release-branch/SKILL.md +4 -5
- package/.cline/skills/run-planning/SKILL.md +6 -1
- package/.cline/skills/verify-work/SKILL.md +2 -1
- package/.codebuddy/skills/build-epic/SKILL.md +1 -1
- package/.codebuddy/skills/commit-message/SKILL.md +1 -0
- package/.codebuddy/skills/release-branch/SKILL.md +4 -5
- package/.codebuddy/skills/run-planning/SKILL.md +6 -1
- package/.codebuddy/skills/verify-work/SKILL.md +2 -1
- package/.codex/skills/build-epic/SKILL.md +1 -1
- package/.codex/skills/commit-message/SKILL.md +1 -0
- package/.codex/skills/release-branch/SKILL.md +4 -5
- package/.codex/skills/run-planning/SKILL.md +6 -1
- package/.codex/skills/verify-work/SKILL.md +2 -1
- package/.copilot/skills/build-epic/SKILL.md +1 -1
- package/.copilot/skills/commit-message/SKILL.md +1 -0
- package/.copilot/skills/release-branch/SKILL.md +4 -5
- package/.copilot/skills/run-planning/SKILL.md +6 -1
- package/.copilot/skills/verify-work/SKILL.md +2 -1
- package/.cursor/rules/build-epic.mdc +1 -1
- package/.cursor/rules/commit-message.mdc +1 -0
- package/.cursor/rules/release-branch.mdc +4 -5
- package/.cursor/rules/run-planning.mdc +6 -1
- package/.cursor/rules/verify-work.mdc +2 -1
- package/.gemini/extensions/bigpowers/commands/prompts/build-epic.md +1 -1
- package/.gemini/extensions/bigpowers/commands/prompts/commit-message.md +1 -0
- package/.gemini/extensions/bigpowers/commands/prompts/release-branch.md +4 -5
- package/.gemini/extensions/bigpowers/commands/prompts/run-planning.md +6 -1
- package/.gemini/extensions/bigpowers/commands/prompts/verify-work.md +2 -1
- package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.gemini/extensions/bigpowers/skills/build-epic/SKILL.md +1 -1
- package/.gemini/extensions/bigpowers/skills/commit-message/SKILL.md +1 -0
- package/.gemini/extensions/bigpowers/skills/release-branch/SKILL.md +4 -5
- package/.gemini/extensions/bigpowers/skills/run-planning/SKILL.md +6 -1
- package/.gemini/extensions/bigpowers/skills/verify-work/SKILL.md +2 -1
- package/.hermes/skills/build-epic/SKILL.md +1 -1
- package/.hermes/skills/commit-message/SKILL.md +1 -0
- package/.hermes/skills/release-branch/SKILL.md +4 -5
- package/.hermes/skills/run-planning/SKILL.md +6 -1
- package/.hermes/skills/verify-work/SKILL.md +2 -1
- package/.kilocode/rules/build-epic.md +1 -1
- package/.kilocode/rules/commit-message.md +1 -0
- package/.kilocode/rules/release-branch.md +4 -5
- package/.kilocode/rules/run-planning.md +6 -1
- package/.kilocode/rules/verify-work.md +2 -1
- package/.mimocode/skills/build-epic/SKILL.md +1 -1
- package/.mimocode/skills/commit-message/SKILL.md +1 -0
- package/.mimocode/skills/release-branch/SKILL.md +4 -5
- package/.mimocode/skills/run-planning/SKILL.md +6 -1
- package/.mimocode/skills/verify-work/SKILL.md +2 -1
- package/.opencode/skills/build-epic/SKILL.md +1 -1
- package/.opencode/skills/commit-message/SKILL.md +1 -0
- package/.opencode/skills/release-branch/SKILL.md +4 -5
- package/.opencode/skills/run-planning/SKILL.md +6 -1
- package/.opencode/skills/verify-work/SKILL.md +2 -1
- package/.pi/package.json +1 -1
- package/.pi/prompts/build-epic.md +1 -1
- package/.pi/prompts/commit-message.md +1 -0
- package/.pi/prompts/release-branch.md +4 -5
- package/.pi/prompts/run-planning.md +6 -1
- package/.pi/prompts/verify-work.md +2 -1
- package/.pi/skills/build-epic/SKILL.md +1 -1
- package/.pi/skills/commit-message/SKILL.md +1 -0
- package/.pi/skills/release-branch/SKILL.md +4 -5
- package/.pi/skills/run-planning/SKILL.md +6 -1
- package/.pi/skills/verify-work/SKILL.md +2 -1
- package/.qwen/skills/build-epic/SKILL.md +1 -1
- package/.qwen/skills/commit-message/SKILL.md +1 -0
- package/.qwen/skills/release-branch/SKILL.md +4 -5
- package/.qwen/skills/run-planning/SKILL.md +6 -1
- package/.qwen/skills/verify-work/SKILL.md +2 -1
- package/.trae/skills/build-epic/SKILL.md +1 -1
- package/.trae/skills/commit-message/SKILL.md +1 -0
- package/.trae/skills/release-branch/SKILL.md +4 -5
- package/.trae/skills/run-planning/SKILL.md +6 -1
- package/.trae/skills/verify-work/SKILL.md +2 -1
- package/.windsurf/rules/build-epic.md +1 -1
- package/.windsurf/rules/commit-message.md +1 -0
- package/.windsurf/rules/release-branch.md +4 -5
- package/.windsurf/rules/run-planning.md +6 -1
- package/.windsurf/rules/verify-work.md +2 -1
- package/.zcode/skills/build-epic/SKILL.md +1 -1
- package/.zcode/skills/commit-message/SKILL.md +1 -0
- package/.zcode/skills/release-branch/SKILL.md +4 -5
- package/.zcode/skills/run-planning/SKILL.md +6 -1
- package/.zcode/skills/verify-work/SKILL.md +2 -1
- package/CHANGELOG.md +7 -0
- package/hooks/pre-tool-use.sh +23 -1
- package/package.json +2 -1
- package/scripts/convert-legado.sh +1 -0
- package/scripts/lib/__pycache__/atomic_write.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_okf.cpython-310.pyc +0 -0
- package/scripts/lib/assert-epic-gate.sh +32 -0
- package/scripts/lib/assert-planning-artifacts.sh +65 -0
- package/scripts/test-generated-path-hook.sh +57 -0
- package/scripts/test-test-lock-hook.sh +100 -0
- package/scripts/validate-doctrine.sh +4 -4
- package/scripts/verify-adversarial.sh +146 -0
- package/scripts/verify-tdd-red-commit.sh +39 -1
- package/skills/build-epic/SKILL.md +1 -1
- package/skills/commit-message/SKILL.md +1 -0
- package/skills/release-branch/SKILL.md +4 -5
- package/skills/run-planning/SKILL.md +6 -1
- package/skills/verify-work/SKILL.md +2 -1
- package/skills-lock.json +5 -5
- package/tests/test-artifact-assertions.sh +86 -0
- 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
|
-
|
|
60
|
+
### 2b. Audit-code & Traceability gates (HARD GATES — Issue #182)
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Run `gate-trace
|
|
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. **
|
|
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) —
|
|
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
|
-
|
|
60
|
+
### 2b. Audit-code & Traceability gates (HARD GATES — Issue #182)
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Run `gate-trace
|
|
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. **
|
|
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) —
|
|
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
|
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -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.
|
|
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"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|