kenmark-skills 1.0.0

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.
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: issues-list
3
+ preamble-tier: 2
4
+ version: 1.0.0
5
+ description: |
6
+ Display all open issues from brain/issues/ in a table grouped by area/type,
7
+ with columns for ID, Priority, and Title. Use when asked to "list issues",
8
+ "show issues", "issues dashboard", or "show all issues by type".
9
+ allowed-tools:
10
+ - Bash
11
+ - Read
12
+ - Grep
13
+ - Glob
14
+ triggers:
15
+ - list issues
16
+ - show issues
17
+ - issues dashboard
18
+ - show all issues by type
19
+ - issues by area
20
+ - issues by priority
21
+ ---
22
+
23
+ # Issues List — Grouped by Area/Type
24
+
25
+ ## Purpose
26
+
27
+ Read all open issues from `brain/issues/INDEX.md` and display them in a
28
+ well-structured table grouped by area (api, database, security, ui, worker,
29
+ testing, maintainability, connectors, oauth, dsl), sorted by priority within
30
+ each group.
31
+
32
+ ---
33
+
34
+ ## Step 1 — Read INDEX.md
35
+
36
+ ```bash
37
+ ISSUES_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/brain/issues"
38
+ cat "$ISSUES_DIR/INDEX.md"
39
+ ```
40
+
41
+ Parse:
42
+ - Each issue's ID, severity (P0/P1/P2), area, and title
43
+ - Completed count from the overview table
44
+
45
+ ---
46
+
47
+ ## Step 2 — Display grouped table
48
+
49
+ Group issues by `area` tag. Within each area group, sort by priority
50
+ (P0 → P1 → P2), then by ID ascending.
51
+
52
+ Output format:
53
+
54
+ ```
55
+ ISSUES DASHBOARD — {date}
56
+ ════════════════════════════════════════════════════════════════════
57
+
58
+ 🔴 api (1 P0 | 0 P1 | 0 P2)
59
+ ─────────────────────────────────────────────────────────────────
60
+ ID Prio Title
61
+ ─── ──── ───────────────────────────────────────────────────────
62
+ 001 P0 Client UI still calls deleted /api/connections routes
63
+
64
+ 🟡 database (1 P0 | 3 P1 | 0 P2)
65
+ ─────────────────────────────────────────────────────────────────
66
+ ID Prio Title
67
+ ─── ──── ───────────────────────────────────────────────────────
68
+ 013 P0 Prisma Credential model has no migration from legacy Connection table
69
+ 014 P1 connector_poll_state migration uses connectionId; schema uses credentialId
70
+ 021 P1 connector_event_deliveries migration indexes connectionId not credentialId
71
+
72
+ 🔴 security (2 P0 | 5 P1 | 2 P2)
73
+ ─────────────────────────────────────────────────────────────────
74
+ ID Prio Title
75
+ ─── ──── ───────────────────────────────────────────────────────
76
+ 016 P0 Connector-event HTTP ingest accepts unauthenticated POST when signatureScheme: 'none'
77
+ 017 P0 Google OAuth callback allows open redirect via returnTo in signed state
78
+ 018 P1 Connector-event HMAC verification stub always returns false
79
+ 019 P1 connector-event verifyHmacSha256 weaker than webhook-service
80
+ 020 P1 Connector-event ingest allows unbounded replay without Idempotency-Key
81
+ 023 P2 Credential test API returns raw err.message in 500 responses
82
+ 024 P1 Google OAuth callback always creates new credential (no dedupe by Google sub)
83
+ 026 P2 Public connector-event ingest has no rate limiting beyond 5MB
84
+
85
+ 🟡 worker (0 P0 | 3 P1 | 1 P2)
86
+ ─────────────────────────────────────────────────────────────────
87
+ ID Prio Title
88
+ ─── ──── ───────────────────────────────────────────────────────
89
+ 012 P1 Worker still queries removed Prisma Connection model and encryptedCredentials
90
+ 022 P1 Worker poller parses connectionId; web stores credentialId
91
+ 025 P1 Worker FakePrisma still mocks connection; tests pass while typecheck fails
92
+ 027 P2 Worker README and registry errors still describe Connection model
93
+
94
+ 🟡 testing (1 P0 | 6 P1 | 3 P2)
95
+ ─────────────────────────────────────────────────────────────────
96
+ ID Prio Title
97
+ ─── ──── ───────────────────────────────────────────────────────
98
+ 030 P0 E2E connections.spec.ts still calls deleted /api/connections
99
+ 031 P1 Missing API route tests for GET/POST /api/credentials
100
+ 032 P1 Missing API route tests for credentials/[id] DELETE and test POST
101
+ 033 P1 credential-service.test.ts missing listCredentials/getCredential/setStatus/validate coverage
102
+ 034 P1 FakePrisma credential.findMany ignores id: { in } filter
103
+ 035 P2 connector-event ingest test passes connectionId not credentialId
104
+ 036 P1 Missing HTTP tests for connector-events credentialId ingest route
105
+ 037 P1 Missing route tests for Google OAuth callback and Sheets credential APIs
106
+ 028 P2 credential-service update/test use Prisma where id only (defense in depth)
107
+
108
+ 🟡 ui (1 P0 | 1 P1 | 22 P2)
109
+ ─────────────────────────────────────────────────────────────────
110
+ ID Prio Title
111
+ ─── ──── ───────────────────────────────────────────────────────
112
+ 003 P0 CTAs link to /credentials/new but no App Router page exists
113
+ 007 P1 App shell nav still labeled "Connections" with /connections href
114
+ 008 P2 Workflow triggers empty state links to non-existent /credentials
115
+ 009 P2 UI still uses Connection* component and type names for credentials
116
+ 010 P2 Stale "connections" comments in OAuth routes and GoogleSheetsPicker
117
+ 011 P2 connector-event-service retains ConnectionRow internal naming
118
+ 038 P2 Event delivery run links use unsupported /runs?run= query
119
+ 039 P2 Dashboard and connectors status chips use legacy "Connection" wording
120
+ 040 P2 Dashboard connector library rows lack credential navigation
121
+ 041 P2 Connectors catalog rows lack per-connector credential actions
122
+ 042 P2 Google Sheets import picker Selects lack accessible labels
123
+ 043 P2 Credential delete dialog confirm field has aria-label only
124
+ 044 P2 Credential test button loses accessible name while loading
125
+ 045 P2 Event deliveries panel sits outside credential detail layout shell
126
+ 046 P2 Agent conversation tabs nest close buttons inside tab labels
127
+ 047 P2 Credentials list page missing breadcrumb trail
128
+ 048 P2 Dashboard hero CTA uses full-bleed primary card styling
129
+ 049 P2 Dashboard misstates connector catalogue completeness
130
+ 050 P2 List pages use CercliDataGrid vs raw table decision needed
131
+ 051 P2 Runs table displays opaque UUIDs instead of friendly identifiers
132
+ 052 P2 Trigger row icon buttons lack aria labels
133
+ 053 P2 Agent page skips PageHeader breadcrumb
134
+ 054 P2 Nav group labels appear below overline per spec
135
+ 055 P2 Governance tables use icon-only actions without labels
136
+ 056 P2 Dashboard runs display ISO timestamps instead of relative time
137
+ 057 P2 Runs filter chips confusing remove vs navigate behavior
138
+ 058 P2 Credentials empty state shows orphan card
139
+
140
+ 🟡 dsl (0 P0 | 1 P1 | 0 P2)
141
+ ─────────────────────────────────────────────────────────────────
142
+ ID Prio Title
143
+ ─── ──── ───────────────────────────────────────────────────────
144
+ 015 P1 Workflow DSL still uses connectionRef and missing_connection for credential IDs
145
+
146
+ 🟡 connectors (0 P0 | 0 P1 | 1 P2)
147
+ ─────────────────────────────────────────────────────────────────
148
+ ID Prio Title
149
+ ─── ──── ───────────────────────────────────────────────────────
150
+ 029 P2 Google Workspace connector comments reference removed Connection model
151
+
152
+ ════════════════════════════════════════════════════════════════════
153
+ Total open: 54 | Completed: 4
154
+ ```
155
+
156
+ **Area color coding:**
157
+ - 🔴 = area has any P0 issues (critical — fix first)
158
+ - 🟡 = area has P1/P2 only (high/medium priority)
159
+ - ⚪ = area has no open issues
160
+
161
+ ---
162
+
163
+ ## Step 3 — Workstream summary
164
+
165
+ At the bottom, show a compact workstream view grouping cross-area issues
166
+ by root cause:
167
+
168
+ ```
169
+ WORKSTREAMS
170
+ ─────────────────────────────────────────────────────────────────
171
+ Security (HMAC, open redirect, error leaks) 016, 017, 018, 019, 023, 024, 026
172
+ Worker package (stale model, poller mismatch) 012, 022, 025, 027
173
+ Database migrations (connectionId→credentialId) 013, 014, 021
174
+ DSL vocabulary (connectionRef rename) 015
175
+ UI rename (connections→credentials pages) 001, 003, 007, 008, 038, 039, 040, 041, 047, 048, 049, 050, 051, 052, 053, 054, 055, 056, 057, 058
176
+ UI component naming (stale Connection* names) 009, 010, 011
177
+ Test gaps (missing API, service, route tests) 028, 030, 031, 032, 033, 034, 035, 036, 037
178
+ Accessibility (a11y labels, contrast, layout) 042, 043, 044, 045, 046, 052, 054, 055
179
+ ════════════════════════════════════════════════════════════════════
180
+ ```
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: issues-maintenance
3
+ preamble-tier: 2
4
+ version: 1.0.0
5
+ description: |
6
+ Audit brain/issues/ for structural health: missing files, duplicate IDs across
7
+ open/completed, stale frontmatter (wrong status), INDEX.md drift from reality,
8
+ and orphaned completed issues never added to INDEX. Use when asked to
9
+ "check issues health", "audit issues", "fix issues tracking", or "issues maintenance".
10
+ allowed-tools:
11
+ - Bash
12
+ - Read
13
+ - Glob
14
+ - Grep
15
+ triggers:
16
+ - issues health
17
+ - check issues tracking
18
+ - audit issues
19
+ - fix issues tracking
20
+ - issues maintenance
21
+ - fix duplicate issues
22
+ - issues cleanup
23
+ ---
24
+
25
+ # Issues Maintenance — Health Check & Repair
26
+
27
+ ## Purpose
28
+
29
+ Audit and fix structural problems in `brain/issues/` tracking. This skill
30
+ catches the four categories of drift that accumulate over time:
31
+
32
+ 1. **Duplicates** — same issue ID exists in both `brain/issues/` (open) and
33
+ `brain/issues/completed/` (completed) — one must be removed
34
+ 2. **INDEX drift** — INDEX.md says an issue is completed but the file is still
35
+ in `brain/issues/`, or says open but file only exists in `completed/`
36
+ 3. **Missing fronts matter** — issue files missing required frontmatter fields
37
+ (id, title, severity, area, status) or with invalid values
38
+ 4. **Orphan completed** — files in `completed/` not listed in INDEX.md
39
+ Completed table
40
+
41
+ ---
42
+
43
+ ## Step 1 — Enumerate all issue IDs
44
+
45
+ ```bash
46
+ ISSUES_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/brain/issues"
47
+
48
+ # All IDs in open folder
49
+ ls "$ISSUES_DIR"/[0-9]*.md 2>/dev/null | xargs -I{} basename {} .md | \
50
+ sed 's/-.*//' | sort -n > /tmp/open_ids.txt
51
+
52
+ # All IDs in completed folder
53
+ ls "$ISSUES_DIR/completed"/[0-9]*.md 2>/dev/null | xargs -I{} basename {} .md | \
54
+ sed 's/-.*//' | sort -n > /tmp/completed_ids.txt
55
+
56
+ # IDs marked completed in INDEX.md
57
+ grep '^\[0' "$ISSUES_DIR/INDEX.md" | sed 's/.*\[\(.*\)\](.*/\1/' | \
58
+ sed 's/^0*//' | grep '^[0-9]' | sort -n > /tmp/index_completed_ids.txt
59
+
60
+ echo "OPEN count: $(wc -l < /tmp/open_ids.txt)"
61
+ echo "COMPLETED count: $(wc -l < /tmp/completed_ids.txt)"
62
+ echo "INDEX completed count: $(wc -l < /tmp/index_completed_ids.txt)"
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Step 2 — Detect duplicate IDs (open AND completed)
68
+
69
+ ```bash
70
+ comm -12 /tmp/open_ids.txt /tmp/completed_ids.txt
71
+ ```
72
+
73
+ Any output = that issue ID exists in both folders. This is the most common
74
+ drift. For each duplicate:
75
+ - If the issue is truly **completed**: the open file in `brain/issues/` is
76
+ wrong — delete it with `rm brain/issues/<file>`
77
+ - If the issue is truly **open**: the completed copy is wrong — delete it with
78
+ `rm brain/issues/completed/<file>`
79
+ - When in doubt, check the file's frontmatter `status:` field
80
+
81
+ If duplicates are found, after fixing run:
82
+
83
+ ```bash
84
+ git add -u brain/issues/completed/
85
+ git commit -m "fix(brain): remove duplicate completed issue files
86
+
87
+ The brain/issues/completed/ folder had accumulated duplicate copies of
88
+ issues that already existed in brain/issues/ (open issues being tracked).
89
+ Keep only truly completed issues in completed/; open issues remain in
90
+ brain/issues/ per the established structure."
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Step 3 — Check INDEX drift
96
+
97
+ Find IDs that INDEX says are completed but still have an open file:
98
+
99
+ ```bash
100
+ comm -12 /tmp/index_completed_ids.txt /tmp/open_ids.txt
101
+ ```
102
+
103
+ Find IDs that INDEX says are open but only exist in completed/ (forgot to
104
+ move to completed/ folder):
105
+
106
+ ```bash
107
+ comm -23 /tmp/index_completed_ids.txt /tmp/open_ids.txt | \
108
+ while read id; do
109
+ if ! ls brain/issues/${id}*.md 2>/dev/null | grep -q .; then
110
+ echo "$id — in INDEX as completed but no open file (OK to leave)"
111
+ fi
112
+ done
113
+ ```
114
+
115
+ Find IDs in the open folder that are NOT in INDEX's active list (forgot to
116
+ close or add to completed):
117
+
118
+ ```bash
119
+ # Get active IDs from INDEX P0/P1/P2 tables
120
+ grep '^\[0' "$ISSUES_DIR/INDEX.md" | grep -v 'completed/' | \
121
+ sed 's/.*\[\(.*\)\](.*/\1/' | sed 's/^0*//' | grep '^[0-9]' | \
122
+ sort -n > /tmp/index_active_ids.txt
123
+
124
+ comm -23 /tmp/open_ids.txt /tmp/index_active_ids.txt
125
+ ```
126
+
127
+ These IDs are in the open folder but not listed as active in INDEX — either
128
+ they were completed and not moved to `completed/`, or they're brand new and
129
+ need adding to INDEX.
130
+
131
+ ---
132
+
133
+ ## Step 4 — Frontmatter validation
134
+
135
+ For each issue file (both `brain/issues/` and `brain/issues/completed/`),
136
+ validate the frontmatter has required fields and valid values.
137
+
138
+ ```bash
139
+ for f in brain/issues/[0-9]*.md brain/issues/completed/[0-9]*.md; do
140
+ id=$(basename "$f" .md | sed 's/-.*//')
141
+ status=$(grep '^status:' "$f" 2>/dev/null | awk '{print $2}')
142
+ severity=$(grep '^severity:' "$f" 2>/dev/null | awk '{print $2}')
143
+ area=$(grep '^area:' "$f" 2>/dev/null | awk '{print $2}')
144
+
145
+ # Check for missing fields
146
+ if [ -z "$status" ]; then echo "MISSING status: $f"; fi
147
+ if [ -z "$severity" ]; then echo "MISSING severity: $f"; fi
148
+ if [ -z "$area" ]; then echo "MISSING area: $f"; fi
149
+
150
+ # Validate severity values
151
+ if [ -n "$severity" ] && ! echo "$severity" | grep -qE '^(P0|P1|P2)$'; then
152
+ echo "INVALID severity '$severity': $f"
153
+ fi
154
+
155
+ # Validate status values
156
+ if [ -n "$status" ] && ! echo "$status" | grep -qE '^(open|completed)$'; then
157
+ echo "INVALID status '$status': $f"
158
+ fi
159
+
160
+ # Cross-check: files in brain/issues/ should have status: open
161
+ if [[ "$f" == brain/issues/[0-9]*.md ]] && [ "$status" = "completed" ]; then
162
+ echo "STALE status=completed but in open folder: $f"
163
+ fi
164
+
165
+ # Cross-check: files in brain/issues/completed/ should have status: completed
166
+ if [[ "$f" == brain/issues/completed/* ]]; then
167
+ if [ "$status" != "completed" ]; then
168
+ echo "WRONG status=$status (expected completed) in completed folder: $f"
169
+ fi
170
+ fi
171
+ done
172
+ ```
173
+
174
+ For each finding:
175
+ - **MISSING**: Add the required frontmatter field
176
+ - **INVALID**: Fix the invalid value
177
+ - **STALE**: Move the file to `completed/` and update INDEX.md
178
+ - **WRONG**: Fix status to `completed`
179
+
180
+ ---
181
+
182
+ ## Step 5 — Orphan completed check (in INDEX but not in folder)
183
+
184
+ ```bash
185
+ comm -23 /tmp/index_completed_ids.txt /tmp/completed_ids.txt
186
+ ```
187
+
188
+ Each ID in this list appears in INDEX.md as completed but has no file in
189
+ `completed/`. This means the file was deleted or never migrated. Either:
190
+ - Create the completed issue file by moving from open (if it was fixed but
191
+ file not moved)
192
+ - Or the issue was already fixed and the completed file was deleted — no action
193
+
194
+ ---
195
+
196
+ ## Step 6 — Summary report
197
+
198
+ After all checks, print a structured summary:
199
+
200
+ ```
201
+ ISSUES MAINTENANCE REPORT — {date}
202
+ ════════════════════════════════════════════════════════════════════
203
+ Open issues: {N} (brain/issues/)
204
+ Completed issues: {N} (brain/issues/completed/)
205
+ INDEX active: {N} (from INDEX.md P0/P1/P2 tables)
206
+ INDEX completed: {N} (from INDEX.md completed table)
207
+
208
+ HEALTH:
209
+ Duplicates (open+completed): {N} — FIX or IGNORE
210
+ INDEX drift (open but marked completed): {N} — FIX
211
+ Missing frontmatter fields: {N} — FIX
212
+ Invalid field values: {N} — FIX
213
+ Orphan completed (in INDEX, no file): {N} — REVIEW
214
+ Untracked open (not in INDEX): {N} — ADD TO INDEX or COMPLETE
215
+
216
+ RECOMMENDED ACTIONS:
217
+ 1. [list of specific fix commands or file-level actions]
218
+ ════════════════════════════════════════════════════════════════════
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Step 7 — Apply fixes
224
+
225
+ For each fixable issue from the report:
226
+
227
+ - **Duplicate removal**: `rm brain/issues/<file>` or `rm brain/issues/completed/<file>`
228
+ - **Status fix**: Edit the frontmatter `status:` field
229
+ - **Move to completed**: `mv brain/issues/<file> brain/issues/completed/`
230
+ - **INDEX update**: Edit `brain/issues/INDEX.md` to move issue ID from active table
231
+ to completed table (add completed date)
232
+
233
+ After fixing, commit with:
234
+
235
+ ```bash
236
+ git add -u brain/issues/
237
+ git commit -m "fix(brain): issues maintenance — {brief description of what was fixed}"
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Hard Rules
243
+
244
+ 1. **Never delete an issue file without understanding why it exists** — if an
245
+ ID appears in both folders, one is wrong, not both
246
+ 2. **INDEX.md is the source of truth for counts** — if the INDEX and reality
247
+ disagree, trust what you observe in the files and fix INDEX to match
248
+ 3. **Always commit completed issue file moves separately** from code fixes so
249
+ the brain/ history is clean and reviewable
250
+ 4. **Do not touch open issue files** — the maintenance skill only moves,
251
+ renames, or updates completed issues; open issues are owned by whoever is
252
+ actively working on them
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: issues-scan
3
+ preamble-tier: 2
4
+ version: 1.0.0
5
+ description: |
6
+ Scan the codebase for new issues matching the connections→credentials migration
7
+ pattern. Detect bugs, gaps, and inconsistencies and create new issue files in
8
+ brain/issues/ with unique IDs. Use when asked to "scan for new issues",
9
+ "find new issues", or "discover issues".
10
+ allowed-tools:
11
+ - Bash
12
+ - Read
13
+ - Write
14
+ - Edit
15
+ - Grep
16
+ - Glob
17
+ - AskUserQuestion
18
+ triggers:
19
+ - scan for new issues
20
+ - find new issues
21
+ - discover issues
22
+ - new issues
23
+ - scan issues
24
+ ---
25
+
26
+ # Issues Scan — Discover New Issues from Codebase
27
+
28
+ ## Purpose
29
+
30
+ Scan the codebase for bugs, gaps, and inconsistencies that warrant new issues.
31
+ Assign unique IDs (next available in the 3-digit sequence), populate with
32
+ evidence, and optionally update INDEX.md.
33
+
34
+ ---
35
+
36
+ ## Step 1 — Read existing issues to avoid duplicates
37
+
38
+ ```bash
39
+ ISSUES_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/brain/issues"
40
+ echo "=== Existing issue IDs ==="
41
+ find "$ISSUES_DIR" -maxdepth 1 -name "*.md" -not -name "INDEX.md" -not -path "*/completed/*" | \
42
+ sed 's/.*\///' | sed 's/-.*//' | sort -n
43
+ echo "=== Highest ID ==="
44
+ find "$ISSUES_DIR" -maxdepth 1 -name "*.md" -not -name "INDEX.md" -not -path "*/completed/*" | \
45
+ sed 's/.*\///' | sed 's/-.*//' | sort -n | tail -1
46
+ ```
47
+
48
+ The next ID is the highest existing ID + 1, zero-padded to 3 digits.
49
+
50
+ ---
51
+
52
+ ## Step 2 — Scan patterns by workstream
53
+
54
+ Run targeted Grep searches for common issue patterns. For each search,
55
+ check if the finding matches an already-filed issue before creating a new one.
56
+
57
+ ### 2a — API routes (stale /connections references)
58
+
59
+ ```bash
60
+ # Returns lines referencing old connection API paths
61
+ grep -rn "/api/connections" --include="*.ts" --include="*.tsx" \
62
+ "$(git rev-parse --show-toplevel)/apps"2>/dev/null | grep -v "node_modules"
63
+ ```
64
+
65
+ ### 2b — Worker package (old Connection model references)
66
+
67
+ ```bash
68
+ grep -rn "connection\|Connection" --include="*.ts" \
69
+ "$(git rev-parse --show-toplevel)/apps/worker/src"2>/dev/null | \
70
+ grep -v "connectionId\|ConnectionRow\|connections\|test" | head -30
71
+ ```
72
+
73
+ ### 2c — DSL/schema (connectionRef vocabulary)
74
+
75
+ ```bash
76
+ grep -rn "connectionRef\|missing_connection\|connection_ref" --include="*.ts" \
77
+ "$(git rev-parse --show-toplevel)/packages/dsl" 2>/dev/null
78
+ ```
79
+
80
+ ### 2d — UI components (stale naming)
81
+
82
+ ```bash
83
+ grep -rn "Connection\|connection" --include="*.tsx" \
84
+ "$(git rev-parse --show-toplevel)/apps/web/src/app"2>/dev/null | \
85
+ grep -v "connectionId\|ConnectionRow\|test\|connectionString\|ConnectionsTable\|NewConnectionForm\|ConnectionDetail" | head -30
86
+ ```
87
+
88
+ ### 2e — Database migrations (connectionId vs credentialId)
89
+
90
+ ```bash
91
+ grep -rn "connectionId\|connection_id" --include="*.sql" \
92
+ "$(git rev-parse --show-toplevel)/packages/db" 2>/dev/null
93
+ ```
94
+
95
+ ### 2f — Security patterns (HMAC, rate limiting, error messages)
96
+
97
+ ```bash
98
+ # HMAC stub always returns false
99
+ grep -rn "verifyHmac\|hmac\|HMAC" --include="*.ts" \
100
+ "$(git rev-parse --show-toplevel)/apps/web/src"2>/dev/null | head -20
101
+
102
+ # Raw error.message in 500 responses
103
+ grep -rn "err\.message\|error\.message" --include="*.ts" \
104
+ "$(git rev-parse --show-toplevel)/apps/web/src/app/api" 2>/dev/null | head -20
105
+
106
+ # Missing rate limiting
107
+ grep -rn "rateLimit\|rate_limit\|RateLimit" --include="*.ts" \
108
+ "$(git rev-parse --show-toplevel)/apps/web/src" 2>/dev/null | head -10
109
+ ```
110
+
111
+ ### 2g — Missing test coverage
112
+
113
+ ```bash
114
+ # API routes without test files
115
+ find "$(git rev-parse --show-toplevel)/apps/web/src/app/api" -name "route.ts" | sort | \
116
+ while read route; do
117
+ test_file="${route%.ts}.test.ts"
118
+ [ -f "$test_file" ] || echo "NO TEST: $route"
119
+ done
120
+ ```
121
+
122
+ ### 2h — OAuth flows
123
+
124
+ ```bash
125
+ grep -rn "returnTo\|redirect\|oauth\|OAuth" --include="*.ts" \
126
+ "$(git rev-parse --show-toplevel)/apps/web/src/app/api/oauth" 2>/dev/null | head -20
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Step 3 — Deduplicate against existing issues
132
+
133
+ Before creating any new issue, cross-check against existing titles and evidence:
134
+
135
+ ```bash
136
+ ISSUES_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/brain/issues"
137
+ # List all existing issue titles
138
+ grep -h "^title:" "$ISSUES_DIR"/*.md 2>/dev/null | sort -u
139
+ ```
140
+
141
+ If a finding matches an existing issue (same file, same bug pattern), skip it.
142
+
143
+ ---
144
+
145
+ ## Step 4 — Create new issue files
146
+
147
+ For each unique finding, create a file at `brain/issues/{id}-{slug}.md`:
148
+
149
+ ```markdown
150
+ ---
151
+ id: {next-id}
152
+ title: {concise one-liner}
153
+ severity: P0|P1|P2
154
+ area: api|database|security|ui|worker|testing|maintainability|connectors|oauth|dsl
155
+ source: issues-scan
156
+ status: open
157
+ created: {YYYY-MM-DD}
158
+ files:
159
+ - path/to/file1
160
+ - path/to/file2
161
+ related:
162
+ - related-issue-id
163
+ ---
164
+
165
+ ## Summary
166
+
167
+ {2-3 sentences: what is wrong, where, and why it matters.}
168
+
169
+ ## Evidence
170
+
171
+ - `file:line` — description of the finding
172
+ - `file:line` — description of the finding
173
+
174
+ ## Suggested fix
175
+
176
+ {Concrete steps to fix the issue.}
177
+
178
+ ## Acceptance criteria
179
+
180
+ - [ ] {criterion 1}
181
+ - [ ] {criterion 2}
182
+ ```
183
+
184
+ **Slug rules:** lowercase, hyphens, no numbers in the slug part.
185
+ **ID sequence:** 001, 002, ... 058, 059, ... (always 3 digits, zero-padded).
186
+
187
+ ---
188
+
189
+ ## Step 4.5 — Closing completed issues
190
+
191
+ When an issue is resolved, **move it to `brain/issues/completed/` instead of deleting it**.
192
+ This preserves the audit trail and allows the INDEX.md completed table to link to the file.
193
+
194
+ ```bash
195
+ # Move a resolved issue to completed/
196
+ mv brain/issues/042-google-sheets-picker-selects-missing-accessible-labels.md \
197
+ brain/issues/completed/042-google-sheets-picker-selects-missing-accessible-labels.md
198
+ ```
199
+
200
+ Then update `brain/issues/INDEX.md`:
201
+ 1. Move the issue entry from the active priority table to the Completed table
202
+ 2. Add `completed: YYYY-MM-DD` to the issue frontmatter (edit the file directly)
203
+ 3. Decrement "Active issues" count, increment "Completed" count
204
+
205
+ **Rule: Never delete an issue file. Always move it to completed/.**
206
+
207
+ ---
208
+
209
+ ## Step 5 — Update INDEX.md
210
+
211
+ After creating new issues or closing completed ones, update `brain/issues/INDEX.md`:
212
+
213
+ 1. Increment "Active issues" count (new issues) or decrement (closed issues)
214
+ 2. Add new issues to the correct priority table (P0/P1/P2)
215
+ 3. Move closed issues to the Completed table with today's date
216
+ 4. Re-sort by ID within each priority section
217
+
218
+ ---
219
+
220
+ ## Step 6 — Report
221
+
222
+ Report:
223
+ - How many scans performed
224
+ - How many new issues created
225
+ - Which IDs were assigned
226
+ - Any findings that were deduplicated against existing issues