diff --git a/.claude/skills/validate-architecture/SKILL.md b/.claude/skills/validate-architecture/SKILL.md index e9b8b5b5e..ff4a0c125 100644 --- a/.claude/skills/validate-architecture/SKILL.md +++ b/.claude/skills/validate-architecture/SKILL.md @@ -130,6 +130,22 @@ architecture.md:L — Process Engine marked "OUT OF SCOPE" but routers/proces Suggested edit: either remove the OUT OF SCOPE tag (if the module is in fact live), or remove the routers (if it is truly dormant). ``` +### Step 2c: Filter Stale Citations + +Before generating the report, validate every `file:line` citation produced in Steps 2a and 2b against the current working tree. The skill is sometimes run on a snapshot taken just before a large deletion PR lands; without this filter, the report (and any auto-created issue) cites paths that no longer exist. + +For each cited path (always quote `"$path"` — citations may contain spaces or shell metacharacters): + +```bash +git ls-files --error-unmatch "$path" >/dev/null 2>&1 && echo exists || echo dropped +``` + +- **If `dropped`**: remove the citation from the violation. Do not retain "ghost" line numbers from a previous tree. +- **After filtering**, if an invariant's violation list is empty, downgrade its status from `FAIL` to `PASS (after stale-citation filter)` and record the dropped citation count in the report so the reader can see what was removed. +- **If the only violations cited paths that no longer exist**, do not propagate this invariant to Step 4's issue-creation trigger. + +This step exists because of issue #479: a 2026-04-24 run cited 11 paths that were deleted by commit e901108 (#430) the same day. None of those P1-critical citations were real on `main`, but the unfiltered report produced a `priority-p1` issue against `main`. + ### Step 3: Generate Report Output two sections: @@ -162,9 +178,9 @@ Output two sections: - architecture.md:L — "
" marked out-of-scope but . Suggested edit: . ``` -### Step 4: Create Issue if Critical +### Step 4: Create or Update Issue if Critical -Create a GitHub issue when any of these fire: +Create or update a GitHub issue when any of these fire (after the Step 2c stale-citation filter has run): **P0-P1 invariants** (critical — break runtime or security): - #1 Three-Layer Backend (layer violations cause maintenance debt) @@ -176,7 +192,41 @@ Create a GitHub issue when any of these fire: - D1 count mismatches with >25% divergence - D2 any scope contradiction (dormant-but-live modules) -If any fire, create issue: +**Dedupe guard — required before any `gh issue create`:** + +Compute a fingerprint over the post-filter findings, then check for an existing open issue with the same fingerprint. The fingerprint is wrapped in an HTML comment marker (``) inside the issue body so the dedupe key is self-evidently programmatic and won't collide with prose mentions of "fingerprint" in unrelated issues. + +```bash +COMMIT_SHA=$(git rev-parse --short HEAD) +# Sorted, comma-separated list of invariant numbers that fired post-filter, +# e.g. "1,3" or "8,14" +FINGERPRINT=$(printf '%s\n' "${FIRED_INVARIANTS[@]}" | sort -n | paste -sd, -) +FINGERPRINT_MARKER="validate-architecture::fingerprint=$FINGERPRINT" + +# Find any open automated arch-validation issue with the exact marker +EXISTING=$(gh issue list --repo abilityai/trinity \ + --label "automated,priority-p1" --state open \ + --search "in:body \"$FINGERPRINT_MARKER\"" \ + --json number,title --jq '.[0].number') +``` + +**Branch on `$EXISTING`. The two paths are mutually exclusive — execute exactly one.** + +**Path A — `$EXISTING` is non-empty (matching open issue found): COMMENT, then STOP.** + +```bash +gh issue comment "$EXISTING" --repo abilityai/trinity --body "Re-run on \`$COMMIT_SHA\` ($(date -u +%Y-%m-%d)): same invariants still failing (\`$FINGERPRINT\`). See attached fresh report. + +[fresh report body] + +" +``` + +After commenting, **DO NOT** execute Path B. The skill workflow ends here for this run. + +**Path B — `$EXISTING` is empty (no matching open issue): CREATE a new issue.** + +Only run this block when Path A did not run. ```bash gh issue create \ @@ -185,10 +235,11 @@ gh issue create \ --body "## Automated Architecture Validation Report **Date**: $(date -u +%Y-%m-%d) +**Commit**: $COMMIT_SHA ### Critical Invariant Violations (P0-P1) -[List each P0-P1 violation with invariant number, file:line, description] +[List each P0-P1 violation with invariant number, file:line, description — Step 2c-filtered, no stale paths] ### Doc Drift — Suggested architecture.md Edits @@ -198,12 +249,22 @@ gh issue create \ 1. [Prioritized fix for each finding] +### Dedupe Notes + +- This issue is keyed by \`$FINGERPRINT_MARKER\` (sorted invariant numbers). +- Future skill runs with the same fingerprint will comment on this issue rather than open a new one. +- Close this issue once the cited invariants pass on \`main\`. + + + --- *Generated by scheduled /validate-architecture run*" \ --label "type-bug,priority-p1,automated" ``` -If nothing critical fires, skip issue creation — report only logged to execution history. +**Concurrency caveat**: this dedupe is best-effort, not atomic. Two runners executing simultaneously could both see `$EXISTING` empty and both create issues. GitHub provides no atomic compare-and-create primitive. The next run with the same fingerprint will detect both open issues and comment on the first; the duplicate can be closed manually with `Closes #`. In practice this is rare because the skill is scheduler-driven (single runner). + +If nothing critical fires after Step 2c's filter, skip issue creation — report only logged to execution history. **Do not create an issue solely on pre-filter results.** ## Outputs