Commit 732b409
## Summary
Third slice of
[#33](#33). Adds a
\`panic-attack query\` subcommand that evaluates a small S-expression
query language over the per-finding hexads from #55 (S1) joined with the
campaign-state hexads from #56 (S2).
> Stacked on #56 — diff against \`main\` includes the S1+S2 changes
until those land; this PR rebases cleanly.
## Supported forms (S3 initial)
\`\`\`scheme
(category UnsafeCode)
(rule-id PA004)
(severity Critical)
(repo <name-substring>) ; case-insensitive substring
(file <path-substring>) ; case-insensitive substring
(pr-state pr-filed|pr-merged|pr-closed|dismissed|nil)
(and <expr> <expr> ...)
(or <expr> <expr> ...)
(not <expr>)
\`\`\`
\`(pr-state nil)\` matches any finding **without a campaign hexad** —
i.e. the operationally important "open work not yet PR'd" view that the
estate-sweep campaign needs most.
## CLI
\`\`\`
panic-attack query "(and (category UnsafeCode) (pr-state nil))"
panic-attack query "(severity Critical)" --format json
panic-attack query "(repo alpha)" --verisimdb-dir verisimdb-data
\`\`\`
Default output: fixed-width table. JSON via \`--format json\`.
## Deferred to S3 follow-ups
Three follow-ups will land in the next PRs in this stack:
- \`(crosslang :from FFI :to ProofDrift)\` — needs integration with
\`src/kanren/crosslang.rs\`.
- \`(diff :since <date> :category <X>)\` — needs an explicit
baseline-run cursor.
- \`panic-attack campaign poll\` (was S2 scope cut) — GitHub PR-state
polling.
## Implementation notes
- Small hand-rolled S-expression tokenizer/parser (~170 LOC) — doesn't
depend on the a2ml parser since the query surface is narrower.
- Evaluator pre-joins findings with their latest campaign event
(newest-by-\`created_at\` wins per \`finding_id\`) before filtering.
\`(pr-state ...)\` is a free clause inside \`and\`/\`or\` rather than a
special case.
## Test plan
- [x] \`cargo test --lib\` — 239 green (19 new in \`src/query/\`).
- [x] \`cargo clippy --all-targets -- -D warnings\` — clean.
- [x] \`cargo fmt --all\` — clean.
- [x] End-to-end CLI smoke: hand-crafted finding hexad + \`campaign
register-pr\` + \`query (and (category UnsafeCode) (pr-state pr-filed))
--format json\` returns the expected JSON with the joined campaign
state.
Refs #33. Stacked on #56 (S2) → #55 (S1).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 016791c commit 732b409
3 files changed
Lines changed: 759 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
778 | 779 | | |
779 | 780 | | |
780 | 781 | | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
781 | 804 | | |
782 | 805 | | |
783 | 806 | | |
| |||
2429 | 2452 | | |
2430 | 2453 | | |
2431 | 2454 | | |
| 2455 | + | |
| 2456 | + | |
| 2457 | + | |
| 2458 | + | |
| 2459 | + | |
| 2460 | + | |
| 2461 | + | |
| 2462 | + | |
| 2463 | + | |
| 2464 | + | |
| 2465 | + | |
| 2466 | + | |
| 2467 | + | |
| 2468 | + | |
| 2469 | + | |
| 2470 | + | |
| 2471 | + | |
| 2472 | + | |
2432 | 2473 | | |
2433 | 2474 | | |
2434 | 2475 | | |
| |||
0 commit comments