-
Notifications
You must be signed in to change notification settings - Fork 0
Pipeline Plan 364
Plan written to .claude/pipeline-artifacts/plan.md. Here's the summary:
2 files to modify (no new files):
-
scripts/lib/pipeline-quality-checks.sh— add SHA helpers + stamp all 5 artifact write paths -
scripts/lib/pipeline-intelligence.sh— guard all read paths + cycle file cleanup
11 tasks, split into two parts:
Part 1 — SHA anchoring (Tasks 1-8):
- New
_pipeline_head_sha()+pipeline_artifact_is_current()functions - Stamp
created_at_commitin all 5 artifacts (JSON uses per-finding stamp matchingcompound-audit.sh:556; MD/log uses first-line comment) - Guard all reads in
_extract_blocking_items()and convergence detection with SHA freshness checks - Backward compatible: artifacts without SHA pass through
Part 2 — Cycle file cleanup (Tasks 9-10):
-
_cleanup_cycle_files()removesnegative-review-cycle*.mdat stage entry and all exit paths
Key design decision: Per-finding SHA stamp for JSON (Approach A) — keeps the array format intact so all existing .[] | select(...) reads work unchanged. Rejected wrapper-object approach (B) due to 3+ read site breakage.
eated_at_commit, findings: [...]}| Clean top-level field | Breaks all.[] | select(...)reads (3+ sites); larger blast radius | Rejected | | C | Sidecar.sha` files | Zero format change | Doubles file count; race conditions; orphan risk | Rejected |
| Risk | Mitigation |
|---|---|
| Detached HEAD / shallow clone |
git rev-parse --short HEAD with fallback to empty string; empty = pass-through |
| Old artifacts without SHA |
pipeline_artifact_is_current() returns 0 when no SHA found |
jq unavailable |
Already a project dependency; guarded with 2>/dev/null
|
| JSON array read sites | Per-finding stamp keeps array format; .[0].created_at_commit for validation |
pipeline-quality-checks.sh (WRITERS)
+-- _pipeline_head_sha() [NEW helper]
+-- pipeline_artifact_is_current() [NEW validator]
+-- quality_check_security() -> security-audit.log (+ "# created_at_commit: <sha>" header)
+-- quality_check_adversarial() -> adversarial-review.json (+ per-finding created_at_commit)
| -> adversarial-review.md (+ "created_at_commit: <sha>" line 1)
+-- quality_check_negative() -> negative-review.md (+ "created_at_commit: <sha>" line 1)
+-- quality_check_dod() -> dod-audit.md (+ "created_at_commit: <sha>" line 1)
pipeline-intelligence.sh (READERS + CLEANUP)
+-- _extract_blocking_items() -> guards each artifact read with is_current()
+-- convergence detection (1822-71) -> guards adversarial + negative reads with is_current()
+-- _cleanup_cycle_files() [NEW helper]
+-- stage_compound_quality() -> calls _cleanup_cycle_files() before every return
compound-audit.sh (READ-ONLY REFERENCE)
+-- line 556: existing created_at_commit pattern (per-finding jq stamp)
# Returns HEAD short SHA (8 chars), or "" on failure. Never errors.
_pipeline_head_sha() -> string
# Returns 0 if artifact SHA matches HEAD. Pass-through when SHA absent.
# JSON: reads .[0].created_at_commit (per-finding stamp).
# MD: reads "created_at_commit: <sha>" from first 5 lines.
# LOG: reads "# created_at_commit: <sha>" from first 3 lines.
pipeline_artifact_is_current(file: path, [sha_field: string]) -> 0|1
# Removes negative-review-cycle*.md from ARTIFACTS_DIR. No-op if none exist.
_cleanup_cycle_files() -> void-
Write path:
quality_check_*()calls_pipeline_head_sha()-> stamps SHA into artifact -
Read path:
_extract_blocking_items()callspipeline_artifact_is_current()before reading each artifact -> skips if stale -
Convergence: Same SHA guard before counting issues from
adversarial-review.*andnegative-review.md -
Cleanup:
stage_compound_quality()removesnegative-review-cycle*.mdat all exit points
-
_pipeline_head_sha()never fails -- returns empty string on error -
pipeline_artifact_is_current()returns 0 (pass-through) when SHA is empty or field is missing - JSON artifacts:
jqextraction with2>/dev/null || true - Markdown artifacts:
sedextraction with|| true
| File | What Changes |
|---|---|
scripts/lib/pipeline-quality-checks.sh |
Add _pipeline_head_sha() + pipeline_artifact_is_current() (after line 74). Stamp SHA in quality_check_security (~line 102), quality_check_adversarial (~lines 659, 675, 726), quality_check_negative (~line 815), quality_check_dod (~line 962) |
scripts/lib/pipeline-intelligence.sh |
Guard reads in _extract_blocking_items() (lines 1051, 1060, 1070, 1079, 1085, 1094). Guard convergence reads (lines 1826, 1831, 1836). Add _cleanup_cycle_files(). Call cleanup before returns at lines ~2175, ~2204, ~2216 |
No new files. compound-audit.sh is untouched.
Insert after pipeline_artifact_is_fresh() (line 74). The validation function:
- For
.json: reads.[0].created_at_commitvia jq (per-finding stamp pattern from compound-audit.sh) - For
.md: readscreated_at_commit: <sha>from first 5 lines via sed - For
.log: reads# created_at_commit: <sha>from first 3 lines via sed - Returns 0 (pass-through) when no SHA found in artifact or HEAD unavailable
- Compares with prefix matching (short SHA lengths may vary)
After tee "$audit_log" completes, prepend a # created_at_commit: <sha> header line.
Use portable prepend: { echo "# created_at_commit: $sha"; cat "$audit_log"; } > tmp && mv tmp "$audit_log"
JSON path (line 659): After writing adversarial-review.json, stamp each finding with jq --arg c "$sha" '[.[] | . + {created_at_commit: $c}]' (matches compound-audit.sh:556 pattern exactly).
MD path (lines 675 and 726): After writing adversarial-review.md, prepend created_at_commit: <sha> line.
After echo "$review_output" > negative-review.md, prepend created_at_commit: <sha> line.
After echo -e ... > dod-audit.md, prepend created_at_commit: <sha> line.
Add && pipeline_artifact_is_current "$ARTIFACTS_DIR/<file>" to each -f check for all 6 artifacts.
Add && pipeline_artifact_is_current to the -f checks for adversarial-review.md (1826), adversarial-review.json (1831), and negative-review.md (1836).
Simple function: rm -f "$ARTIFACTS_DIR"/negative-review-cycle*.md 2>/dev/null || true
Call at:
- Stage entry (clean stale files from prior runs)
- Line ~2175 (quality gate failed return)
- Line ~2204 (quality gate passed return)
- Line ~2216 (exhausted cycles return)
./scripts/sw-pipeline-test.sh and npm test
- Task 1: Add
_pipeline_head_sha()helper to pipeline-quality-checks.sh (after line 74) - Task 2: Add
pipeline_artifact_is_current()validator to pipeline-quality-checks.sh - Task 3: Stamp SHA in
quality_check_security()-- prepend comment to security-audit.log - Task 4: Stamp SHA in
quality_check_adversarial()-- per-finding stamp in JSON, prepend in MD - Task 5: Stamp SHA in
quality_check_negative()-- prepend line in negative-review.md - Task 6: Stamp SHA in
quality_check_dod()-- prepend line in dod-audit.md - Task 7: Guard all 6 artifact reads in
_extract_blocking_items()withpipeline_artifact_is_current() - Task 8: Guard convergence detection reads (lines 1826-1864) with
pipeline_artifact_is_current() - Task 9: Add
_cleanup_cycle_files()helper to pipeline-intelligence.sh - Task 10: Wire
_cleanup_cycle_files()into allstage_compound_quality()exit paths + entry - Task 11: Run tests --
./scripts/sw-pipeline-test.shandnpm test
-
Unit-level: Run
./scripts/sw-pipeline-test.sh-- existing pipeline tests exercisestage_compound_quality,_extract_blocking_items, and quality checks -
Manual verification:
- Run a compound_quality cycle; inspect artifacts for
created_at_commitfield/line - Manually commit after artifact creation, verify
pipeline_artifact_is_currentreturns 1 - Verify no
negative-review-cycle*.mdfiles remain after stage completion
- Run a compound_quality cycle; inspect artifacts for
- Backward compat: Test with artifacts that lack SHA stamps -- should pass through (return 0)
-
_pipeline_head_sha()returns 8-char short SHA or empty string -
pipeline_artifact_is_current()correctly validates SHA for .json, .md, .log formats -
pipeline_artifact_is_current()returns 0 (pass-through) for artifacts without SHA field - All 5 artifact write paths stamp
created_at_commit -
_extract_blocking_items()skips stale artifacts - Convergence detection skips stale artifacts
-
negative-review-cycle*.mdfiles are cleaned up at stage entry and all exit paths -
./scripts/sw-pipeline-test.shpasses -
npm testpasses - No breaking changes to existing artifact consumers (backward compat)
| Threat | Applies? | Analysis |
|---|---|---|
| Spoofing | No | SHA is read from local git, not user input |
| Tampering | Low | Attacker with artifact write access already has repo access |
| Repudiation | No | Event logging already covers audit trail |
| Info Disclosure | No | SHA is not sensitive |
| DoS | No | SHA check adds negligible overhead |
| Elevation | No | No auth/authz boundaries affected |
Auth Flow, Input Validation, Security Checklist: Not applicable -- internal pipeline plumbing with no user input, no network boundaries, no credentials.
Not applicable -- shell script infrastructure change, no API/UI components.