@bongos/core 1.19.39 → 1.19.41

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.
package/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.39",
6
- "core_contract": "1.19.39",
7
- "source_commit": "d61c48b44f6f117383794f20d49b5bd7b83d35d7",
5
+ "core_version": "1.19.41",
6
+ "core_contract": "1.19.41",
7
+ "source_commit": "ef65ee2c6121ae010e5ad2c1ac67b349cf9b9161",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-11T16:25:51.076Z",
9
+ "built_at": "2026-08-11T16:42:56.706Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 288,
12
+ "docs_redacted": 289,
13
13
  "agent_docs_stubbed": 21,
14
- "functional_verbatim": 1212,
14
+ "functional_verbatim": 1213,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1521,
20
- "tree_sha256": "14c889242599811c768effd9bc7ef844e4a6ee4ef0a041c74ff1629fda6f8907",
19
+ "file_count": 1523,
20
+ "tree_sha256": "8bdaf758774aaff55446f0ca5ac714795f728937d893a1085e21cf14db853915",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -154,6 +154,11 @@
154
154
  "mode": "0000644",
155
155
  "sha256": "e789b9157c9817bac354eaeed8152b53f3013f677934b062bca1c8e07f1f2dc8"
156
156
  },
157
+ {
158
+ "path": ".claude/skills/grade-audit/SKILL.md",
159
+ "mode": "0000644",
160
+ "sha256": "2b4d33396291f58f1c0d7c01d173b64d587b2a0a483b17328e57eb602a8df9fe"
161
+ },
157
162
  {
158
163
  "path": ".claude/skills/grade-recover/SKILL.md",
159
164
  "mode": "0000644",
@@ -1362,7 +1367,7 @@
1362
1367
  {
1363
1368
  "path": "docs/file-map.md",
1364
1369
  "mode": "0000644",
1365
- "sha256": "f51d3978f50ec9b2a34dc12b8fa3b9e8874543e5ab3503f72a09d7d2b06c565b"
1370
+ "sha256": "15801d1c51e7e46ccf4e52dad5d3d34e4adc96b72eed60e3ef2c9ccccf94d1da"
1366
1371
  },
1367
1372
  {
1368
1373
  "path": "docs/handoff-template.md",
@@ -3372,12 +3377,12 @@
3372
3377
  {
3373
3378
  "path": "modules/lifecycle/db.js",
3374
3379
  "mode": "0000644",
3375
- "sha256": "c7dfc49df888857c8975077a069e8730d16926d8e92776f09489f69a40068f31"
3380
+ "sha256": "57c88852ff0aa9f0b3673fa62b35f352d92cc9070e7872137003e395c42eb026"
3376
3381
  },
3377
3382
  {
3378
3383
  "path": "modules/lifecycle/done-when.js",
3379
3384
  "mode": "0000644",
3380
- "sha256": "803ce237622eaba321c6fd87db1645b8ba474af3d3486643aa9d52a6b2bd27d1"
3385
+ "sha256": "4e6f6af1cdaf026466155c1b0656a0e89872aa2bb264f36c50997ec4f069e808"
3381
3386
  },
3382
3387
  {
3383
3388
  "path": "modules/lifecycle/github-push.js",
@@ -4022,12 +4027,12 @@
4022
4027
  {
4023
4028
  "path": "package-lock.json",
4024
4029
  "mode": "0000644",
4025
- "sha256": "12291ed6b1927973c8d8c42c5384d7ba2368779b9a39c566336253069214cfb2"
4030
+ "sha256": "3d5765b40ed913e05bea697ecb878e98fa799e667a5352d4d8f37236a0b671b4"
4026
4031
  },
4027
4032
  {
4028
4033
  "path": "package.json",
4029
4034
  "mode": "0000644",
4030
- "sha256": "18139931183bcb89c64704ae5167ab99d1e8ae9841728870b277959af8523069"
4035
+ "sha256": "552a8ef2e0d6b1a69377cbf67bdc0b49574312a68b0ac4162bc606db5870afff"
4031
4036
  },
4032
4037
  {
4033
4038
  "path": "public-docs/index.html",
@@ -5647,7 +5652,7 @@
5647
5652
  {
5648
5653
  "path": "src/module-api.js",
5649
5654
  "mode": "0000644",
5650
- "sha256": "5f51d5f403c88eedf838dab85e4a9ddfa8ff575ec07b5f1189e12999001a335a"
5655
+ "sha256": "f1c39c1f5862092c57f1c5f5232dfce0a9ac41376838899c92c286df3178936f"
5651
5656
  },
5652
5657
  {
5653
5658
  "path": "src/module-loader/loader.js",
@@ -6167,7 +6172,7 @@
6167
6172
  {
6168
6173
  "path": "tests/criterion_progress.mjs",
6169
6174
  "mode": "0000644",
6170
- "sha256": "3ba6ac8556e9f5f0cd57f724cc136d024d64f684ff1d892bf6825e1b8ec0822d"
6175
+ "sha256": "e5b5ccf87a34658b3b25e4289caf702fbc44d77bacfec5bd823d5f6666ff213b"
6171
6176
  },
6172
6177
  {
6173
6178
  "path": "tests/criterion_proposal.mjs",
@@ -6437,7 +6442,7 @@
6437
6442
  {
6438
6443
  "path": "tests/goal_achievement.mjs",
6439
6444
  "mode": "0000644",
6440
- "sha256": "0838db3cbd5922e47777a25828b2a1b662284a251c5b8309610da634d448c90a"
6445
+ "sha256": "a6ddb674d4ce1f6da9eb7b653ef92b9f0fcc85387e70bdf5358aff84ef518824"
6441
6446
  },
6442
6447
  {
6443
6448
  "path": "tests/goal_advisory.mjs",
@@ -6477,7 +6482,7 @@
6477
6482
  {
6478
6483
  "path": "tests/goal_routes.mjs",
6479
6484
  "mode": "0000644",
6480
- "sha256": "cfd76bf4bd4ed22702861833966cd3eb02dee9815d11d0429cb2773ae17a48ad"
6485
+ "sha256": "4fd3ec8ef855b0131e17931908e9aebf16238d45febdf0b8306e2908152e2655"
6481
6486
  },
6482
6487
  {
6483
6488
  "path": "tests/goal_scope_check.mjs",
@@ -7464,6 +7469,11 @@
7464
7469
  "mode": "0000644",
7465
7470
  "sha256": "0eeccae9320f419667cf9ede3c13671a5b03571f7f2f17067bff1857234ddf91"
7466
7471
  },
7472
+ {
7473
+ "path": "tests/skill_grade_audit.mjs",
7474
+ "mode": "0000644",
7475
+ "sha256": "75ad6cd89f6e84e88764a99350e093df31a525eff392e3f84ce99fa32cb952c4"
7476
+ },
7467
7477
  {
7468
7478
  "path": "tests/skill_grade_recover.mjs",
7469
7479
  "mode": "0000644",
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: grade-audit
3
+ description: Manual-cadence accountability sweep over recent grades — the override ledger (what shipped despite a failing grade, and was it accounted for), dropped blocker/major findings on passing grades, a false-pass spot-check for runtime-behavior blindness, and an outage roll-up. Triggers when the user says "/grade-audit", "audit recent grades", "run the grade audit", "what shipped past the grader", or "weekly grader accountability sweep". Describes and queues follow-up work ONLY — it NEVER confirms a task, flips a status, or re-grades anything (ADR 0162; verdicts are advisory, never self-executing). Manually invoked; do not depend on scheduled routines.
4
+ ---
5
+
6
+ You are auditing the last week of grades for accountability gaps: work that shipped past a failing grade unaccounted for, real findings that rode passing grades into production and evaporated, and passes the panel structurally could not have judged. You **describe and queue**. You never confirm, flip, re-grade, or add a gate — every follow-up lands as a queued idea or a handoff to another skill.
7
+
8
+ ## The sweep window
9
+
10
+ ```bash
11
+ node scripts/gds/api.js GET "/api/gds/public/recent-shipped?days=7"
12
+ ```
13
+
14
+ For each shipped task in the window, read its persisted grade:
15
+
16
+ ```bash
17
+ node scripts/gds/api.js GET "/api/gds/tasks/<id>?include=grade"
18
+ ```
19
+
20
+ Bucket by grade shape: `passed`, failed, `signals.panel_outcome='unavailable'` (outage — no quality signal existed), or no grade row at all.
21
+
22
+ ## Leg 1 — the override ledger
23
+
24
+ For every shipped task whose grade **failed** (or is absent), the land needed an override. Cross-reference the ledger (Archon — `override_request.decide`):
25
+
26
+ ```bash
27
+ node scripts/gds/api.js GET "/api/gds/override-requests?status=approved"
28
+ ```
29
+
30
+ - Split **infra vs real** first: a fail that is actually `panel_outcome='unavailable'` (or the errored-worker zero-issue shape) is an outage artifact, not an overridden quality verdict — route those to the Leg 4 roll-up.
31
+ - A shipped task with a real failing grade and **no approved override-request row** landed via the raw confirm — the un-audited path. Name it in the report and print its unaddressed `issues[]` verbatim. That set is the ledger's debt.
32
+ - For each such task, queue the debt (see the queueing rule below) — do not chase the lander, do not un-ship anything.
33
+
34
+ ## Leg 2 — dropped findings on passing grades
35
+
36
+ A pass with `blocker`/`major` entries in `issues[]` shipped real findings that no process ever picks up (the evaluation counted 274 findings on passing ships with zero conversion paths). For each:
37
+
38
+ - **Worker-attribution filter:** findings attributed to the advisory **Narc** are the known noise class — triage them by hand (read the finding, decide), never auto-trust them into the queue. Findings from the gating workers (Quality, Hacker, Efficiency) or the deterministic pre-passes are higher-confidence — queue them unless plainly stale.
39
+ - **Queueing rule (idempotent):** first read the open inbox (`node scripts/gds/api.js GET /api/gds/inbox`) and skip any finding already queued — the marker is the deterministic title prefix. Then:
40
+
41
+ ```bash
42
+ node scripts/gds/capture.js "[grade-audit] task <id>: <finding summary>" --kind bug
43
+ ```
44
+
45
+ One idea per surviving finding, titled exactly `[grade-audit] task <id>: …` so a re-run of this audit files nothing twice.
46
+
47
+ ## Leg 3 — false-pass spot-check (runtime-behavior blindness)
48
+
49
+ The re-review found 2/8 material misses, both the same root cause: **the panel grades diff text and never reasons about runtime** — a CI workflow whose default shallow checkout broke on first run scored functional_fidelity 10/10, and an SSRF guard bypassable via HTTP redirect passed at 9.5. For the riskiest ships in the window — security surfaces, CI/workflow files, the largest diffs, and 9.5s with suspiciously few findings — re-read the merged diff asking one question: *what does this code do at runtime that the diff text does not show?* (defaults it inherits, redirects it follows, first-run behavior, tool/CI defaults). A suspicion is queued via the Leg 2 rule, prefixed the same way — never a re-grade, never a flipped verdict.
50
+
51
+ ## Leg 4 — outage roll-up
52
+
53
+ Count the window's `panel_outcome='unavailable'` rows. One-off outages just get the `--regrade` pointer in the report; a streak (2+ consecutive, or clustered on one builder) is a health problem — hand off to **/grader-health**, which owns outage diagnosis. Do not diagnose here.
54
+
55
+ ## Report shape
56
+
57
+ End with the ledger, most severe first:
58
+
59
+ 1. **Un-accounted overrides** — task, findings printed verbatim, queued-idea ref. A task that landed with no approved override-request row is the headline.
60
+ 2. **Queued findings** — what was filed this run, what was skipped as already-queued, what was held back by the Narc filter (and why).
61
+ 3. **Spot-check suspicions** — task, the runtime question the panel couldn't answer, queued-idea ref.
62
+ 4. **Outage roll-up** — count + the /grader-health handoff if a streak.
63
+
64
+ A clean week is four one-liners.
65
+
66
+ ## Constraints
67
+
68
+ - **Never confirm, never flip, never re-grade (ADR 0162).** Verdicts are advisory and never self-executing here — this skill's entire output is a report plus queued `idea_inbox` rows. If a leg tempts you to "just fix it", the fix is a queued idea or a claimed task, not an action inside the audit.
69
+ - **Manual cadence.** Invoke by hand (weekly is the intended rhythm). Do not wire it to cron or depend on `.claude/scheduled-tasks/`.
70
+ - **Idempotent re-runs.** The `[grade-audit] task <id>:` title marker + the inbox pre-check are what make running it twice harmless — keep both.
71
+ - **Archon surfaces degrade gracefully.** Without `override_request.decide`, Leg 1 can only report "ledger unreadable at this rank" — say so rather than skipping silently.
72
+
73
+ ## Files this skill touches
74
+
75
+ - Reads: `GET /api/gds/public/recent-shipped`, `GET /api/gds/tasks/:id?include=grade`, `GET /api/gds/override-requests?status=approved` (Archon), `GET /api/gds/inbox`.
76
+ - Writes: `idea_inbox` rows via `node scripts/gds/capture.js` (queued follow-ups only).
77
+ - Never calls: any confirm, status, grade, or regrade endpoint.
package/docs/file-map.md CHANGED
@@ -350,6 +350,7 @@ tests/
350
350
  ├── fix-task/SKILL.md ← /fix-task N — claim, fix (test-first), verify, and ship a single GDS task by id end to end; defers to /builder-claim + /builder-ship for their own mechanics
351
351
  ├── goal-create/SKILL.md ← /goal-create — plan and create ONE goal (the module-scoped workspace between version and criteria, ADR 0086) in one session: <redacted>, scope wall, 2-4 criteria, seed tasks; also the auto-pickup when a task is created with no goal to live in (goal_advisory); Metic+ (Archon for a protected-module scope)
352
352
  ├── goal-review/SKILL.md ← /goal-review — walk the criterion review queue: each auto-flagged 'met — pending review' criterion gets confirm / reject / defer; confirming a goal's last criterion auto-achieves it (ADR 0086 §6); Metic+
353
+ ├── grade-audit/SKILL.md ← /grade-audit — manual-cadence accountability sweep: override ledger (shipped-despite-fail, infra vs real, un-requested confirms named), dropped blocker/major findings on passes queued via capture.js with the advisory-Narc filter, runtime-blindness false-pass spot-check, outage roll-up → /grader-health; describes and queues only, never confirms (ADR 0162, task 1002657)
353
354
  ├── grade-recover/SKILL.md ← /grade-recover N — the 'my grade failed, now what' loop: diagnose outage vs fabricated fail vs genuine fail from the persisted grade, then run the right recovery (--regrade or the auditable override-request); adds no ship-path gate (ADR 0162, task 1002655)
354
355
  ├── grader-health/SKILL.md ← /grader-health — read-only grader health check: outage runs (n_unavailable streaks, outage-vs-rejection confirmation), pass-rate drift vs baseline, per-grade cost anomalies, stale model pins; emits exact --regrade remediation lines; optional --probe live calibration (Metic+, paid) (task 1002656)
355
356
  ├── idea-triage/SKILL.md ← /idea-triage — daily walk through the GDS idea_inbox (promote / discard / merge); Metic+
@@ -893,7 +893,10 @@ async function listCriteriaForGoal(goalId, deps = {}) {
893
893
  counts AS (
894
894
  SELECT tc.criterion_id,
895
895
  count(*)::int AS linked_tasks,
896
- count(*) FILTER (WHERE t.status <> 'shipped')::int AS unshipped_tasks
896
+ -- task 1002651 (migration 017's family): abandoned is a terminal
897
+ -- state (deliberately cut), not pending work — it must not hold a
898
+ -- criterion's "met — pending review" flip hostage forever.
899
+ count(*) FILTER (WHERE t.status NOT IN ('shipped', 'abandoned'))::int AS unshipped_tasks
897
900
  FROM task_criteria tc
898
901
  JOIN tasks t ON t.id = tc.task_id
899
902
  WHERE tc.criterion_id IN (SELECT id FROM crit)
@@ -126,7 +126,7 @@ async function listPendingReviewCriteria({ versionId = null } = {}, deps = {}) {
126
126
  JOIN tasks t ON t.id = tc.task_id
127
127
  WHERE c.satisfied = false
128
128
  GROUP BY c.id
129
- HAVING count(*) FILTER (WHERE t.status <> 'shipped') = 0
129
+ HAVING count(*) FILTER (WHERE t.status NOT IN ('shipped', 'abandoned')) = 0
130
130
  ),
131
131
  goal_unsat AS (
132
132
  SELECT goal_id, count(*)::int AS unsatisfied
@@ -269,7 +269,9 @@ function buildCriterionProgress(versionId, rows, unattributedCount = 0, goalRows
269
269
  const counts = { total: c.tasks.length };
270
270
  for (const t of c.tasks) counts[t.status] = (counts[t.status] || 0) + 1;
271
271
  const shipped = counts.shipped || 0;
272
- const remaining = c.tasks.filter((t) => t.status !== 'shipped');
272
+ // task 1002651: abandoned is terminal (cut work, migration 017's family) —
273
+ // it is not "remaining" and must not block the pending-review flip.
274
+ const remaining = c.tasks.filter((t) => t.status !== 'shipped' && t.status !== 'abandoned');
273
275
  // Auto-flag (ADR 0086 §6 / BV1.R63): a criterion is "met — pending review"
274
276
  // when it has ≥1 linked task and every one has shipped, but it has NOT yet
275
277
  // been confirmed (satisfied=false). Purely DERIVED — no stored column, no
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.39",
3
+ "version": "1.19.41",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.39",
9
+ "version": "1.19.41",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.39",
3
+ "version": "1.19.41",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
package/src/module-api.js CHANGED
@@ -505,8 +505,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
505
505
  // landed since 1.19.37 with no explicit bump. run 31511537558. (task 1002620)
506
506
  // 1.19.39 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
507
507
  // landed since 1.19.38 with no explicit bump. run 31512284325. (task 1002620)
508
+ // 1.19.40 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
509
+ // landed since 1.19.39 with no explicit bump. run 31513136724. (task 1002620)
510
+ // 1.19.41 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
511
+ // landed since 1.19.40 with no explicit bump. run 31513793491. (task 1002620)
508
512
  // ---------------------------------------------------------------------------
509
- const CORE_VERSION = '1.19.39'; // CI auto-patch carrier (ADR 0161); see changelog above
513
+ const CORE_VERSION = '1.19.41'; // CI auto-patch carrier (ADR 0161); see changelog above
510
514
 
511
515
  // A namespaced logger so a module's log lines are attributable + consistent.
512
516
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -247,6 +247,40 @@ test('isPendingReview: agrees with the rollup it was extracted from', () => {
247
247
  assert.equal(b.pending_review, false);
248
248
  });
249
249
 
250
+ // --- task 1002651 (idea 1000635): abandoned tasks are terminal, not "remaining" ---
251
+
252
+ test('1002651 one shipped + one abandoned → remaining=0, criterion flips to pending review', () => {
253
+ const rows = [
254
+ row(1, 20, 'c-ab', false, 300, 'landed', 'shipped'),
255
+ row(1, 20, 'c-ab', false, 301, 'cut', 'abandoned'),
256
+ ];
257
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
258
+ assert.equal(c.counts.remaining, 0, 'the abandoned task no longer counts as unshipped');
259
+ assert.deepEqual(c.remaining, [], 'the remaining list omits deliberately-cut work');
260
+ assert.equal(c.counts.abandoned, 1, 'the per-status bucket still reports it honestly');
261
+ assert.equal(c.pending_review, true, 'the criterion surfaces for human confirm instead of hanging forever');
262
+ });
263
+
264
+ test('1002651 abandoned does not hide genuinely unshipped work', () => {
265
+ const rows = [
266
+ row(1, 21, 'c-mix', false, 310, 'landed', 'shipped'),
267
+ row(1, 21, 'c-mix', false, 311, 'cut', 'abandoned'),
268
+ row(1, 21, 'c-mix', false, 312, 'in-flight', 'ready'),
269
+ ];
270
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
271
+ assert.equal(c.counts.remaining, 1);
272
+ assert.deepEqual(c.remaining.map((t) => t.id), [312], 'only the live task remains');
273
+ assert.equal(c.pending_review, false, 'live work still blocks the flip');
274
+ });
275
+
276
+ test('1002651 an all-abandoned criterion surfaces for review rather than auto-satisfying silently', () => {
277
+ const rows = [row(1, 22, 'c-cut', false, 320, 'cut', 'abandoned')];
278
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
279
+ // linked>0 + unshipped=0 → pending review: a human confirms or rejects what
280
+ // "met" means for a criterion whose entire linked work was cut.
281
+ assert.equal(c.pending_review, true);
282
+ });
283
+
250
284
  console.log('');
251
285
  console.log(`criterionProgress aggregation tests: ${passed} passed, ${failed} failed`);
252
286
  if (failed > 0) process.exit(1);
@@ -138,7 +138,9 @@ await test('listPendingReviewCriteria: SQL encodes the auto-flag (all shipped) +
138
138
  const sql = String(find(pool.queries, /WITH pending AS/).sql).replace(/\s+/g, ' ');
139
139
  // The derivation: satisfied=false AND every linked task shipped (zero non-shipped).
140
140
  assert.match(sql, /c\.satisfied = false/, 'only unconfirmed criteria are pending');
141
- assert.match(sql, /HAVING count\(\*\) FILTER \(WHERE t\.status <> 'shipped'\) = 0/, 'all linked tasks must be shipped');
141
+ // task 1002651: abandoned is terminal (cut work) — it must not hold the queue
142
+ // entry hostage, so the filter counts only live-and-unshipped tasks.
143
+ assert.match(sql, /HAVING count\(\*\) FILTER \(WHERE t\.status NOT IN \('shipped', 'abandoned'\)\) = 0/, 'all LIVE linked tasks must be shipped');
142
144
  assert.match(sql, /is_last_in_goal/, 'surfaces whether confirming completes the goal');
143
145
  });
144
146
 
@@ -173,6 +173,17 @@ await test('listCriteriaForGoal: pending_review false once the criterion is conf
173
173
  assert.equal(out.pending_review, false);
174
174
  });
175
175
 
176
+ await test('listCriteriaForGoal: the unshipped filter excludes abandoned (task 1002651)', async () => {
177
+ const pool = critPool([critRow()]);
178
+ await listCriteriaForGoal(3, { pool });
179
+ const sel = find(pool.queries, CRIT_SQL_RE);
180
+ assert.match(
181
+ sel.sql,
182
+ /FILTER \(WHERE t\.status NOT IN \('shipped', 'abandoned'\)\)/,
183
+ 'an abandoned linked task must not hold the pending-review flip hostage (migration 017 family)'
184
+ );
185
+ });
186
+
176
187
  await test('listCriteriaForGoal: pending_review false with NO linked tasks (vacuous truth guard)', async () => {
177
188
  const pool = critPool([critRow({ satisfied: false, linked_tasks: 0, unshipped_tasks: 0 })]);
178
189
  const [out] = await listCriteriaForGoal(3, { pool });
@@ -0,0 +1,50 @@
1
+ // tests/skill_grade_audit.mjs — the /grade-audit skill exists and covers the
2
+ // accountability sweep from the grader-hardening evaluation §6 (task 1002657):
3
+ // the override ledger, dropped blocker/major findings on passing grades (with
4
+ // the advisory-Narc attribution filter), the false-pass spot-check, and the
5
+ // outage roll-up handoff — describing and queueing ONLY (ADR 0162/0158: it
6
+ // never confirms a task or flips status).
7
+ import assert from 'node:assert/strict';
8
+ import { test } from 'node:test';
9
+ import { readFileSync, existsSync } from 'node:fs';
10
+ import { fileURLToPath } from 'node:url';
11
+ import path from 'node:path';
12
+
13
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
14
+ const SKILL = path.join(ROOT, '.claude', 'skills', 'grade-audit', 'SKILL.md');
15
+
16
+ // .claude/skills is INSTANCE-materialized; a neutral core checkout carries none
17
+ // (task 1002470) — skip there, same guard shape as file_map.mjs.
18
+ const skillsDirExists = existsSync(path.join(ROOT, '.claude', 'skills'));
19
+
20
+ test('/grade-audit exists, covers the sweep legs, and stays describe-and-queue only', (t) => {
21
+ if (!skillsDirExists) return t.skip('.claude/skills not materialized in this checkout');
22
+ assert.ok(existsSync(SKILL), '.claude/skills/grade-audit/SKILL.md must exist (task 1002657)');
23
+ const md = readFileSync(SKILL, 'utf8');
24
+
25
+ // Data surfaces.
26
+ assert.match(md, /\/api\/gds\/public\/recent-shipped/, 'sweeps the recent-shipped window');
27
+ assert.match(md, /include=grade/, 'reads each task\'s persisted grade');
28
+ assert.match(md, /\/api\/gds\/override-requests/, 'cross-references the override ledger');
29
+
30
+ // Leg 1 — override ledger: shipped-with-failing-grade, infra vs real split.
31
+ assert.match(md, /panel_outcome='unavailable'/, 'splits infra outage from a real fail');
32
+ assert.match(md, /no approved (override-)?request/i, 'names tasks that landed with no request row');
33
+
34
+ // Leg 2 — dropped findings with the worker-attribution filter.
35
+ assert.match(md, /blocker|major/, 'targets blocker/major findings on passing grades');
36
+ assert.match(md, /Narc/i, 'carries the advisory-Narc attribution filter');
37
+ assert.match(md, /capture\.js/, 'queues findings via capture.js');
38
+ assert.match(md, /\[grade-audit\]/, 'uses the deterministic title marker for idempotency');
39
+
40
+ // Leg 3 — false-pass spot-check (the 2/8 runtime-blindness classes).
41
+ assert.match(md, /runtime/i, 'spot-checks for runtime-behavior blindness');
42
+
43
+ // Leg 4 — outage roll-up handoff.
44
+ assert.match(md, /\/grader-health/, 'hands outage windows to /grader-health');
45
+
46
+ // The hard constraint.
47
+ assert.match(md, /never confirm/i, 'states it never confirms');
48
+ assert.match(md, /ADR 0162/, 'anchors the no-new-gates constraint');
49
+ assert.ok(!/curl[^\n]*\/confirm/.test(md), 'carries no ready-to-paste confirm curl');
50
+ });