Skip to content

feat(sweep-tracker): hierarchical estate-sweep Markdown report#62

Merged
hyperpolymath merged 3 commits into
mainfrom
feat/sweep-tracker-report
May 27, 2026
Merged

feat(sweep-tracker): hierarchical estate-sweep Markdown report#62
hyperpolymath merged 3 commits into
mainfrom
feat/sweep-tracker-report

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Adds panic-attack sweep-tracker subcommand — an issue-#32-shaped sweep
tracker derived from the per-finding (issue #33 S1) and campaign-state
(issue #33 S2) hexad stores. Complements (does not replace) the existing
per-finding campaign status table.

Distinct from campaign status:

  • Hierarchical, not flat — grouped by repo and/or by category.
  • Estate summary header: total findings, repos, criticals, highs,
    PR-filed, dismissed, open-no-PR.
  • Always sourced from the finding store: a finding with no campaign
    hexad still appears (state open); campaign status shows only
    rows with a campaign event.

CLI

panic-attack sweep-tracker [--verisimdb-dir DIR] [--output FILE]
                          [--by-repo | --by-category]

No flag = both sections. --by-repo / --by-category select one
section only (mutually exclusive via clap arg group).

Output shape

# Estate sweep tracker

_Generated <ISO>_

**Estate summary**: N findings across R repos (C critical, H high).
M PR-filed, D dismissed, U open (no PR).

## By repo

### alpha (2 findings, 1 critical)
- [x] PA001 src/lib.rs:23 — pr-merged ([#42](https://github.com/...))
- [ ] PA004 src/ffi.rs:7  — open
...

Determinism

  • Repos sorted alphabetically.
  • Findings within each repo sorted by (rule_id, file, line, finding_id).
  • Categories sorted by rule_id.

Implementation

  • New module src/sweep_tracker/ with public render_report(base_dir, shape) and ReportShape::{ByRepo, ByCategory, Both} (default Both).
  • Reuses storage::load_finding_hexads / storage::load_campaign_hexads
    — no new I/O paths.
  • New CLI variant Commands::SweepTracker wired in src/main.rs.
  • 7 unit tests (1 extra above the spec floor of 5):
    empty-store, by-repo grouping, by-category grouping, campaign-state
    join (open / pr-merged / dismissed), deterministic ordering,
    both-shape ordering, PR-number label parser.

Notes on base

This PR depends on issue-#33 S1 (feat/issue-33-s1-finding-hexads,
PR #55) and S2 (feat/issue-33-s2-campaign-state, PR #56) for the
loaders and CampaignSemantic type. Branched off the S2 tip so the
diff is minimal; base is main as standing policy. Once #55 and #56
land this PR's diff will narrow to just src/sweep_tracker/ plus the
small wiring delta in src/lib.rs + src/main.rs.

Test plan

  • cargo test --lib — 227 tests pass, including 7 new sweep_tracker tests
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --all -- --check clean
  • Smoke-test against a real verisimdb-data/ produced by an
    assemblyline run with PANIC_ATTACK_STORE_FINDING_HEXADS=1

🤖 Generated with Claude Code

hyperpolymath and others added 3 commits May 26, 2026 12:30
Adds a per-WeakPoint hexad path to persist_assemblyline_report so a
batch scan can persist one hexad per finding in addition to the existing
aggregate hexad. Subject identity is `finding:<repo>:<file>:<line>:<category>`,
chosen for cross-run stability so the upcoming S2 (campaign register-pr)
and S3 (query) slices can join on it without diffing JSON.

New public surface:
- HexadSemantic gains an optional `finding: Option<FindingSemantic>`
  (additive, skip_serializing_if = none → existing consumers unaffected).
- FindingSemantic carries finding_id / repo / file / line / category /
  rule_id / rule_name / severity / description / first_seen_run /
  last_seen_run / framework. rule_id and rule_name reuse the canonical
  SARIF mapping (sarif.rs::rule_id / rule_name now pub(crate)).
- build_finding_hexads(report) -> Vec<PanicAttackHexad>.
- STORE_FINDING_HEXADS_ENV = "PANIC_ATTACK_STORE_FINDING_HEXADS" — when
  set non-empty AND StorageMode::VerisimDb is configured,
  persist_assemblyline_report writes one file per finding under
  `<dir>/hexads/findings/`.

Behaviour preserved:
- Default path unchanged (env var off → no per-finding writes).
- Aggregate hexad still emitted in every VerisimDb run.
- Suppressed WeakPoints are skipped, keeping the store aligned with
  fleet/CI counts.

S1 sets first_seen_run == last_seen_run; back-stamping from a prior
hexad is S2's job (per the issue), not S1's.

Tests: 7 new (id stability, category discrimination, count per WP,
suppression skip, canonical rule_id/name, file write + round-trip,
env-var default-off). Full suite: 215 lib + 13 + 16 + 6 + 12 + 3 + 7
+ 12 + 14 + 20 + 10 + 8 + 22 + 22 + 12 + 2 doc — all green. Clippy
clean with -D warnings.

Refs #33.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the second slice of issue #33: a panic-attack campaign subcommand
that tracks the lifecycle of individual findings produced by the
assemblyline per-finding hexad path (S1). State is persisted as
campaign-facet hexads written under <dir>/hexads/campaign/, indexed by
finding_id, append-only — the current state per finding is the newest
campaign hexad with that finding_id as subject.

New surface:

- HexadSemantic gains `campaign: Option<CampaignSemantic>` (additive,
  skip_serializing_if = none).
- CampaignSemantic { finding_id, state, pr_url?, reason?, last_polled? }
  — state is a free-form String so future labels can be added without
  a schema bump.
- storage: build_campaign_hexad / write_campaign_hexad /
  load_{finding,campaign,aggregate}_hexads helpers.
- src/campaign/ module — register_pr, dismiss, current_state,
  status_markdown.
- panic-attack campaign register-pr|dismiss|status — CLI surface.

`status` renders a Markdown tracker matching the shape of the issue #32
manual checklist: summary line, table with finding-id, repo, rule_id,
location, state, PR link (or dismissal reason), last-event timestamp,
checkbox column.

Out of scope (S2b): poll subcommand that queries GitHub for PR-state
transitions. The data path is in place — the polling logic lands once
the rate-limit / pagination shape is settled.

Tests: 5 new in src/campaign/ (register, dismiss-overrides-open,
empty-arg rejection, empty-store status, two-row render). Full lib
suite: 220 green. Clippy clean with -D warnings. End-to-end CLI smoke
test green: register-pr + dismiss + status round-trip prints the
expected markdown.

Refs #33. Stacked on #55 (S1) — diff against main includes the S1
changes until S1 lands; this PR will rebase clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `panic-attack sweep-tracker` subcommand that renders an issue-#32
shaped sweep tracker by joining per-finding hexads (issue #33 S1) with
campaign-state hexads (issue #33 S2). Distinct from `campaign status`:
that is a flat per-finding table; this is a hierarchical checklist
grouped by repo and/or category, with an estate-wide summary header.

Flags:
- `--verisimdb-dir DIR`  : hexad store root (default `verisimdb-data`)
- `--output FILE`        : write Markdown to file instead of stdout
- `--by-repo`            : emit only the "By repo" section
- `--by-category`        : emit only the "By category" section
- no flag                : emit both sections (default)

Output is deterministic — repos alphabetically, findings within each
repo sorted by (rule_id, file, line). A finding with no campaign
hexad shows state `open`; with one, shows the latest state plus PR
URL (rendered as `#<num>` link) or dismissal reason.

Tests cover empty store, by-repo grouping, by-category grouping,
campaign-state join (open / pr-merged / dismissed), deterministic
ordering, both-shape ordering, and PR-number label extraction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 26, 2026 12:47
Comment thread src/sweep_tracker/mod.rs
@@ -0,0 +1,678 @@
// SPDX-License-Identifier: MPL-2.0
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 46 issues detected

Severity Count
🔴 Critical 5
🟠 High 16
🟡 Medium 25

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Nickel file missing SPDX-License-Identifier header (1 occurrences, CWE-1104)",
    "type": "ncl_missing_spdx",
    "file": "/home/runner/work/panic-attack/panic-attack/reports/panic-attack-20260211180017.ncl",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/sweep_tracker/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "expect() in hot path (2 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/chain.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/evidence.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/ambush/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/kanren/strategy.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/axial/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "expect() in hot path (4 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/assail/analyzer.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (4 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/panic-attack/panic-attack/benches/scan_bench.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit 09b80f4 into main May 27, 2026
25 of 26 checks passed
@hyperpolymath hyperpolymath deleted the feat/sweep-tracker-report branch May 27, 2026 01:44
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.

2 participants