hex-audit 0.5.4 → 0.5.6

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.
Files changed (108) hide show
  1. package/DEMO.md +5 -5
  2. package/README.md +31 -10
  3. package/dashboard/.next/BUILD_ID +1 -1
  4. package/dashboard/.next/app-build-manifest.json +22 -22
  5. package/dashboard/.next/app-path-routes-manifest.json +1 -1
  6. package/dashboard/.next/build-manifest.json +4 -4
  7. package/dashboard/.next/next-server.js.nft.json +1 -1
  8. package/dashboard/.next/prerender-manifest.js +1 -1
  9. package/dashboard/.next/prerender-manifest.json +1 -1
  10. package/dashboard/.next/required-server-files.json +1 -1
  11. package/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/dashboard/.next/server/app/_not-found.html +1 -1
  13. package/dashboard/.next/server/app/_not-found.rsc +1 -1
  14. package/dashboard/.next/server/app/access/page_client-reference-manifest.js +1 -1
  15. package/dashboard/.next/server/app/api/notes/done/route.js +1 -0
  16. package/dashboard/.next/server/app/api/notes/done/route.js.nft.json +1 -0
  17. package/dashboard/.next/server/app/api/notes/lead/route.js +1 -0
  18. package/dashboard/.next/server/app/api/notes/lead/route.js.nft.json +1 -0
  19. package/dashboard/.next/server/app/api/notes/question/route.js +1 -0
  20. package/dashboard/.next/server/app/api/notes/question/route.js.nft.json +1 -0
  21. package/dashboard/.next/server/app/api/notes/record/route.js +3 -3
  22. package/dashboard/.next/server/app/api/notes/route.js +2 -2
  23. package/dashboard/.next/server/app/calls/page_client-reference-manifest.js +1 -1
  24. package/dashboard/.next/server/app/conformance/page_client-reference-manifest.js +1 -1
  25. package/dashboard/.next/server/app/diagram/page_client-reference-manifest.js +1 -1
  26. package/dashboard/.next/server/app/diagram.html +1 -1
  27. package/dashboard/.next/server/app/diagram.rsc +1 -1
  28. package/dashboard/.next/server/app/flows/page_client-reference-manifest.js +1 -1
  29. package/dashboard/.next/server/app/flows.html +1 -1
  30. package/dashboard/.next/server/app/flows.rsc +1 -1
  31. package/dashboard/.next/server/app/functions/page_client-reference-manifest.js +1 -1
  32. package/dashboard/.next/server/app/issues/page.js +5 -5
  33. package/dashboard/.next/server/app/issues/page_client-reference-manifest.js +1 -1
  34. package/dashboard/.next/server/app/notes/page.js +3 -3
  35. package/dashboard/.next/server/app/notes/page_client-reference-manifest.js +1 -1
  36. package/dashboard/.next/server/app/overleaf/page_client-reference-manifest.js +1 -1
  37. package/dashboard/.next/server/app/page.js +2 -2
  38. package/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  39. package/dashboard/.next/server/app/progress/page.js +1 -1
  40. package/dashboard/.next/server/app/progress/page_client-reference-manifest.js +1 -1
  41. package/dashboard/.next/server/app/stats/page_client-reference-manifest.js +1 -1
  42. package/dashboard/.next/server/app-paths-manifest.json +11 -8
  43. package/dashboard/.next/server/chunks/42.js +1 -1
  44. package/dashboard/.next/server/chunks/530.js +1 -1
  45. package/dashboard/.next/server/chunks/622.js +3 -0
  46. package/dashboard/.next/server/functions-config-manifest.json +1 -1
  47. package/dashboard/.next/server/middleware-build-manifest.js +1 -1
  48. package/dashboard/.next/server/pages/404.html +1 -1
  49. package/dashboard/.next/server/pages/500.html +1 -1
  50. package/dashboard/.next/server/server-reference-manifest.js +1 -1
  51. package/dashboard/.next/server/server-reference-manifest.json +1 -1
  52. package/dashboard/.next/static/chunks/app/issues/page-d1607f57783782ae.js +1 -0
  53. package/dashboard/.next/static/chunks/app/notes/page-17af94c67ba79c00.js +1 -0
  54. package/dashboard/.next/static/chunks/app/page-24bac51dfe007c66.js +1 -0
  55. package/dashboard/.next/static/chunks/app/progress/page-427fb18da3afb061.js +1 -0
  56. package/dashboard/.next/static/chunks/{main-a6cc6e599cefa893.js → main-9a811c95265dbd11.js} +1 -1
  57. package/dashboard/.next/static/css/4a38d8f6f6b7a68f.css +3 -0
  58. package/dashboard/app/api/notes/done/route.ts +26 -0
  59. package/dashboard/app/api/notes/lead/route.ts +40 -0
  60. package/dashboard/app/api/notes/question/route.ts +30 -0
  61. package/dashboard/app/api/notes/record/route.ts +10 -2
  62. package/dashboard/app/issues/IssuesBoardClient.tsx +18 -2
  63. package/dashboard/app/notes/NotesClient.tsx +494 -77
  64. package/dashboard/app/notes/page.tsx +5 -2
  65. package/dashboard/app/progress/ProgressClient.tsx +29 -8
  66. package/dashboard/app/progress/page.tsx +2 -0
  67. package/dashboard/components/MarkdownRenderer.tsx +31 -0
  68. package/dashboard/next.config.mjs +11 -0
  69. package/dashboard/whisper-server.py +85 -0
  70. package/dist/commands/dashboard.d.ts.map +1 -1
  71. package/dist/commands/dashboard.js +23 -1
  72. package/dist/commands/dashboard.js.map +1 -1
  73. package/dist/commands/init.js +2 -2
  74. package/dist/commands/issue.js +2 -2
  75. package/dist/commands/issue.js.map +1 -1
  76. package/dist/commands/note.d.ts.map +1 -1
  77. package/dist/commands/note.js +55 -2
  78. package/dist/commands/note.js.map +1 -1
  79. package/dist/core/issues.d.ts +1 -1
  80. package/dist/core/issues.d.ts.map +1 -1
  81. package/dist/core/issues.js +2 -1
  82. package/dist/core/issues.js.map +1 -1
  83. package/dist/core/notes.d.ts +68 -0
  84. package/dist/core/notes.d.ts.map +1 -1
  85. package/dist/core/notes.js +167 -0
  86. package/dist/core/notes.js.map +1 -1
  87. package/dist/core/review.d.ts +14 -0
  88. package/dist/core/review.d.ts.map +1 -0
  89. package/dist/core/review.js +80 -0
  90. package/dist/core/review.js.map +1 -0
  91. package/dist/core/whisper.d.ts +8 -0
  92. package/dist/core/whisper.d.ts.map +1 -1
  93. package/dist/core/whisper.js +55 -0
  94. package/dist/core/whisper.js.map +1 -1
  95. package/dist/skills/diane.md +49 -67
  96. package/dist/skills/ingest-aa-report.md +6 -6
  97. package/dist/skills/init-audit.md +1 -1
  98. package/dist/skills/validate-all-findings.md +23 -9
  99. package/dist/skills/{validate-issue.md → validate-finding.md} +71 -25
  100. package/dist/skills/write-finding.md +3 -3
  101. package/package.json +3 -2
  102. package/dashboard/.next/static/chunks/app/issues/page-abb062e4cf118340.js +0 -1
  103. package/dashboard/.next/static/chunks/app/notes/page-bc458d3e1639c526.js +0 -1
  104. package/dashboard/.next/static/chunks/app/page-1226422e170d8895.js +0 -1
  105. package/dashboard/.next/static/chunks/app/progress/page-f07ac39d1d27b480.js +0 -1
  106. package/dashboard/.next/static/css/23b3f575e87bea75.css +0 -3
  107. /package/dashboard/.next/static/{JIjye0HUTWALF3gbtaZF8 → hV9GBuhq9vm6hXiHOWEfE}/_buildManifest.js +0 -0
  108. /package/dashboard/.next/static/{JIjye0HUTWALF3gbtaZF8 → hV9GBuhq9vm6hXiHOWEfE}/_ssgManifest.js +0 -0
@@ -45,87 +45,69 @@ When a unit is ambiguous, treat it as a note (the safe default — nothing is lo
45
45
 
46
46
  This is what makes you a pair auditor, not a stenographer. For **every checkable claim** in the narration — how a function behaves, a value, an invariant, "this is safe because…" — open the relevant source (Read / Grep over the project scope) and check it. Classify each:
47
47
 
48
- - **Confirmed** → record it as fact in the profile; a brief ✓ is enough, don't clutter.
49
- - **Wrong** → record the **correct** behavior in Description, flagged with a `⚠` callout naming the contradiction + `file:line` (e.g. `⚠ you said rounds down; rounds UP (Vault.sol:88)`).
50
- - **Incomplete / missed** → add to **Leads** (e.g. an unchecked return, a missing bound or zero-check, an edge case the auditor walked past), noting they didn't mention it.
51
- - **Interesting angle** you spot independently → **Leads**, phrased as a hypothesis to investigate.
48
+ - **Confirmed** → record it as fact in `description` (the function's `notes`, or purpose/storage/roles); don't clutter.
49
+ - **Wrong** → record the **correct** behavior, and add a `⚠` string to that function's `warnings` naming the contradiction + `file:line` (e.g. `⚠ you said rounds down; rounds UP (Vault.sol:88)`).
50
+ - **Incomplete / missed** → add a `leads` entry (e.g. an unchecked return, a missing bound or zero-check, an edge case the auditor walked past), noting they didn't mention it.
51
+ - **Interesting angle** you spot independently → a `leads` entry, phrased as a hypothesis to investigate.
52
52
 
53
53
  Push back only with concrete, code-grounded evidence — never contrarian for its own sake. If a claim isn't checkable from the code, note it as an assumption rather than asserting it true or false.
54
54
 
55
55
  ## Step 4 — Route the triggers
56
56
 
57
- All three edit the same living profile; Step 5 covers the merge mechanics (read the doc → edit → write it back).
57
+ All three edit the same structured record; Step 5 covers the read-merge-write mechanics.
58
58
 
59
- **create a finding** → invoke the existing `/write-finding` skill, prefilled from the narration (the affected contract/function and the auditor's stated impact). Let that skill own duplicate-checking and the board write. After it returns, find the matching bullet in **Leads** and mark it resolved — strike it through and append the id, rather than deleting it (audit trail): `- ~~<lead>~~ → logged <H-NNN>`. If no lead existed for it, no-op.
59
+ **create a finding** → invoke the existing `/write-finding` skill, prefilled from the narration (the affected contract/function and the auditor's stated impact). Let that skill own duplicate-checking and the board write. After it returns, set the matching lead's `status` to `"logged"` and its `ref` to the new `H-NNN` — it leaves the open-leads list but stays in the record (audit trail). If no lead matched, no-op.
60
60
 
61
- **answer a question** → answer it yourself using codebase access (Read / Grep over the project scope), or search online for things you're unsure about. Be concrete, concise, and cite `file:line`. Record it in the **Questions** section as a single inline bullet:
61
+ **answer a question** → answer it yourself using codebase access (Read / Grep over the project scope), or search online for things you're unsure about. Be concrete, concise, and cite `file:line`. Put it in `questions[]`: fill the `answer` of the matching open question if one exists, else add `{ q, answer }`.
62
62
 
63
- ```
64
- - <question>? **Answer**: <concise answer, with file:line>
65
- ```
66
-
67
- If it answers a question already open in that list, fill that bullet in place — don't add a duplicate. Never write a separate `**Q:**` / `**A:**` block.
68
-
69
- **search online** → use WebSearch, read the most relevant result, and fold it into wherever it belongs — an answer in **Questions**, a point in **Description**, or a **Leads** bullet — with the source link. No standalone block.
63
+ **search online** → use WebSearch, read the most relevant result, and fold it where it belongs — a question's `answer`, a `description` field, or a `leads` entry — with the source link.
70
64
 
71
65
  ## Step 5 — Update the contract profile
72
66
 
73
- Notes for a contract live in **one growing profile**, not per-session blocks. Each run: read the current profile, merge the session's thought-units **and your Step 3 verification** into the right place, write the whole doc back. **Clean up** filler, repetition, and false starts — keep the reasoning, not the disfluency.
74
-
75
- The profile has three sections (create whatever's missing; add a subsection or entry only when there's something to say):
76
-
77
- ````markdown
78
- # <Contract>.sol
79
-
80
- ## Description
81
-
82
- ### Purpose
83
- - <what it's for>
84
-
85
- ### Inheritance
86
- - <base contracts / interfaces>
87
-
88
- ### Storage
89
- - `name` (type) — role / notes
90
-
91
- ### Roles & Modifiers
92
- - `onlyOwner` — gates X, Y
93
-
94
- ### Functions
95
- #### deposit(assets, receiver) — external
96
- - Purpose: pull assets, mint shares
97
- - Access: anyone
98
- - Effects: transferFrom → _mint → totalAssets +=
99
- - Notes: pulls before state update
100
- - Notes: ⚠ you said rounds down; rounds UP (Vault.sol:88)
101
-
102
- ## Questions
103
- - <question>? **Answer**: <concise answer> (file:line)
104
- - <question>? (open — no answer requested yet)
105
-
106
- ## Leads
107
- - <lead> — why it might be an issue
108
- - setFee has no upper bound (Vault.sol:120) — you didn't mention it
109
- - ~~<promoted lead>~~ → logged H-007
110
- ````
67
+ A contract's notes are a **structured record** you grow over time — not per-session blocks. Each run: read the current record, merge this session **and your Step 3 verification** into it, write it back. Clean up filler/repetition; keep the reasoning, not the disfluency.
68
+
69
+ The record (JSON):
70
+
71
+ ```json
72
+ {
73
+ "contract": "Vault.sol",
74
+ "marked_done": false,
75
+ "description": {
76
+ "purpose": ["ERC-4626-ish vault"],
77
+ "inheritance": ["..."],
78
+ "storage": ["`totalAssets` (uint256) — managed balance"],
79
+ "roles": ["`onlyOwner` — gates setFee / setOwner"],
80
+ "functions": [
81
+ {
82
+ "sig": "deposit(assets, receiver) — external",
83
+ "purpose": "pull assets, mint shares",
84
+ "access": "anyone",
85
+ "effects": "transferFrom → _mint → totalAssets +=",
86
+ "notes": ["pulls before state update"],
87
+ "warnings": ["⚠ you said rounds down; rounds UP (Vault.sol:88)"]
88
+ }
89
+ ]
90
+ },
91
+ "questions": [
92
+ { "id": "Q1", "q": "is uint8 enough for decimals?", "answer": "yes, 0–255 (MockToken.sol:9)" }
93
+ ],
94
+ "leads": [
95
+ { "id": "L1", "text": "setFee has no upper bound (Vault.sol:120) — you didn't mention it", "status": "open" }
96
+ ]
97
+ }
98
+ ```
111
99
 
112
- - **Description** = how the contract works (verified truth), built top-to-bottom. Each function the auditor walks through gets a `#### name(args) — visibility` entry using the **Purpose / Access / Effects / Notes** template, inserted in **source order**. When your Step 3 check contradicts the auditor, the relevant `Notes:` line carries the `⚠ …(file:line)` callout.
113
- - **Questions** = the running Q&A list (Step 4).
114
- - **Leads** = things worth investigating that could become findings — including the misses and interesting angles you surfaced in Step 3 (Step 4 strikes them through when promoted).
100
+ - **description** = how the contract works (verified truth), top-to-bottom: `purpose`, `inheritance`, `storage`, `roles`, then `functions` in **source order** (each with Purpose / Access / Effects + `notes`). Step-3 corrections go in that function's `warnings`, with `file:line`.
101
+ - **questions** = your Q&A; an entry with no `answer` is still open.
102
+ - **leads** = things worth investigating (the Step-3 misses/angles). Only `status:"open"` leads show in the dashboard; `logged` (→ finding) and `dismissed` are kept but hidden.
115
103
 
116
- Merge mechanics:
104
+ Merge mechanics — read the record, edit JSON, write it back:
117
105
 
118
- 1. Read the current profile (empty on first run):
119
- ```bash
120
- npx hex note read "<contract>" > /tmp/diane_<ts>.md
121
- ```
122
- 2. Edit `/tmp/diane_<ts>.md` **in place**: scaffold the skeleton if it was empty; otherwise refine existing entries and add new ones where they belong (fill a Storage row, flesh out a function's Effects, answer an open question, add a lead). Do **not** append a dated block, and do **not** duplicate something already captured — sharpen it instead.
123
- 3. Write the whole doc back:
124
- ```bash
125
- npx hex note write "<contract>" --body-file /tmp/diane_<ts>.md
126
- ```
106
+ 1. `npx hex note show "<contract>" > /tmp/diane_<ts>.json` — prints an empty skeleton on first run.
107
+ 2. Edit `/tmp/diane_<ts>.json`: add or refine entries where they belong (fill a storage row, flesh out a function, set a question's `answer`, add a lead, attach a `warning`). **Preserve existing `id`s, don't duplicate** — sharpen what's there. Keep arrays concise.
108
+ 3. `npx hex note set "<contract>" --json-file /tmp/diane_<ts>.json` (this also recomputes the `/progress` review state).
127
109
 
128
- `<contract>` is the session's `contract` value verbatim (e.g. `Vault.sol`). Cross-cutting remarks go to `general`, which uses the same three sections but a free-form Description (no per-function list).
110
+ `<contract>` is the session's `contract` value verbatim (e.g. `Vault.sol`). Cross-cutting remarks still go to the free-form `general` markdown note: `npx hex note append general --body-file …` (`general` isn't structured). If the auditor says they're done with a contract, run `npx hex note done "<contract>"`.
129
111
 
130
112
  ## Step 6 — Close the loop & deliver the verdict
131
113
 
@@ -143,4 +125,4 @@ Then deliver a candid **pair-audit verdict** — lead with the substance (correc
143
125
 
144
126
  - **Never hand-edit** `notes.json` or the `.md` files directly — always go through `npx hex note …`, exactly as the other skills route through `npx hex issue …`.
145
127
  - One session per invocation. If the auditor wants the next one, they run `/diane` again (or process them in a loop if they ask).
146
- - Do not create PoCs from narration — that stays an explicit, auditor-driven `/validate-issue` step.
128
+ - Do not create PoCs from narration — that stays an explicit, auditor-driven `/validate-finding` step.
@@ -43,15 +43,15 @@ Never invoke `aa scan` to start a new scan. If the auditor has no scan ID, instr
43
43
 
44
44
  ## Step 3 — Fetch findings
45
45
 
46
- Run:
46
+ Export the full findings for the linked scan:
47
47
 
48
48
  ```bash
49
- aa scan --report <scan_id>
49
+ aa findings --all
50
50
  ```
51
51
 
52
- (Or the equivalent results-fetch command exposed by the installed `aa` version — check `aa scan --help` if `--report` is not supported.)
52
+ `aa findings --all` writes the full findings (with details) to `auditagent/findings-<scan_id>.md`. If the scan isn't linked yet, run `aa link <scan_id>` first, then re-run `aa findings --all`. (`aa findings` without `--all` only prints summaries; **do not** use `aa scan --report` — that subcommand does not exist.)
53
53
 
54
- Capture the raw output to `<output_dir>/ai-results/auditagent/raw-output.md`.
54
+ Copy the exported `auditagent/findings-<scan_id>.md` to `<output_dir>/ai-results/auditagent/raw-output.md` and parse it from there.
55
55
 
56
56
  ## Step 4 — Normalize into findings.json
57
57
 
@@ -142,7 +142,7 @@ Then record the match in `<output_dir>/comparison.json` under `duplicates`. The
142
142
 
143
143
  If `comparison.json` doesn't exist, create it with `{ "duplicates": [], "novel": [], "rejected": [] }`.
144
144
 
145
- For entries that don't match anything, leave them as `status: "unverified"` (the auditor will run `/validate-issue AA-<NNN>` on them).
145
+ For entries that don't match anything, leave them as `status: "unverified"` (the auditor will run `/validate-finding AA-<NNN>` on them).
146
146
 
147
147
  ## Step 7 — Report
148
148
 
@@ -154,7 +154,7 @@ Ingested N AuditAgent findings (scan <scan_id>).
154
154
  - K flagged as Duplicate of existing entries
155
155
  - 0 errors
156
156
 
157
- Run /validate-issue <id> on any new finding to confirm and promote it to Verified.
157
+ Run /validate-finding <id> on any new finding to confirm and promote it to Verified.
158
158
  ```
159
159
 
160
160
  ## Outputs (recap)
@@ -247,7 +247,7 @@ npx hex issue new --source conformance --source-ref "<spec-item-id, e.g. SC-001>
247
247
  --severity "<spec-item.severity_hint, default Info>"
248
248
  ```
249
249
 
250
- `--source-ref` is the spec item's own id (e.g. `SC-001`) — Hex keeps that as the back-reference; the board card itself gets the uniform `H-NNN`. These appear on the Issues board as Potential cards immediately; the auditor validates them later with `/validate-issue <id>` (or `/validate-all-findings`).
250
+ `--source-ref` is the spec item's own id (e.g. `SC-001`) — Hex keeps that as the back-reference; the board card itself gets the uniform `H-NNN`. These appear on the Issues board as Potential cards immediately; the auditor validates them later with `/validate-finding <id>` (or `/validate-all-findings`).
251
251
 
252
252
  **Idempotency:** before creating, check whether a tracking entry already has this `source_ref` (re-running `/init-audit` shouldn't double-create). Skip items whose `source_ref` is already present.
253
253
 
@@ -6,7 +6,9 @@ description: "Walk every Potential issue on the board and validate them one by o
6
6
 
7
7
  **Recommended model:** Opus
8
8
 
9
- Runs `/validate-issue` over every issue currently in the **Potential** column, one at a time, so the auditor can clear the backlog in a single pass without naming each id.
9
+ Runs `/validate-finding` over every issue currently in the **Potential** column, one at a time, so the auditor can clear the backlog in a single pass without naming each id.
10
+
11
+ Each issue gets `/validate-finding`'s full two-axis verdict — correctness **and** the Step-2.5 security-relevance gate. **`auditagent` / `github` findings skew recall-over-precision, so the realism gate is mandatory for them**; `manual` / `conformance` items already passed an auditor's eye and can lean lighter.
10
12
 
11
13
  ## Step 1 — Build the work list
12
14
 
@@ -14,6 +16,16 @@ Read `<output_dir>/tracking.json`. The Potential column is every entry whose `st
14
16
 
15
17
  If `N` is 0, report "No Potential issues to validate." and stop.
16
18
 
19
+ ## Step 1.5 — Cheap realism pre-pass
20
+
21
+ Before deep-diving, do a fast first pass over all `N` and emit a one-line realism read per issue — the Step-2.5 reality-gate take only, no full code trace yet:
22
+
23
+ ```
24
+ <id> [<sev>] <title> — likely: security | by-design | self-inflicted | incorrect | unsure
25
+ ```
26
+
27
+ Print it as a table. This front-loads "which of these even deserve a deep trace" so effort concentrates on the genuine security candidates instead of spreading evenly. It's a triage hint, not a verdict — every issue still gets its real verdict in Step 2.
28
+
17
29
  ## Step 2 — Loop
18
30
 
19
31
  For each issue `i` of `N` (1-based), print a header:
@@ -22,15 +34,16 @@ For each issue `i` of `N` (1-based), print a header:
22
34
  ── Validating i/N: <id> [<severity>] <title> ──
23
35
  ```
24
36
 
25
- Then run the full **`/validate-issue`** flow for that `<id>` exactly as that skill defines it:
37
+ Then run the full **`/validate-finding`** flow for that `<id>` exactly as that skill defines it:
26
38
  1. Assemble context (the tracking entry + its source record + the affected source).
27
39
  2. Trace the claim in the code.
28
- 3. Reach a verdict:
29
- - **Valid** → ask PoC-vs-memo (AskUserQuestion), write the validation memo, optionally generate the PoC, then `hex issue patch <id> ...` + `hex issue move <id> --to verified`; offer the severity-adjustment prompt.
30
- - **Invalid** → `hex issue move <id> --to invalid`, memo records why.
40
+ 3. Reach a verdict (correctness + the Step-2.5 security-relevance gate):
41
+ - **Verified** (true + security-relevant) → re-rate severity, ask PoC-vs-memo, write the memo, `hex issue patch <id> ...` + `hex issue move <id> --to verified`.
42
+ - **Rejected — by-design** (true, not security-relevant) → memo with `Disposition: by-design`, `hex issue patch <id> --resolution Acknowledged` + `hex issue move <id> --to rejected`.
43
+ - **Rejected — incorrect** (false) → `hex issue move <id> --to rejected` (no resolution), memo records why.
31
44
  - **Uncertain** → leave in place, `hex issue patch <id> --notes "<open questions>"`.
32
45
 
33
- Do not batch or skip the interactive prompts — this skill is the same per-issue UX as `/validate-issue`, just iterated. The auditor stays in control of each verdict.
46
+ Batch by tier — don't rubber-stamp. **Security-relevant candidates get the full per-issue prompt flow** (PoC-vs-memo and severity), one at a time — the auditor stays in control of every real finding. **Info / by-design / self-inflicted items may be batched**: collect them and confirm their dispositions (and any PoC/severity calls) in a single consolidated **AskUserQuestion** instead of N separate modals. That keeps a large sweep controllable without diluting scrutiny on the findings that matter.
34
47
 
35
48
  ## Step 3 — Resume safety
36
49
 
@@ -42,9 +55,10 @@ After the loop, print a tally:
42
55
 
43
56
  ```
44
57
  Validated N Potential issues:
45
- → Verified: V
46
- → Invalid: X
47
- → Left for review (uncertain): U
58
+ → Verified: V
59
+ → Rejected (incorrect): X
60
+ → Rejected (valid, not security): B
61
+ → Left for review (uncertain): U
48
62
  ```
49
63
 
50
64
  Remind the auditor of the next steps: `/sync-issues` to push the Verified findings to GitHub, then `/generate-overleaf` for the report.
@@ -1,8 +1,8 @@
1
1
  ---
2
- description: "Validate any potential issue on the board (manual / auditagent / conformance / github) and promote it to Verified or Invalid"
2
+ description: "Validate any potential finding on the board (manual / auditagent / conformance / github): check both correctness and security relevance, then route to Verified, Rejected, or leave Uncertain"
3
3
  ---
4
4
 
5
- # Skill: Validate Issue
5
+ # Skill: Validate Finding
6
6
 
7
7
  **Recommended model:** Opus
8
8
 
@@ -15,17 +15,17 @@ Takes a single potential issue from the dashboard's Issues board and runs an ind
15
15
  - **Conformance** (materialized by `/init-audit`) — `source: "conformance"`, `status: "pending_validation"`.
16
16
  - **GitHub** (teammate findings via `/sync-issues`) — `source: "github"`, `status: "pending_validation"`.
17
17
 
18
- The skill writes a validation memo (always), optionally generates a PoC, and updates the tracking entry to `verified` or `rejected`. Card moves on the board.
18
+ The skill judges **two** things, not one: first **correctness** (is the claim true?), then for true claims **security relevance** (is it actually a vuln worth reporting, or true-but-by-design / a missing feature / self-inflicted?). It writes a validation memo (always), optionally generates a PoC, and moves the card. Outcomes: **Verified** (true + security-relevant), **Rejected** (either factually incorrect, *or* true-but-not-a-security-issue), or **Uncertain**. Both Rejected kinds land in the board's **Rejected** column, distinguished by disposition.
19
19
 
20
20
  ## Invocation
21
21
 
22
22
  The auditor invokes by id or by free-text reference:
23
23
 
24
24
  ```
25
- /validate-issue F003
26
- /validate-issue SC-007
27
- /validate-issue AA-N002
28
- /validate-issue for the rounding issue in Vault.sol
25
+ /validate-finding F003
26
+ /validate-finding SC-007
27
+ /validate-finding AA-N002
28
+ /validate-finding for the rounding issue in Vault.sol
29
29
  ```
30
30
 
31
31
  Resolve to a single tracking entry by:
@@ -65,12 +65,35 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
65
65
  - Check existing protections (modifiers, reentrancy guards, input validation, economic constraints).
66
66
  - For conformance items: re-read the cited spec section, compare to the implementation.
67
67
 
68
+ ### 2.5 Reality gate — is it security-relevant?
69
+
70
+ Correctness is not enough. AI scanners (and sometimes teammates) surface claims that are **factually true but not security issues** — by-design behavior, missing features, defense-in-depth nice-to-haves, or risks only the deployer can inflict on themselves. This is the precision pass. **Run it for every finding; treat it as mandatory for `source: auditagent | github`** (those skew recall-over-precision). `manual` / `conformance` items already passed an auditor's eye, so they can lean lighter.
71
+
72
+ For a claim you found **true**, run these five checks — each one pushes toward *not-security-relevant*:
73
+
74
+ - **Trigger model.** Can only the deployer/owner harm their own deployment? Then it's self-inflicted, not an attack.
75
+ - **Out-of-scope dependency.** Does the impact require an out-of-scope component (e.g. the integrating contract) to be buggy or malicious — and if so, does the finding grant a capability that component didn't already have? No capability gain → defense-in-depth noise.
76
+ - **Trivial workaround.** Is there an integrator-controlled or already-documented mitigation (a constructor param, a config, standard usage)?
77
+ - **Class.** Defect, deliberate design decision, or missing feature? Only defects are findings.
78
+ - **Net test.** Would a senior auditor put this in a client report as a security issue, as an informational note, or not at all?
79
+
80
+ Carry the answers into the verdict and the memo (Triggerability / Depends-on-out-of-scope / Disposition).
81
+
68
82
  ### 3. Verdict
69
83
 
70
- **Valid:**
84
+ Combine the two axes — correctness (Step 2) and security relevance (Step 2.5):
85
+
86
+ | Correctness | Security-relevant? | → Outcome |
87
+ |---|---|---|
88
+ | True | Yes | **Verified** |
89
+ | True | No | **Rejected** (by-design / informational) |
90
+ | False | — | **Rejected** (incorrect) |
91
+ | Uncertain | — | **Uncertain** |
92
+
93
+ **Verified — true and security-relevant:**
71
94
 
72
95
  1. Use **AskUserQuestion** to ask:
73
- > The issue appears **VALID**. How would you like to proceed?
96
+ > The finding appears **VALID and security-relevant**. How would you like to proceed?
74
97
  >
75
98
  > 1. Generate a full PoC test, then promote to Verified.
76
99
  > 2. Accept with rational verification only — validation memo, no PoC.
@@ -81,7 +104,9 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
81
104
 
82
105
  3. **If memo-only chosen:** no PoC file.
83
106
 
84
- 4. **Persist via the CLI — do not hand-edit `findings.json` or `tracking.json`.** The `hex issue` command is the single source of truth for board mutations; it materializes a `findings.json` entry from the source record (conformance / auditagent / github) when one does not exist yet, and routes fields correctly. Write the validation memo and the recommendation to temp files, then:
107
+ 4. **Calibrate severity — don't inherit it.** The source's severity (especially AuditAgent's) is a *hypothesis*, not authoritative. Re-derive it from realistic impact × likelihood; many technically-true findings collapse to Info. A finding whose worst case is self-inflicted or chain-conditional must say so and re-rate accordingly.
108
+
109
+ 5. **Persist via the CLI — do not hand-edit `findings.json` or `tracking.json`.** The `hex issue` command is the single source of truth for board mutations; it materializes a `findings.json` entry from the source record (conformance / auditagent / github) when one does not exist yet, and routes fields correctly. Write the validation memo and the recommendation to temp files, then:
85
110
 
86
111
  ```bash
87
112
  # Land the editable fields (materializes a finding for conformance/auditagent/github ids):
@@ -99,21 +124,34 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
99
124
 
100
125
  Hex does not write `@audit-issue` comments into the client's source files — the board and finding record are the only place the issue lives.
101
126
 
102
- 5. Use **AskUserQuestion** to ask:
127
+ 6. Use **AskUserQuestion** to ask:
103
128
  > Issue verified as **\<id\>** with severity **\<current severity\>**.
104
129
  >
105
130
  > Would you like to change the severity? Reply with the new severity (Critical / High / Medium / Low / Info), or **no** to keep it.
106
131
 
107
- 6. If the user provides a new severity, run `npx hex issue patch <id> --severity <NewSeverity>`.
132
+ 7. If the user provides a new severity, run `npx hex issue patch <id> --severity <NewSeverity>`.
108
133
 
109
- **Invalid:**
134
+ **Rejected — true but not a security issue (by-design / informational):**
110
135
 
111
- 1. Explain why:
112
- - Is the attack path blocked? By what specifically (modifier, require, state check)?
113
- - Are the preconditions impossible? Why?
114
- - Is the impact assessment wrong? What actually happens?
115
- 2. Move it to the Invalid column: `npx hex issue move <id> --to invalid`.
116
- 3. Do NOT write a finding body. The memo alone records the reasoning.
136
+ The claim is correct, but Step 2.5 says it isn't a vuln worth reporting (self-inflicted, design decision, missing feature, defense-in-depth noise). This is **not** the same as "incorrect" — give it its own disposition so it's distinguishable from a false positive and AuditAgent gets honest feedback.
137
+
138
+ 1. Write the memo with `Disposition: by-design` (or `informational`) and the Step-2.5 reasoning.
139
+ 2. Mark the disposition and move it:
140
+ ```bash
141
+ npx hex issue patch <id> --resolution Acknowledged
142
+ npx hex issue move <id> --to rejected
143
+ ```
144
+ `Acknowledged` on a Rejected card means "AA was right, but it's by-design / not a security issue" — the board shows a **By-design** chip. (A Rejected card with *no* resolution = factually incorrect; see below.)
145
+ 3. Do NOT write a finding body or carry it into the report.
146
+
147
+ **Rejected — incorrect (false positive):**
148
+
149
+ The claim is factually wrong. Explain why:
150
+ - Is the attack path blocked? By what specifically (modifier, require, state check)?
151
+ - Are the preconditions impossible? Why?
152
+ - Is the impact assessment wrong? What actually happens?
153
+
154
+ Move it to the Rejected column with **no** resolution (the board shows an **Invalid** chip): `npx hex issue move <id> --to rejected`. Do NOT write a finding body — the memo records the reasoning.
117
155
 
118
156
  **Uncertain:**
119
157
 
@@ -126,11 +164,15 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
126
164
  Write `<output_dir>/validations/<id>_memo.md` (always — for valid, invalid, and uncertain verdicts):
127
165
 
128
166
  ```markdown
129
- # Issue Validation: <ID> — <Title>
167
+ # Finding Validation: <ID> — <Title>
130
168
 
131
169
  **Source:** <manual | auditagent | conformance | github>
132
170
  **Original record:** <pointer to the specific record being validated — e.g., findings.json#F003, ai-results/auditagent/findings.json#AA-014>
133
- **Verdict:** Valid / Invalid / Uncertain
171
+ **Correctness:** True / False / Uncertain
172
+ **Security relevance:** security-relevant / not-security-relevant / n/a
173
+ **Triggerability:** external | self-inflicted
174
+ **Depends on out-of-scope component:** no | yes (capability gain: yes/no)
175
+ **Disposition:** security-finding | by-design | informational | false
134
176
  **Date:** <YYYY-MM-DD>
135
177
 
136
178
  ## Claim being validated
@@ -142,13 +184,17 @@ Write `<output_dir>/validations/<id>_memo.md` (always — for valid, invalid, an
142
184
  ## Protections evaluated
143
185
  [Access control, reentrancy guards, input validation, economic constraints, etc.]
144
186
 
187
+ ## Reality gate
188
+ [The five Step-2.5 checks for true claims: trigger model, out-of-scope dependency, trivial workaround, class, net test. Skip with "n/a (claim is false)" for incorrect findings.]
189
+
145
190
  ## Verdict reasoning
146
- [Why valid / invalid / uncertain]
191
+ [Why Verified / Rejected (by-design or incorrect) / Uncertain — tie it to the two axes above]
147
192
 
148
193
  ## Action taken
149
- [If valid + PoC: PoC at <path>, test passing, finding F<NNN> updated/created]
150
- [If valid + memo-only: finding F<NNN> updated/created, no PoC]
151
- [If invalid: tracking entry rejected, no finding written]
194
+ [If Verified + PoC: PoC at <path>, test passing, finding updated/created; severity re-rated to <X>]
195
+ [If Verified + memo-only: finding updated/created, no PoC; severity <X>]
196
+ [If Rejected by-design: moved to Rejected with resolution Acknowledged, no finding written]
197
+ [If Rejected incorrect: moved to Rejected, no resolution, no finding written]
152
198
  [If uncertain: open questions listed; tracking status unchanged]
153
199
  ```
154
200
 
@@ -6,7 +6,7 @@ description: "Write a manual issue as a Potential card on the board (pending val
6
6
 
7
7
  **Recommended model:** Sonnet
8
8
 
9
- This skill records a manual issue that the auditor has spotted during review. The issue lands on the dashboard's Issues board as a **Potential** card with `status: "pending_validation"` and `source: "manual"`. The auditor promotes it to Verified later (via drag-drop on the board, or by running `/validate-issue <id>` which produces a validation memo and optional PoC).
9
+ This skill records a manual issue that the auditor has spotted during review. The issue lands on the dashboard's Issues board as a **Potential** card with `status: "pending_validation"` and `source: "manual"`. The auditor promotes it to Verified later (via drag-drop on the board, or by running `/validate-finding <id>` which produces a validation memo and optional PoC).
10
10
 
11
11
  ## Context Assembly
12
12
 
@@ -140,8 +140,8 @@ There is **no source-file annotation** — Hex does not write `@audit-issue` com
140
140
 
141
141
  The finding appears on the dashboard's `/issues` board as a Potential card (`H-NNN`). The auditor can:
142
142
 
143
- - Run `/validate-issue <id>` to write a validation memo and optionally generate a PoC; on success the card moves to Verified.
143
+ - Run `/validate-finding <id>` to write a validation memo and optionally generate a PoC; on success the card moves to Verified.
144
144
  - Drag the card from Potential → Verified on the board if no PoC is needed (e.g., trivial Info-level issues).
145
145
  - Run `/sync-issues` (once findings are Verified and `settings.github.repo` is set) to push them to GitHub.
146
146
 
147
- Do NOT run `/validate-issue` or `/sync-issues` automatically — those are auditor-driven.
147
+ Do NOT run `/validate-finding` or `/sync-issues` automatically — those are auditor-driven.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hex-audit",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "type": "module",
5
5
  "description": "CLI toolkit for Solidity smart contract auditors",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "build": "tsc && npm run copy-skills && npm run build:dashboard",
13
13
  "build:cli": "tsc && npm run copy-skills",
14
14
  "build:dashboard": "next build dashboard",
15
- "copy-skills": "node -e \"const fs=require('fs');const p=require('path');fs.cpSync(p.join('src','skills'),p.join('dist','skills'),{recursive:true})\"",
15
+ "copy-skills": "node -e \"const fs=require('fs');const p=require('path');const d=p.join('dist','skills');fs.rmSync(d,{recursive:true,force:true});fs.cpSync(p.join('src','skills'),d,{recursive:true})\"",
16
16
  "clean:next-cache": "node -e \"const fs=require('fs');for (const d of ['dashboard/.next/cache','dashboard/.next/trace','dashboard/.next/types']) fs.rmSync(d,{recursive:true,force:true})\"",
17
17
  "prepack": "npm run build && npm run clean:next-cache",
18
18
  "dev:cli": "tsc --watch",
@@ -31,6 +31,7 @@
31
31
  "dashboard/public",
32
32
  "dashboard/next.config.mjs",
33
33
  "dashboard/next-env.d.ts",
34
+ "dashboard/whisper-server.py",
34
35
  "dashboard/postcss.config.cjs",
35
36
  "dashboard/tailwind.config.ts",
36
37
  "dashboard/tsconfig.json",
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4521],{4260:function(e,t,n){Promise.resolve().then(n.bind(n,7063))},7063:function(e,t,n){"use strict";n.d(t,{IssuesBoardClient:function(){return m}});var r=n(7821),a=n(8078),o=n(3924);let s={Critical:"bg-[var(--critical)]/18 text-[var(--critical)]",High:"bg-[var(--high)]/18 text-[var(--high)]",Medium:"bg-[var(--medium)]/18 text-[var(--medium)]",Low:"bg-[var(--low)]/18 text-[var(--low)]",Info:"bg-[var(--info)]/18 text-[var(--info)]"};function i(e){var t;let{severity:n}=e,a=null!==(t=s[n])&&void 0!==t?t:s.Info;return(0,r.jsx)("span",{className:"inline-flex items-center rounded-md px-2 py-0.5 text-caption font-medium ".concat(a),children:n})}var c=n(9487);function l(e,t){var n;let r=new Set,a=e.code_locations.map(e=>e.file).filter(e=>e&&!r.has(e)&&r.add(e)).map(e=>(function(e,t){if(t.repositoryUrl&&t.commit){let n=t.repositoryUrl.replace(/\/+$/,"");return"[`".concat(e,"`](").concat(n,"/blob/").concat(t.commit,"/").concat(e,")")}return"[`".concat(e,"`]()")})(e,t)).join(", "),o=e.resolution||"Unresolved",s=null!==(n=e.update_from_client)&&void 0!==n?n:"";return["**File(s)**: ".concat(a),"","**Description**: ".concat(function(e){if(!e)return e;let t=!1,n="";return e.split("\n").map(e=>{let r=e.match(/^\s*(```+|~~~+)/);if(r)return t?r[1][0]===n&&(t=!1,n=""):(t=!0,n=r[1][0]),e;if(t)return e;let a=e.match(/^\s*#{1,6}\s+(.*\S)\s*$/);return a?"**".concat(a[1],"**"):/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(e)?"":e}).join("\n")}(e.description)),"","**Recommendation(s)**: ".concat(e.recommendation),"","**Status**: ".concat(o),"","**Update from the client**: ".concat(s)].join("\n")}let d=[{id:"potential",label:"Potential",description:"Awaiting validation"},{id:"verified",label:"Verified",description:"Confirmed, ready to push"},{id:"synced",label:"Synced to GitHub",description:"On GitHub — edit there, not here"},{id:"invalid",label:"Invalid",description:"Rejected after review"},{id:"duplicate",label:"Duplicate",description:"Covered by another entry"}],u={Critical:0,High:1,Medium:2,Low:3,Info:4},x=["Critical","High","Medium","Low","Info"],p=["Fixed","Mitigated","Acknowledged","Not Fixed","Unresolved"];function m(e){var t;let{issues:n,formatCtx:s={}}=e,[i,c]=(0,a.useState)(n),[l,x]=(0,a.useState)(null),[p,m]=(0,a.useState)("all");(0,a.useEffect)(()=>{c(n)},[n]);let h=(0,o.Dy)((0,o.VT)(o.we,{activationConstraint:{distance:4}})),y=(0,a.useMemo)(()=>"all"===p?i:i.filter(e=>e.source===p),[i,p]),v=(0,a.useMemo)(()=>{let e={potential:[],verified:[],synced:[],invalid:[],duplicate:[]};for(let t of y)e[t.column].push(t);for(let t of Object.values(e))t.sort((e,t)=>{var n,r;return(null!==(n=u[e.severity])&&void 0!==n?n:5)-(null!==(r=u[t.severity])&&void 0!==r?r:5)||e.id.localeCompare(t.id)});return e},[y]),j=(0,a.useMemo)(()=>{let e={potential:0,verified:0,synced:0,invalid:0,duplicate:0};for(let t of i)e[t.column]++;return e},[i]),N=(0,a.useCallback)(async(e,t)=>{c(n=>n.map(n=>n.id===e?{...n,column:t,status:function(e){switch(e){case"potential":return"pending_validation";case"verified":return"verified";case"synced":return"synced";case"invalid":return"rejected";case"duplicate":return"duplicate"}}(t),duplicate_of:"duplicate"===t?n.duplicate_of:null}:n));try{let n=await fetch("/api/issues/".concat(encodeURIComponent(e),"/move"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({column:t})});if(!n.ok){var r;let e=await n.json().catch(()=>({}));throw Error(null!==(r=e.error)&&void 0!==r?r:"move failed")}}catch(t){console.error(t),c(t=>t.map(t=>{var r;return t.id===e&&null!==(r=n.find(t=>t.id===e))&&void 0!==r?r:t}))}},[n]),w=(0,a.useCallback)(async(e,t)=>{c(n=>n.map(n=>n.id===e?{...n,...t}:n));try{let r=await fetch("/api/issues/".concat(encodeURIComponent(e)),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){var n;let e=await r.json().catch(()=>({}));throw Error(null!==(n=e.error)&&void 0!==n?n:"update failed")}}catch(e){console.error(e)}},[]),_=null!==(t=i.find(e=>e.id===l))&&void 0!==t?t:null;return(0,r.jsxs)("div",{children:[(0,r.jsxs)("header",{className:"mb-6 flex flex-wrap items-end justify-between gap-3",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{className:"text-title font-semibold text-text-primary",children:"Issues"}),(0,r.jsxs)("p",{className:"mt-1 text-body text-text-secondary",children:[i.length," total — ",j.potential," potential, ",j.verified," verified,"," ",j.synced," synced, ",j.invalid," invalid, ",j.duplicate," duplicate. Drag a card between columns; click to edit. Synced cards are read-only (edit on GitHub)."]})]}),(0,r.jsx)(b,{value:p,onChange:m})]}),(0,r.jsx)(o.LB,{sensors:h,onDragEnd:e=>{let{active:t,over:n}=e;if(!n)return;let r=n.id,a=t.id,o=i.find(e=>e.id===a);o&&o.column!==r&&"synced"!==r&&"synced"!==o.column&&N(a,r)},children:(0,r.jsx)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-5",children:d.map(e=>(0,r.jsx)(f,{column:e,issues:v[e.id],onOpen:x},e.id))})}),_&&(0,r.jsx)(g,{issue:_,formatCtx:s,onClose:()=>x(null),onSave:e=>w(_.id,e)})]})}function b(e){let{value:t,onChange:n}=e;return(0,r.jsx)("div",{className:"flex flex-wrap gap-1.5",children:[{id:"all",label:"All sources"},{id:"manual",label:"Manual"},{id:"auditagent",label:"AuditAgent"},{id:"conformance",label:"Conformance"},{id:"github",label:"GitHub"}].map(e=>{let a=t===e.id;return(0,r.jsx)("button",{type:"button",onClick:()=>n(e.id),className:"rounded-md px-2.5 py-1 text-caption font-medium ".concat(a?"bg-accent text-surface-0":"bg-surface-2 text-text-secondary hover:text-text-primary"),children:e.label},e.id)})})}function f(e){let{column:t,issues:n,onOpen:a}=e,{isOver:s,setNodeRef:i}=(0,o.Zj)({id:t.id,disabled:"synced"===t.id});return(0,r.jsxs)("section",{ref:i,"aria-label":t.label,className:"flex flex-col gap-2 rounded-lg border bg-surface-1 p-3 transition-colors ".concat(s?"border-accent bg-surface-2":"border-border-subtle"),children:[(0,r.jsxs)("header",{className:"flex items-baseline justify-between",children:[(0,r.jsx)("h2",{className:"text-heading font-medium text-text-primary",children:t.label}),(0,r.jsx)("span",{className:"text-caption text-text-tertiary",children:n.length})]}),(0,r.jsx)("p",{className:"text-caption text-text-tertiary",children:t.description}),(0,r.jsxs)("div",{className:"mt-1 flex flex-col gap-2",children:[n.map(e=>(0,r.jsx)(h,{issue:e,onOpen:a},e.id)),0===n.length&&(0,r.jsx)("div",{className:"rounded-md border border-dashed border-border-subtle py-6 text-center text-caption text-text-tertiary",children:"Empty"})]})]})}function h(e){let{issue:t,onOpen:n}=e,a="synced"===t.column,{attributes:s,listeners:c,setNodeRef:l,isDragging:d,transform:u}=(0,o.O1)({id:t.id,disabled:a}),x=u?{transform:"translate3d(".concat(u.x,"px, ").concat(u.y,"px, 0)"),zIndex:30}:{};return(0,r.jsxs)("article",{ref:l,style:x,...a?{}:s,...a?{}:c,onClick:e=>{u||(e.stopPropagation(),n(t.id))},className:"group rounded-md border bg-surface-0 p-3 text-left ".concat(a?"cursor-pointer":"cursor-grab active:cursor-grabbing"," ").concat(d?"border-accent shadow-lg":"border-border-subtle hover:border-border-default"),children:[(0,r.jsxs)("div",{className:"flex items-start justify-between gap-2",children:[(0,r.jsx)("span",{className:"font-mono text-caption text-text-tertiary",children:t.id}),(0,r.jsx)(i,{severity:t.severity})]}),(0,r.jsx)("h3",{title:t.title,className:"mt-1 text-body font-medium text-text-primary line-clamp-3",children:t.title}),(0,r.jsxs)("div",{className:"mt-2 flex flex-wrap items-center gap-1.5 text-caption",children:[(0,r.jsx)(y,{source:t.source}),(0,r.jsx)(v,{issue:t}),t.duplicate_of&&(0,r.jsxs)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-secondary",children:["dup of ",t.duplicate_of]}),"passing"===t.poc_status&&(0,r.jsx)("span",{className:"rounded-md bg-[var(--low)]/15 px-1.5 py-0.5 text-[var(--low)]",children:"PoC ✓"}),"failing"===t.poc_status&&(0,r.jsx)("span",{className:"rounded-md bg-[var(--critical)]/15 px-1.5 py-0.5 text-[var(--critical)]",children:"PoC ✕"}),t.resolution&&(0,r.jsx)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-secondary",children:t.resolution})]}),t.match_signals&&(0,r.jsx)("div",{className:"mt-2 text-caption text-text-tertiary",children:(0,r.jsx)("span",{className:"font-mono",children:[t.match_signals.contract?"contract":null,t.match_signals.function?"function":null,"different"!==t.match_signals.root_cause?"rc:".concat(t.match_signals.root_cause):null,"different"!==t.match_signals.attack_vector?"av:".concat(t.match_signals.attack_vector):null].filter(Boolean).join(" \xb7 ")})})]})}function y(e){var t;let{source:n}=e;return(0,r.jsx)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-tertiary",children:null!==(t=({manual:"Manual",auditagent:"AuditAgent",conformance:"Conformance",github:"GitHub"})[n])&&void 0!==t?t:n})}function v(e){var t,n;let{issue:a}=e,o=null!==(t=a.sync_state)&&void 0!==t?t:"none";if("none"===o)return null;if("unsynced"===o)return(0,r.jsx)("span",{title:"Run /sync-issues to push verified findings to GitHub.",className:"rounded-md border border-dashed border-border-default px-1.5 py-0.5 text-text-tertiary",children:"GH unsynced"});if("conflict"===o)return(0,r.jsx)("span",{title:"Local and remote diverged. Re-run /sync-issues to reconcile.",className:"rounded-md bg-[var(--critical)]/15 px-1.5 py-0.5 text-[var(--critical)]",children:"GH conflict"});let s="synced_closed"===o,i="GH #".concat(null!==(n=a.github_issue_number)&&void 0!==n?n:"?"," ").concat(s?"closed":"open"),c=s?"bg-surface-2 text-text-tertiary":"bg-[var(--low)]/15 text-[var(--low)]";return a.github_issue_url?(0,r.jsx)("a",{href:a.github_issue_url,target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),className:"rounded-md px-1.5 py-0.5 hover:underline ".concat(c),children:i}):(0,r.jsx)("span",{className:"rounded-md px-1.5 py-0.5 ".concat(c),children:i})}function g(e){var t,n,o;let{issue:s,formatCtx:i,onClose:d,onSave:u}=e,[m,b]=(0,a.useState)(s.title),[f,h]=(0,a.useState)(s.severity),[g,N]=(0,a.useState)(s.description),[w,_]=(0,a.useState)(s.recommendation),[C,k]=(0,a.useState)(null!==(t=s.resolution)&&void 0!==t?t:""),[S,H]=(0,a.useState)(null!==(n=s.update_from_client)&&void 0!==n?n:""),[M,G]=(0,a.useState)(!1),[E,I]=(0,a.useState)(!1),[R,O]=(0,a.useState)(!1),P=(0,a.useRef)(null),T="synced"===s.status;(0,a.useEffect)(()=>{var e;null===(e=P.current)||void 0===e||e.focus()},[]);let D=async()=>{let e=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"## [".concat(e.severity,"] ").concat(e.title,"\n\n").concat(l(e,t),"\n\n---\n")}({...s,title:m,severity:f,description:g,recommendation:w,resolution:C||void 0,update_from_client:S||void 0},i);try{await navigator.clipboard.writeText(e),I(!0),setTimeout(()=>I(!1),1500)}catch(e){}},U=async()=>{G(!0),await u({title:m,severity:f,description:g,recommendation:w,resolution:C||void 0,update_from_client:S||void 0}),G(!1),O(!1)};return(0,r.jsx)("div",{ref:P,tabIndex:-1,className:"fixed inset-0 z-50 flex items-center justify-center p-4 outline-none",style:{background:"var(--overlay-bg)"},onClick:d,onKeyDown:e=>{"Escape"===e.key&&d()},children:(0,r.jsxs)("div",{role:"dialog","aria-modal":"true","aria-label":"Issue ".concat(s.id),className:"relative flex h-[85vh] w-full max-w-4xl flex-col overflow-hidden border border-border-emphasis bg-surface-1 shadow-2xl",style:{borderRadius:"var(--radius-md)",boxShadow:"var(--shadow-accent)"},onClick:e=>e.stopPropagation(),children:[(0,r.jsxs)("div",{className:"flex items-center justify-between border-b border-border-default bg-surface-2 px-4 py-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2 text-caption",children:[(0,r.jsx)("span",{className:"font-mono text-body text-text-tertiary",children:s.id}),(0,r.jsx)(y,{source:s.source}),(0,r.jsx)(v,{issue:s})]}),(0,r.jsx)("button",{type:"button",onClick:d,"aria-label":"Close",className:"rounded p-1 text-text-tertiary hover:bg-surface-3 hover:text-text-primary",children:(0,r.jsx)("svg",{className:"h-5 w-5","aria-hidden":"true",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"})})})]}),(0,r.jsxs)("div",{className:"flex-1 overflow-y-auto p-4",children:[T&&(0,r.jsxs)("div",{className:"mb-4 rounded-md border border-border-default bg-surface-2 px-3 py-2 text-caption text-text-secondary",children:["This issue is synced to GitHub and is read-only here. Edit it on GitHub, then run"," ",(0,r.jsx)("span",{className:"font-mono",children:"/sync-issues"})," to pull the changes back."]}),R?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(j,{label:"Title",children:(0,r.jsx)("input",{type:"text",value:m,onChange:e=>b(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsxs)("div",{className:"mt-3 grid grid-cols-2 gap-3",children:[(0,r.jsx)(j,{label:"Severity",children:(0,r.jsx)("select",{value:f,onChange:e=>h(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60",children:x.map(e=>(0,r.jsx)("option",{value:e,children:e},e))})}),(0,r.jsx)(j,{label:"Resolution (verified only)",children:(0,r.jsxs)("select",{value:C,onChange:e=>k(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60",children:[(0,r.jsx)("option",{value:"",children:"— none —"}),p.map(e=>(0,r.jsx)("option",{value:e,children:e},e))]})})]}),(0,r.jsx)(j,{label:"Description",className:"mt-3",children:(0,r.jsx)("textarea",{value:g,onChange:e=>N(e.target.value),rows:8,disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsx)(j,{label:"Recommendation",className:"mt-3",children:(0,r.jsx)("textarea",{value:w,onChange:e=>_(e.target.value),rows:4,disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsx)(j,{label:"Update from the client",className:"mt-3",children:(0,r.jsx)("textarea",{value:S,onChange:e=>H(e.target.value),rows:4,disabled:T,placeholder:"How the client responded — used by /generate-overleaf.",className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})})]}):(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(c.MarkdownRenderer,{content:"**Title**: ".concat(s.title,"\n\n**Severity**: ").concat(s.severity,"\n\n").concat(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return l(e,t)}(s,i))})}),s.code_locations.length>0&&(0,r.jsxs)("div",{className:"mt-4",children:[(0,r.jsx)("div",{className:"mb-1 text-caption font-medium text-text-tertiary",children:"Code locations"}),(0,r.jsx)("ul",{className:"space-y-1 font-mono text-caption text-text-secondary",children:s.code_locations.map((e,t)=>(0,r.jsx)("li",{children:e.file},"".concat(e.file,"-").concat(t)))})]}),s.match_signals&&(0,r.jsxs)("div",{className:"mt-4",children:[(0,r.jsx)("div",{className:"mb-1 text-caption font-medium text-text-tertiary",children:"Duplicate signals"}),(0,r.jsx)("p",{className:"text-caption text-text-secondary",children:null!==(o=s.reasoning)&&void 0!==o?o:"Matched against another entry."}),(0,r.jsxs)("div",{className:"mt-1 flex flex-wrap gap-1.5 font-mono text-caption text-text-tertiary",children:[(0,r.jsxs)("span",{children:["contract: ",s.match_signals.contract?"✓":"—"]}),(0,r.jsxs)("span",{children:["function: ",s.match_signals.function?"✓":"—"]}),(0,r.jsxs)("span",{children:["root_cause: ",s.match_signals.root_cause]}),(0,r.jsxs)("span",{children:["attack_vector: ",s.match_signals.attack_vector]})]})]})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between gap-2 border-t border-border-default bg-surface-2 px-4 py-3",children:[(0,r.jsx)("button",{type:"button",onClick:D,className:"rounded-md border border-border-default px-3 py-1.5 text-body text-text-secondary hover:bg-surface-3 hover:text-text-primary",children:E?"Copied!":"Copy as HackMD"}),(0,r.jsx)("div",{className:"flex items-center gap-2",children:R?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("button",{type:"button",onClick:()=>O(!1),className:"rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary",children:"Cancel"}),(0,r.jsx)("button",{type:"button",onClick:U,disabled:M,className:"rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:opacity-50",children:M?"Saving…":"Save"})]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("button",{type:"button",onClick:d,className:"rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary",children:"Close"}),(0,r.jsx)("button",{type:"button",onClick:()=>O(!0),disabled:T,title:T?"Synced to GitHub — edit it there, then run /sync-issues to pull the changes back.":void 0,className:"rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:cursor-not-allowed disabled:opacity-40",children:"Edit"})]})})]})]})})}function j(e){let{label:t,className:n,children:a}=e;return(0,r.jsxs)("label",{className:"block ".concat(null!=n?n:""),children:[(0,r.jsx)("span",{className:"mb-1 block text-caption font-medium text-text-tertiary",children:t}),a]})}},9487:function(e,t,n){"use strict";n.d(t,{MarkdownRenderer:function(){return s}});var r=n(7821),a=n(8525),o=n(5592);function s(e){let{content:t,className:n}=e;return(0,r.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=n?n:""),children:(0,r.jsx)(a.UG,{remarkPlugins:[o.Z],children:t})})}}},function(e){e.O(0,[7397,3924,7115,3205,1744],function(){return e(e.s=4260)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9151],{3152:function(e,t,r){Promise.resolve().then(r.bind(r,2024))},2024:function(e,t,r){"use strict";r.d(t,{NotesClient:function(){return d}});var n=r(7821),a=r(8078),o=r(168),s=r(9487);let c="__other__";function d(e){var t,r;let{general:d,contractNotes:i,contractOptions:l,activeContract:u,unprocessedCount:p}=e,x=(0,o.useRouter)(),b=(0,a.useMemo)(()=>[{key:"general",label:"General",body:d},...i.map(e=>({key:e.name,label:e.name,body:e.body}))],[d,i]),m=u&&b.some(e=>e.key===u)?u:"general",[f,y]=(0,a.useState)(m),h=null!==(t=b.find(e=>e.key===f))&&void 0!==t?t:b[0],[g,v]=(0,a.useState)(!1),[j,N]=(0,a.useState)(""),[k,w]=(0,a.useState)(!1),C=async()=>{w(!0);try{var e;let t=await fetch("/api/notes",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({target:h.key,body:j})});if(!t.ok)throw Error(null!==(e=(await t.json().catch(()=>({}))).error)&&void 0!==e?e:"save failed");v(!1),x.refresh()}catch(e){U(e instanceof Error?e.message:"Save failed")}finally{w(!1)}},[S,R]=(0,a.useState)("idle"),[_,E]=(0,a.useState)(null!==(r=null!=u?u:l[0])&&void 0!==r?r:c),[M,P]=(0,a.useState)(""),[T,O]=(0,a.useState)(0),[G,U]=(0,a.useState)(null),q=(0,a.useRef)(null),z=(0,a.useRef)([]),D=(0,a.useRef)(null),I=(0,a.useRef)(null),A=(_===c?M:_).trim(),B=(0,a.useCallback)(async()=>{R("transcribing"),U("Transcribing locally…");try{let t=new Blob(z.current,{type:"audio/webm"}),r=new FormData;r.append("audio",t,"narration.webm"),r.append("contract",A||"general");let n=await fetch("/api/notes/record",{method:"POST",body:r}),a=await n.json().catch(()=>({}));if(422===n.status&&a.whisperMissing)U("Audio saved (".concat(a.audioSaved,") but no local Whisper engine is installed. ").concat(a.error));else if(n.ok)U("Session saved for ".concat(a.contract,". Run /diane in Claude Code to process it.")),x.refresh();else{var e;U(null!==(e=a.error)&&void 0!==e?e:"Recording failed")}}catch(e){U(e instanceof Error?e.message:"Recording failed")}finally{R("idle")}},[A,x]),F=(0,a.useCallback)(async()=>{U(null);try{let e=await navigator.mediaDevices.getUserMedia({audio:!0});D.current=e,z.current=[];let t=new MediaRecorder(e);t.ondataavailable=e=>{e.data.size>0&&z.current.push(e.data)},t.onstop=()=>{var e;null===(e=D.current)||void 0===e||e.getTracks().forEach(e=>e.stop()),I.current&&clearInterval(I.current),B()},q.current=t,t.start(),O(0),I.current=setInterval(()=>O(e=>e+1),1e3),R("recording")}catch(e){U("Could not access the microphone. Grant permission and try again.")}},[B]),J=(0,a.useCallback)(()=>{var e;null===(e=q.current)||void 0===e||e.stop()},[]),W="idle"===S&&A.length>0;return(0,n.jsxs)("div",{className:"space-y-sp-5",children:[(0,n.jsxs)("div",{className:"rounded-lg border border-border-subtle bg-surface-1 p-4",children:[(0,n.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,n.jsx)("span",{className:"text-heading font-medium text-text-primary",children:"Record narration"}),(0,n.jsxs)("select",{value:_,onChange:e=>E(e.target.value),disabled:"idle"!==S,className:"rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50",children:[l.map(e=>(0,n.jsx)("option",{value:e,children:e},e)),(0,n.jsx)("option",{value:c,children:"Other…"})]}),_===c&&(0,n.jsx)("input",{type:"text",placeholder:"Contract name",value:M,onChange:e=>P(e.target.value),disabled:"idle"!==S,className:"rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"}),"recording"===S?(0,n.jsxs)("button",{type:"button",onClick:J,className:"flex items-center gap-2 rounded-md bg-severity-critical px-3 py-1.5 text-body font-medium text-surface-0",children:[(0,n.jsx)("span",{className:"h-2.5 w-2.5 rounded-sm bg-surface-0"}),"Stop \xb7 ","".concat(Math.floor(T/60),":").concat(String(T%60).padStart(2,"0"))]}):(0,n.jsxs)("button",{type:"button",onClick:F,disabled:!W,className:"flex items-center gap-2 rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 transition-opacity duration-200 hover:opacity-90 disabled:opacity-40",children:[(0,n.jsx)("span",{className:"h-2.5 w-2.5 rounded-full bg-surface-0"}),"transcribing"===S?"Transcribing…":"Record"]})]}),G&&(0,n.jsx)("p",{className:"mt-3 text-caption text-text-secondary",children:G}),p>0&&(0,n.jsxs)("p",{className:"mt-2 text-caption text-accent",children:[p," narration session",p>1?"s":""," waiting — run"," ",(0,n.jsx)("code",{className:"font-mono",children:"/diane"})," in Claude Code to process."]})]}),(0,n.jsxs)("div",{className:"flex gap-sp-5",children:[(0,n.jsx)("nav",{className:"w-44 shrink-0 space-y-0.5",children:b.map(e=>(0,n.jsx)("button",{type:"button",onClick:()=>{y(e.key),v(!1)},className:"block w-full truncate rounded-md px-3 py-1.5 text-left text-body ".concat(e.key===f?"bg-accent-subtle font-medium text-accent":"text-text-secondary hover:bg-surface-2 hover:text-text-primary"),children:e.label},e.key))}),(0,n.jsxs)("div",{className:"min-w-0 flex-1 rounded-lg border border-border-subtle bg-surface-1 p-5",children:[(0,n.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,n.jsx)("span",{className:"text-heading font-medium text-text-primary",children:h.label}),g?(0,n.jsxs)("div",{className:"flex gap-2",children:[(0,n.jsx)("button",{type:"button",onClick:()=>v(!1),className:"rounded-md px-3 py-1 text-caption text-text-secondary hover:text-text-primary",children:"Cancel"}),(0,n.jsx)("button",{type:"button",onClick:C,disabled:k,className:"rounded-md bg-accent px-3 py-1 text-caption font-medium text-surface-0 disabled:opacity-50",children:k?"Saving…":"Save"})]}):(0,n.jsx)("button",{type:"button",onClick:()=>{N(h.body),v(!0)},className:"rounded-md border border-border-default px-3 py-1 text-caption text-text-secondary hover:bg-surface-2 hover:text-text-primary",children:"Edit"})]}),g?(0,n.jsx)("textarea",{value:j,onChange:e=>N(e.target.value),className:"h-[60vh] w-full resize-y rounded-md border border-border-default bg-surface-0 p-3 font-mono text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"}):h.body.trim()?(0,n.jsx)(s.MarkdownRenderer,{content:h.body}):(0,n.jsxs)("p",{className:"text-body text-text-tertiary",children:["No notes yet for ",h.label,". Record a narration above and run"," ",(0,n.jsx)("code",{className:"font-mono",children:"/diane"}),", or click Edit to write notes by hand."]})]})]})]})}},9487:function(e,t,r){"use strict";r.d(t,{MarkdownRenderer:function(){return s}});var n=r(7821),a=r(8525),o=r(5592);function s(e){let{content:t,className:r}=e;return(0,n.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=r?r:""),children:(0,n.jsx)(a.UG,{remarkPlugins:[o.Z],children:t})})}},168:function(e,t,r){"use strict";var n=r(7462);r.o(n,"usePathname")&&r.d(t,{usePathname:function(){return n.usePathname}}),r.o(n,"useRouter")&&r.d(t,{useRouter:function(){return n.useRouter}})}},function(e){e.O(0,[7397,7115,3205,1744],function(){return e(e.s=3152)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1931],{9778:function(e,r,n){Promise.resolve().then(n.bind(n,9487)),Promise.resolve().then(n.t.bind(n,7498,23))},9487:function(e,r,n){"use strict";n.d(r,{MarkdownRenderer:function(){return c}});var t=n(7821),o=n(8525),s=n(5592);function c(e){let{content:r,className:n}=e;return(0,t.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=n?n:""),children:(0,t.jsx)(o.UG,{remarkPlugins:[s.Z],children:r})})}}},function(e){e.O(0,[7397,7498,7115,3205,1744],function(){return e(e.s=9778)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7092],{5534:function(e,t,s){Promise.resolve().then(s.bind(s,7701))},7701:function(e,t,s){"use strict";s.d(t,{ProgressClient:function(){return x}});var a=s(7821),r=s(8078);function i(e){return"".concat(e.file,"::").concat(e.contract)}function l(e){return e>=67?"text-[var(--success)]":e>=34?"text-[var(--medium)]":"text-[var(--critical)]"}let c={contract:0,abstract:1,library:2,interface:3},n={contract:"bg-[var(--low)]/15 text-[var(--low)]",interface:"bg-[var(--info)]/15 text-[var(--info)]",library:"bg-[var(--info)]/15 text-[var(--info)]",abstract:"bg-[var(--high)]/15 text-[var(--high)]"},d=["Critical","High","Medium","Low","Info","Gas"],o={Critical:"bg-[var(--critical)]/15 text-[var(--critical)]",High:"bg-[var(--high)]/15 text-[var(--high)]",Medium:"bg-[var(--medium)]/15 text-[var(--medium)]",Low:"bg-[var(--low)]/15 text-[var(--low)]",Info:"bg-[var(--info)]/15 text-[var(--info)]",Gas:"bg-surface-3 text-text-secondary"};function x(e){let{contracts:t,totalsNsloc:s,reviewedContracts:x,auditSteps:p,findingsTotal:m,findingsBySeverity:u,findingsTracked:h}=e,[v,f]=(0,r.useState)(x),[b,g]=(0,r.useState)("all"),j=(0,r.useMemo)(()=>t.filter(e=>v[i(e)]).reduce((e,t)=>e+t.nsloc,0),[t,v]),y=(0,r.useMemo)(()=>t.filter(e=>v[i(e)]).length,[t,v]),N=s>0?j/s*100:0,w=p.filter(e=>e.completed).length/p.length*100,C=m>0?h/m*100:100,k=.7*N+.2*w+.1*C,M=(0,r.useMemo)(()=>{let e=[...t].sort((e,t)=>{var s,a;let r=null!==(s=c[e.type])&&void 0!==s?s:99,i=null!==(a=c[t.type])&&void 0!==a?a:99;return r!==i?r-i:t.nsloc-e.nsloc});return"all"===b?e:e.filter(e=>{let t=!!v[i(e)];return"checked"===b?t:!t})},[t,b,v]),O=(0,r.useMemo)(()=>Math.max(...t.map(e=>e.nsloc),1),[t]);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"mb-sp-5 rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"mb-2 flex items-baseline justify-between gap-sp-2",children:[(0,a.jsxs)("span",{className:"text-display font-semibold ".concat(l(k)),children:[k.toFixed(0),"%"]}),(0,a.jsxs)("span",{className:"text-caption text-text-tertiary inline-flex items-center gap-1.5",title:"Overall progress is a weighted average: 70% nSLOC reviewed (contract review), 20% audit-step completion, 10% findings triage.","aria-label":"Overall progress formula: 70% contract review by nSLOC, 20% step completion, 10% findings triage",children:["Contract Review ",N.toFixed(0),"% \xb7 Steps ",w.toFixed(0),"% \xb7 Triage ",C.toFixed(0),"%",(0,a.jsx)("svg",{className:"h-3.5 w-3.5 text-text-tertiary","aria-hidden":"true",viewBox:"0 0 16 16",fill:"currentColor",children:(0,a.jsx)("path",{d:"M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zM7.25 7.25a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0v-3.5zM8 4.5a.875.875 0 1 1 0 1.75A.875.875 0 0 1 8 4.5z"})})]})]}),(0,a.jsx)("div",{className:"h-3 w-full overflow-hidden rounded-full bg-surface-3",children:(0,a.jsx)("div",{className:"h-full rounded-full transition-all duration-300 ".concat(k>=67?"bg-[var(--success)]":k>=34?"bg-[var(--medium)]":"bg-[var(--critical)]"),style:{width:"".concat(Math.min(k,100),"%")},role:"progressbar","aria-valuenow":Math.round(k),"aria-valuemin":0,"aria-valuemax":100,"aria-label":"Overall audit progress"})}),(0,a.jsx)("p",{className:"mt-2 text-caption text-text-tertiary",children:"Weighted: 70% nSLOC reviewed \xb7 20% audit steps \xb7 10% findings triage. Hover the breakdown for details."})]}),(0,a.jsxs)("div",{className:"mb-sp-5 grid grid-cols-2 gap-sp-3 lg:grid-cols-4",children:[(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display ".concat(l(k)),children:[k.toFixed(0),"%"]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Overall Progress"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display text-text-primary",children:[y," / ",t.length]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Contracts Reviewed"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display text-text-primary",children:[j.toLocaleString()," / ",s.toLocaleString()]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"nSLOC Reviewed"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsx)("div",{className:"text-display text-text-primary",children:m}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Findings"}),Object.keys(u).length>0&&(0,a.jsx)("div",{className:"mt-2 flex flex-wrap gap-1.5",children:d.filter(e=>u[e]).map(e=>{var t;return(0,a.jsxs)("span",{className:"inline-flex items-center rounded-md px-2 py-0.5 text-caption font-medium ".concat(null!==(t=o[e])&&void 0!==t?t:"bg-surface-3 text-text-secondary"),children:[u[e]," ",e]},e)})})]})]}),(0,a.jsxs)("div",{className:"mb-sp-5",children:[(0,a.jsx)("h3",{className:"mb-sp-2 text-heading font-medium text-text-primary",children:"Audit Steps"}),(0,a.jsx)("div",{className:"flex flex-wrap gap-2",children:p.map(e=>(0,a.jsxs)("span",{className:"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-caption font-medium ".concat(e.completed?"bg-[var(--success)]/15 text-[var(--success)]":"bg-surface-3 text-text-tertiary"),children:[e.completed?(0,a.jsx)("svg",{className:"h-3.5 w-3.5",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z",clipRule:"evenodd"})}):(0,a.jsx)("svg",{className:"h-3.5 w-3.5",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm.75-11.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z",clipRule:"evenodd"})}),e.label]},e.label))})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"mb-sp-2 flex items-center justify-between",children:[(0,a.jsx)("h3",{className:"text-heading font-medium text-text-primary",children:"Contract Checklist"}),(0,a.jsx)("div",{className:"inline-flex rounded-md bg-surface-3 p-0.5",children:["all","unchecked","checked"].map(e=>(0,a.jsx)("button",{type:"button",onClick:()=>g(e),className:"rounded-sm px-2.5 py-1 text-caption font-medium ".concat(b===e?"bg-surface-2 text-text-primary shadow-sm":"text-text-secondary hover:text-text-primary"),children:"all"===e?"All (".concat(t.length,")"):"checked"===e?"Reviewed (".concat(y,")"):"Remaining (".concat(t.length-y,")")},e))})]}),(0,a.jsx)("div",{className:"overflow-x-auto rounded-md border border-border-default",children:(0,a.jsxs)("table",{className:"w-full text-left text-body",children:[(0,a.jsx)("thead",{className:"border-b border-border-default bg-surface-2 text-caption font-medium uppercase tracking-wider text-text-tertiary",children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{className:"w-10 px-sp-3 py-sp-2"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"Contract"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"File"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"Type"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2 text-right",children:"nSLOC"}),(0,a.jsx)("th",{className:"w-32 px-sp-4 py-sp-2",children:"Weight"})]})}),(0,a.jsx)("tbody",{className:"divide-y divide-border-subtle",children:M.map(e=>{var t;let s=i(e),r=!!v[s],l=e.nsloc/O*100;return(0,a.jsxs)("tr",{className:"h-9 transition-colors ".concat(r?"bg-[var(--success)]/5":"bg-surface-1"," hover:bg-surface-3"),children:[(0,a.jsx)("td",{className:"px-sp-3 py-sp-2",children:(0,a.jsx)("button",{type:"button",onClick:()=>(function(e){let t=i(e),s=!v[t];f(e=>({...e,[t]:s})),fetch("/api/progress",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reviewed_contracts:{[t]:s}})}).catch(()=>{})})(e),className:"flex h-5 w-5 items-center justify-center rounded border transition-colors ".concat(r?"border-[var(--accent)] bg-[var(--accent)] text-surface-0":"border-border-emphasis bg-surface-1 hover:border-[var(--accent)]"),children:r&&(0,a.jsx)("svg",{className:"h-3 w-3",viewBox:"0 0 12 12",fill:"none",stroke:"currentColor",strokeWidth:2,children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.5 6l2.5 2.5 4.5-5"})})})}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2 font-medium text-text-primary",children:e.contract}),(0,a.jsx)("td",{className:"max-w-[200px] truncate px-sp-4 py-sp-2 font-mono text-caption text-text-tertiary",children:e.file}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2",children:(0,a.jsx)("span",{className:"inline-flex items-center rounded-sm px-2 py-0.5 text-caption font-medium ".concat(null!==(t=n[e.type])&&void 0!==t?t:"bg-surface-3 text-text-secondary"),children:e.type})}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2 text-right font-mono tabular-nums text-text-secondary",children:e.nsloc}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2",children:(0,a.jsx)("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-surface-3",children:(0,a.jsx)("div",{className:"h-full rounded-full bg-[var(--accent)]",style:{width:"".concat(l,"%")}})})})]},s)})})]})})]})]})}}},function(e){e.O(0,[7115,3205,1744],function(){return e(e.s=5534)}),_N_E=e.O()}]);