@bongos/core 1.19.31 → 1.19.33

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.31",
6
- "core_contract": "1.19.31",
7
- "source_commit": "2e9b2abd8b182334b2eeb52e9815f499f340a3e3",
5
+ "core_version": "1.19.33",
6
+ "core_contract": "1.19.33",
7
+ "source_commit": "53e6567ae132484c597ea9f16ed141b3f38c4d98",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-11T14:47:58.633Z",
9
+ "built_at": "2026-08-11T15:10:31.561Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 286,
13
13
  "agent_docs_stubbed": 21,
14
- "functional_verbatim": 1207,
14
+ "functional_verbatim": 1208,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1514,
20
- "tree_sha256": "46bd790b34d0d3e1c2884f43eecf20fa747ddd57fb76ad0331f24dfb362fc6a2",
19
+ "file_count": 1515,
20
+ "tree_sha256": "c4eb5d8939b2cfefc3bddc6b97e016418d2fd5a7c99a9090116183a407bfc937",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -87,7 +87,7 @@
87
87
  {
88
88
  "path": ".claude/skills/builder-sequence/SKILL.md",
89
89
  "mode": "0000644",
90
- "sha256": "7772f3a12d76e6872ae4ae7b6cd16e9af839a0698b6ca89769429b38363157b2"
90
+ "sha256": "4284bc0184dd27752202ce02974fa8909fc462f546ee3cae38fa26b4ca071f81"
91
91
  },
92
92
  {
93
93
  "path": ".claude/skills/builder-setup/SKILL.md",
@@ -97,7 +97,7 @@
97
97
  {
98
98
  "path": ".claude/skills/builder-ship/SKILL.md",
99
99
  "mode": "0000644",
100
- "sha256": "c9ad7867f8cb4be347dde8b33ff5f3ae64ac6812a06209205f2d0c98fc16374d"
100
+ "sha256": "b21b28bc173431adbaf8c98faec0d32c84808258099a35c346b2cff6612f9f51"
101
101
  },
102
102
  {
103
103
  "path": ".claude/skills/builder-stage/SKILL.md",
@@ -142,7 +142,7 @@
142
142
  {
143
143
  "path": ".claude/skills/fix-task/SKILL.md",
144
144
  "mode": "0000644",
145
- "sha256": "ffae88604d885ab93557d315f82351c737df831e790cf4084c695172a73f0106"
145
+ "sha256": "1cb690260144b33fe2c149348d5f048454a90c525285aa768de326cf097a7be9"
146
146
  },
147
147
  {
148
148
  "path": ".claude/skills/goal-create/SKILL.md",
@@ -352,7 +352,7 @@
352
352
  {
353
353
  "path": "clients/bongos-client/index.d.ts",
354
354
  "mode": "0000644",
355
- "sha256": "7e6732aedaf7aaa3c657fc05e2dfd1dcc63e0fb06586d4077248d79d6198297e"
355
+ "sha256": "649065862779b6a9b871607debfee5ca18bde8cccbde4d00ea42b0d175938c9d"
356
356
  },
357
357
  {
358
358
  "path": "clients/bongos-client/index.mjs",
@@ -1282,12 +1282,12 @@
1282
1282
  {
1283
1283
  "path": "docs/api-reference.md",
1284
1284
  "mode": "0000644",
1285
- "sha256": "e28575936d1cb0cce684b20723743fd0d801ccd0a7ec58fff5360e160b23b5e7"
1285
+ "sha256": "8f83ea78edb348688196414bf2fb7352df3de0ded6abbbe44f2371c099431016"
1286
1286
  },
1287
1287
  {
1288
1288
  "path": "docs/api/openapi.json",
1289
1289
  "mode": "0000644",
1290
- "sha256": "b232ca69ae97842be312b76f6ac49ca66090c5c8fef392f609bb857e5f5c2979"
1290
+ "sha256": "27cf9c2b8548168f13dd2add1633bc9b3ca57f2fd5aa3b42ac8dbfb5cf60c41f"
1291
1291
  },
1292
1292
  {
1293
1293
  "path": "docs/architecture.md",
@@ -3362,7 +3362,7 @@
3362
3362
  {
3363
3363
  "path": "modules/lifecycle/db.js",
3364
3364
  "mode": "0000644",
3365
- "sha256": "85c856016e548399538734098d2f08d3920fb52814759ffa314ed26d98bf32eb"
3365
+ "sha256": "b2927d0be39718b274dda102c0d9d9035d323d1c60937ea7cd7a216315d5dc64"
3366
3366
  },
3367
3367
  {
3368
3368
  "path": "modules/lifecycle/done-when.js",
@@ -3467,7 +3467,7 @@
3467
3467
  {
3468
3468
  "path": "modules/lifecycle/routes/tasks.js",
3469
3469
  "mode": "0000644",
3470
- "sha256": "aa179ab79508a002e8f580fb839767d8aaa8b6f02a46d021bb61633dd9f8f9a4"
3470
+ "sha256": "8a6ecd6c7ecadcbec79483a589fef11957d13e2c94b569a9ff9da97f2908b713"
3471
3471
  },
3472
3472
  {
3473
3473
  "path": "modules/lifecycle/routes/versions.js",
@@ -4012,12 +4012,12 @@
4012
4012
  {
4013
4013
  "path": "package-lock.json",
4014
4014
  "mode": "0000644",
4015
- "sha256": "bb741853a2f1a9a62c27bfa769492a653a975103458dd872c4e51f7a7e4e6b9a"
4015
+ "sha256": "3b7b3283efe27724944d786e894dffd69731c4aca5f10c72c4dd318f2e05e1bf"
4016
4016
  },
4017
4017
  {
4018
4018
  "path": "package.json",
4019
4019
  "mode": "0000644",
4020
- "sha256": "1d0695ec7d2636aab3ca123aff5dfb89e5ccc6ddfd3cb3692b4570fd114e4f97"
4020
+ "sha256": "854d15e8cf4e81bf848db848280b88dfc40a3836858a0ad1ab008f808f96677b"
4021
4021
  },
4022
4022
  {
4023
4023
  "path": "public-docs/index.html",
@@ -5637,7 +5637,7 @@
5637
5637
  {
5638
5638
  "path": "src/module-api.js",
5639
5639
  "mode": "0000644",
5640
- "sha256": "1b4208d337a8dd0b88ba82cb42f5685537be1e4d12824c4972564cecbf84ce32"
5640
+ "sha256": "229087cbc698ed7d1a71aa3cea5b210528e2d5d1fcc7232ee6a536e9a2ca2010"
5641
5641
  },
5642
5642
  {
5643
5643
  "path": "src/module-loader/loader.js",
@@ -6832,7 +6832,7 @@
6832
6832
  {
6833
6833
  "path": "tests/migration_allocation.mjs",
6834
6834
  "mode": "0000644",
6835
- "sha256": "f014dee000483e41d01d41bc53361b81deba8ff1b21e5810aaf9200d65b8f366"
6835
+ "sha256": "10b1faf40cef2a3872c44109b63680e79922446b573bb476c79540d44a5622ac"
6836
6836
  },
6837
6837
  {
6838
6838
  "path": "tests/migration_namespace.mjs",
@@ -7439,6 +7439,11 @@
7439
7439
  "mode": "0000644",
7440
7440
  "sha256": "7e4f4270188771f1fc1fa7d68e9730b4562fb772ee9ecbd04b65fd1738ee9795"
7441
7441
  },
7442
+ {
7443
+ "path": "tests/skill_docs_unavailable_regrade.mjs",
7444
+ "mode": "0000644",
7445
+ "sha256": "0eeccae9320f419667cf9ede3c13671a5b03571f7f2f17067bff1857234ddf91"
7446
+ },
7442
7447
  {
7443
7448
  "path": "tests/skill_prefs.mjs",
7444
7449
  "mode": "0000644",
@@ -60,7 +60,7 @@ Between passes, report progress in one line — which task landed, what is next,
60
60
 
61
61
  Two more halts are yours to detect, not the script's:
62
62
 
63
- - **`ship_failed`** — the ship bounced, the grade failed, or the task did not reach `shipped`.
63
+ - **`ship_failed`** — the ship bounced, the grade failed, or the task did not reach `shipped`. Tell the two non-pass grade shapes apart first: **`GRADER UNAVAILABLE`** (`panel_outcome='unavailable'`) is **not a quality rejection** — the panel could not run, there is nothing to fix, and `node scripts/gds/ship.js <id> --regrade` re-runs the grade once the grader is reachable (a recoverable pause, not a halt). A genuine **`FAIL`** has findings: fix, commit, re-run `node scripts/gds/ship.js <id> --regrade`; if it still will not land, halt and report.
64
64
  - **`scope_surprise`** — the task turns out to need work outside its `touches[]`, or its premise is wrong (the thing it describes is already done, or no longer true). Do not quietly widen the scope; that is a new task.
65
65
 
66
66
  **On any halt:** leave the current task claimed, stop, and report — what you hit, which task, and what it needs from a human. Then say plainly what already landed. Everything shipped stays shipped; a halt costs the rest of the run, not the work done.
@@ -19,6 +19,11 @@ The CLI drives a **three-state lifecycle** with a subagent grader between states
19
19
 
20
20
  `/builder-ship` runs steps 1 + 2 inline; on auto-confirm it chains into step 3 (push, merge on main, smoke, deploy).
21
21
 
22
+ **Two non-pass grade shapes — tell them apart before reacting:**
23
+
24
+ - **`GRADER UNAVAILABLE`** (`panel_outcome='unavailable'`) — the grading panel could not run (worker infra outage or a pre-spawn bypass). This is **not a quality rejection**: no quality signal was produced, there are no findings to fix, and the task parks at `completed` ungraded (aggregates exclude these rounds as `n_unavailable` — an outage never counts as a zero). Recovery: when the grader is reachable again, re-run just the grade step — `node scripts/gds/ship.js <id> --regrade`.
25
+ - **A genuine `FAIL`** — the panel ran and returned findings. Fix the real issues, commit on the claim branch (the grader reads committed history, not the working tree), then re-grade with the same command: `node scripts/gds/ship.js <id> --regrade`.
26
+
22
27
  (Dev-box game changes, `--approved`) Persists the builder's sandbox self-approval to `tasks.sandbox_review` — `{ by, at, stamp, url, method }`, with `by`/`at` stamped server-side ([#1048](https://example.com/builders#/task/1048), ADR 0046).
23
28
 
24
29
  ## How to use
@@ -48,7 +48,9 @@ Runs the recurring "claim, fix, and ship task NNNN" macro end to end. Its own va
48
48
  node scripts/gds/api.js GET /api/gds/tasks/<id>
49
49
  ```
50
50
  - `shipped` (or `confirmed`, awaiting `/merge-mode`) → done; relay the completion card per `/builder-ship`.
51
- - Still `completed` → the grader bounced it. **Surface its feedback verbatim** — don't paraphrase or summarize away the specifics — then loop back to step 4 with that feedback in hand.
51
+ - Still `completed` → read the grade shape before reacting; two distinct outcomes park a task here:
52
+ - **`GRADER UNAVAILABLE`** (`panel_outcome='unavailable'`) — the panel could not run (infra outage/bypass). **Not a quality rejection**: there are no findings to fix. Re-run the grade when the grader is reachable: `node scripts/gds/ship.js <id> --regrade`.
53
+ - **A genuine `FAIL`** — the grader bounced it with findings. **Surface its feedback verbatim** — don't paraphrase or summarize away the specifics — then loop back to step 4 with that feedback in hand; fix, commit, and re-grade: `node scripts/gds/ship.js <id> --regrade`.
52
54
 
53
55
  ## Constraints
54
56
 
@@ -131,7 +131,7 @@ export interface PatchMeSkillPrefsResponse { prefs: unknown; effective: unknown;
131
131
  export interface PatchMeSoundPrefsResponse { prefs: unknown; effective: unknown; effective_map: unknown; volume: unknown; defaults: unknown; known_sounds: unknown }
132
132
  export interface PatchProjectsIdFeaturedRequest { featured: boolean }
133
133
  export interface PatchProjectsIdFeaturedResponse { project: unknown }
134
- export interface PatchTasksIdRequest { kind?: string; discipline?: string; requires_rank?: string; module_key?: string; goal_id?: number; parent_task_id?: StringifiableId; parallel_safe?: unknown; newcomer_friendly?: boolean; xenos_claimable?: boolean; security_sensitive?: boolean; automation_tag?: string; status?: unknown }
134
+ export interface PatchTasksIdRequest { kind?: string; discipline?: string; requires_rank?: string; module_key?: string; goal_id?: number; parent_task_id?: StringifiableId; parallel_safe?: unknown; newcomer_friendly?: boolean; xenos_claimable?: boolean; security_sensitive?: boolean; automation_tag?: string; needs_migration?: boolean; status?: unknown }
135
135
  export interface PostAccessRequestsRequest { github_login: string; display_name?: string; note?: string }
136
136
  export interface PostAccessRequestsResponse { ok: boolean; request?: unknown; already_pending?: boolean }
137
137
  export interface PostAuthAppPairApproveRequest { pair_code: string }
@@ -13789,6 +13789,9 @@
13789
13789
  "type": "string",
13790
13790
  "maxLength": 64
13791
13791
  },
13792
+ "needs_migration": {
13793
+ "type": "boolean"
13794
+ },
13792
13795
  "status": {}
13793
13796
  },
13794
13797
  "additionalProperties": false
@@ -463,7 +463,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
463
463
  | GET | `/api/bongos/tasks` | `any-builder` | — | reads (filtered task list); preserved per CRITICAL §4. |
464
464
  | POST | `/api/bongos/tasks` | `metic+archon` | `version_id`, `title`, `description`, `status`, `touches`, `est_minutes`, `est_cost_usd`, `manual_degree`, `priority`, `automation_tag`, `credits_reward`, `source`, `source_ref`, `value_summary`, `kind`, `discipline`, `parallel_safe`, `newcomer_friendly`, `security_sensitive`, `requires_rank`, `auto_detect_touches`, `criterion_ids`, `module_key`, `goal_id` | Task create/promote/edit (plan/scope mutation) is Metic+ as of ADR 0090 (owner decision 2026-06-26): a Metic who can run idea-triage may … |
465
465
  | GET | `/api/bongos/tasks/:id` | `any-builder` | — | single-task read; preserved per CRITICAL §4 (supervisor hot path). |
466
- | PATCH | `/api/bongos/tasks/:id` | `metic+archon` | `kind`, `discipline`, `requires_rank`, `module_key`, `goal_id`, `parent_task_id`, `parallel_safe`, `newcomer_friendly`, `xenos_claimable`, `security_sensitive`, `automation_tag`, `status` | PATCH /tasks/:id — supports body.parent_task_id (V2 C2), body.kind (V2 B1), body.discipline (mig 014), body.parallel_safe (mig 028), and … |
466
+ | PATCH | `/api/bongos/tasks/:id` | `metic+archon` | `kind`, `discipline`, `requires_rank`, `module_key`, `goal_id`, `parent_task_id`, `parallel_safe`, `newcomer_friendly`, `xenos_claimable`, `security_sensitive`, `automation_tag`, `needs_migration`, `status` | PATCH /tasks/:id — supports body.parent_task_id (V2 C2), body.kind (V2 B1), body.discipline (mig 014), body.parallel_safe (mig 028), and … |
467
467
  | POST | `/api/bongos/tasks/:id/abandon` | `metic+archon` | `reason` | POST /tasks/:id/abandon — terminal "retire this task" transition (task 987). |
468
468
  | POST | `/api/bongos/tasks/:id/attest-gate` | `any-builder` | `head_sha` | POST /tasks/:id/attest-gate — body: { head_sha }. |
469
469
  | POST | `/api/bongos/tasks/:id/confirm` | `any-builder` | `verified` | |
@@ -1686,6 +1686,11 @@ async function updateTaskParallelSafe(id, parallelSafe) { return updateTaskColum
1686
1686
 
1687
1687
  async function updateTaskAutomationTag(id, automationTag) { return updateTaskColumn(id, 'automation_tag', automationTag); }
1688
1688
 
1689
+ // task 1002648: correct the create-time migration heuristic's verdict — a false
1690
+ // flag makes claiming reserve a migration number that goes unused (task 1002614
1691
+ // burned 213 that way). Boolean only; the route validates.
1692
+ async function updateTaskNeedsMigration(id, needsMigration) { return updateTaskColumn(id, 'needs_migration', needsMigration === true); }
1693
+
1689
1694
  // BV1.R53 (task 1422): assign (or clear) the module_key that declares which
1690
1695
  // module this task's work belongs to. Drives module-disjointness at claim time
1691
1696
  // and diff⊆module enforcement at ship time. Accepts any key in MODULE_SCOPE_MAP,
@@ -5441,6 +5446,7 @@ module.exports = {
5441
5446
  updateTaskParallelSafe,
5442
5447
  updateTaskSecuritySensitive,
5443
5448
  updateTaskAutomationTag,
5449
+ updateTaskNeedsMigration,
5444
5450
  updateTaskModuleKey,
5445
5451
  listClaimableTasks,
5446
5452
  capNewcomerTasks,
@@ -69,8 +69,27 @@ const VALID_FITS = new Set(['autonomous', 'parallel']);
69
69
  const MIGRATION_KEYWORD_RE =
70
70
  /\bmigrations?\b|\bschema\b|\balter\s+table\b|\bcreate\s+table\b/i;
71
71
 
72
+ // task 1002648 (idea 1000642): compounds where the keyword is qualified by a
73
+ // NON-database noun are refactor/API language, not schema work, and are blanked
74
+ // before the keyword test. Two families surfaced in the wild: "gate/route/code
75
+ // migration" (code-migration phrasing) and "request/PATCH/:NNN schema"
76
+ // (API-shape phrasing — the task-1002614 false positive, which reserved
77
+ // migration 213 for zero schema change). Bare "migration"/"schema" and every
78
+ // DB-shaped compound ("schema migration", "DB schema") still flag.
79
+ const NON_DB_COMPOUND_RE = new RegExp(
80
+ [
81
+ /\b(?:gate|route|code)s?[-\s]+migrations?\b/.source,
82
+ /\bmigrations?\s+of\s+(?:gate|route|code)s?\b/.source,
83
+ /\b(?:request|response|body|validation|patch|post|put|delete|json|openapi|api|error)[-\s]+schemas?\b/.source,
84
+ /:\d+[-\s]+schemas?\b/.source,
85
+ ].join('|'),
86
+ 'gi'
87
+ );
88
+
72
89
  function detectsMigration(...texts) {
73
- return texts.some((t) => typeof t === 'string' && MIGRATION_KEYWORD_RE.test(t));
90
+ return texts.some(
91
+ (t) => typeof t === 'string' && MIGRATION_KEYWORD_RE.test(t.replace(NON_DB_COMPOUND_RE, ' '))
92
+ );
74
93
  }
75
94
 
76
95
  // ---- peer votes (V3.R25 #245) ---------------------------------------------
@@ -773,6 +792,7 @@ module.exports = function buildTasksRouter() {
773
792
  xenos_claimable: { type: 'boolean' }, // pre-rename alias for newcomer_friendly
774
793
  security_sensitive: { type: 'boolean' }, // task 990 — Archon-only ship broadcast
775
794
  automation_tag: { type: 'string', maxLength: 64 },
795
+ needs_migration: { type: 'boolean' }, // task 1002648 — correct the create-time heuristic's flag (the claim-time allocator keys off it)
776
796
  // task 1002614 (map D-18): DECLARED so strict-mode admits the request far
777
797
  // enough to reach the deliberate status_not_patchable hint below (#514) —
778
798
  // the hint had been unreachable dead code behind unknown_field since the
@@ -794,6 +814,7 @@ module.exports = function buildTasksRouter() {
794
814
  const hasParallelSafe = Object.prototype.hasOwnProperty.call(body, 'parallel_safe');
795
815
  const hasSecuritySensitive = Object.prototype.hasOwnProperty.call(body, 'security_sensitive');
796
816
  const hasAutomationTag = Object.prototype.hasOwnProperty.call(body, 'automation_tag');
817
+ const hasNeedsMigration = Object.prototype.hasOwnProperty.call(body, 'needs_migration');
797
818
  const hasRequiresRank = Object.prototype.hasOwnProperty.call(body, 'requires_rank');
798
819
  const hasModuleKey = Object.prototype.hasOwnProperty.call(body, 'module_key');
799
820
  const hasGoalId = Object.prototype.hasOwnProperty.call(body, 'goal_id');
@@ -808,8 +829,8 @@ module.exports = function buildTasksRouter() {
808
829
  if (Object.prototype.hasOwnProperty.call(body, 'status')) {
809
830
  return res.fail('status_not_patchable', { status: 400, message: STATUS_HINT });
810
831
  }
811
- if (!hasParent && !hasKind && !hasDiscipline && !hasNewcomerOrXenos && !hasParallelSafe && !hasSecuritySensitive && !hasAutomationTag && !hasRequiresRank && !hasModuleKey && !hasGoalId) {
812
- return res.fail('no_supported_fields', { status: 400, message: 'PATCH /tasks/:id supports parent_task_id, kind, discipline, parallel_safe, security_sensitive, automation_tag, requires_rank, module_key, goal_id, and newcomer_friendly (alias: xenos_claimable). ' + STATUS_HINT });
832
+ if (!hasParent && !hasKind && !hasDiscipline && !hasNewcomerOrXenos && !hasParallelSafe && !hasSecuritySensitive && !hasAutomationTag && !hasNeedsMigration && !hasRequiresRank && !hasModuleKey && !hasGoalId) {
833
+ return res.fail('no_supported_fields', { status: 400, message: 'PATCH /tasks/:id supports parent_task_id, kind, discipline, parallel_safe, security_sensitive, automation_tag, needs_migration, requires_rank, module_key, goal_id, and newcomer_friendly (alias: xenos_claimable). ' + STATUS_HINT });
813
834
  }
814
835
  // ---- task 1002609 (G-22): settle EVERY gate and validation before the ----
815
836
  // ---- FIRST write. The previous shape validated-and-wrote per field in ----
@@ -937,6 +958,15 @@ module.exports = function buildTasksRouter() {
937
958
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
938
959
  }
939
960
  }
961
+ if (hasNeedsMigration) {
962
+ try {
963
+ updated = await db.updateTaskNeedsMigration(id, body.needs_migration);
964
+ if (!updated) return res.fail('task_not_found', 404);
965
+ } catch (err) {
966
+ console.error('[gds] PATCH /tasks/:id needs_migration', err);
967
+ return res.fail('patch_failed', { status: 500, message: 'internal error' });
968
+ }
969
+ }
940
970
  if (hasModuleKey) {
941
971
  try {
942
972
  updated = await db.updateTaskModuleKey(id, body.module_key ?? null);
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.31",
3
+ "version": "1.19.33",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.31",
9
+ "version": "1.19.33",
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.31",
3
+ "version": "1.19.33",
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
@@ -489,8 +489,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
489
489
  // landed since 1.19.29 with no explicit bump. run 31502880105. (task 1002620)
490
490
  // 1.19.31 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
491
491
  // landed since 1.19.30 with no explicit bump. run 31503461332. (task 1002620)
492
+ // 1.19.32 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
493
+ // landed since 1.19.31 with no explicit bump. run 31504858325. (task 1002620)
494
+ // 1.19.33 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
495
+ // landed since 1.19.32 with no explicit bump. run 31505528205. (task 1002620)
492
496
  // ---------------------------------------------------------------------------
493
- const CORE_VERSION = '1.19.31'; // CI auto-patch carrier (ADR 0161); see changelog above
497
+ const CORE_VERSION = '1.19.33'; // CI auto-patch carrier (ADR 0161); see changelog above
494
498
 
495
499
  // A namespaced logger so a module's log lines are attributable + consistent.
496
500
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -27,6 +27,7 @@ import { strict as assert } from 'node:assert';
27
27
  import { readFileSync } from 'node:fs';
28
28
  import { fileURLToPath } from 'node:url';
29
29
  import { dirname, join } from 'node:path';
30
+ import { createRequire } from 'node:module';
30
31
 
31
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
32
33
  const repoRoot = join(__dirname, '..');
@@ -141,6 +142,56 @@ function shouldReserveMigrationNumber(task) {
141
142
  );
142
143
  });
143
144
 
145
+ // ---- task 1002648 (idea 1000642): the classifier must not flag non-DB ----
146
+ // "migration"/"schema" compounds, and a wrong verdict must be correctable
147
+ // via PATCH. Behavioral tests use the real exported detectsMigration.
148
+ const require = createRequire(import.meta.url);
149
+ const { detectsMigration } = require('../modules/lifecycle/routes/tasks.js');
150
+
151
+ await test('classifier ignores code-migration and API-schema phrasing (task 1002648)', () => {
152
+ const falsePositives = [
153
+ 'gate migration cleanup across the router',
154
+ 'finish the route-migration started in R86',
155
+ 'the migration of gates to the new catalog',
156
+ // the task-1002614 repro — reserved migration 213 for zero schema change:
157
+ 'the PATCH schema declares 11 keys and description is not among them',
158
+ 'status is undeclared in the :751 schema, so strict-mode validation returns unknown_field first',
159
+ 'align the request schema with the response schema',
160
+ ];
161
+ for (const text of falsePositives) {
162
+ assert.equal(detectsMigration(text), false, `must NOT flag: ${JSON.stringify(text)}`);
163
+ }
164
+ });
165
+
166
+ await test('classifier still flags real schema work (regression)', () => {
167
+ const truePositives = [
168
+ 'add a migration to backfill the marker',
169
+ 'ALTER TABLE builders add a column',
170
+ 'create table for the new ledger',
171
+ 'schema change on tasks',
172
+ 'a schema migration for the counter floor',
173
+ 'apply migrations/ before deploy',
174
+ ];
175
+ for (const text of truePositives) {
176
+ assert.equal(detectsMigration(text), true, `must flag: ${JSON.stringify(text)}`);
177
+ }
178
+ });
179
+
180
+ await test('PATCH /tasks/:id accepts needs_migration so a wrong flag is correctable', () => {
181
+ assert.ok(
182
+ /needs_migration: \{ type: 'boolean' \}/.test(routesSrc),
183
+ 'the PATCH schema must declare needs_migration as a boolean'
184
+ );
185
+ assert.ok(
186
+ /db\.updateTaskNeedsMigration\(id, body\.needs_migration\)/.test(routesSrc),
187
+ 'the PATCH handler must write via db.updateTaskNeedsMigration'
188
+ );
189
+ assert.ok(
190
+ /async function updateTaskNeedsMigration/.test(dbSrc) && /updateTaskNeedsMigration,/.test(dbSrc),
191
+ 'db.js must define and export updateTaskNeedsMigration'
192
+ );
193
+ });
194
+
144
195
  console.log(`\n${passed} passed, ${failed} failed`);
145
196
  if (failed > 0) process.exit(1);
146
197
  })();
@@ -0,0 +1,35 @@
1
+ // tests/skill_docs_unavailable_regrade.mjs — the builder-facing skills teach
2
+ // unavailable-vs-fail + the --regrade recovery command (task 1002654, Wave 2 of
3
+ // the grader-hardening evaluation). A GRADER UNAVAILABLE outcome
4
+ // (panel_outcome='unavailable', R01/R02) is not a quality rejection, and every
5
+ // skill that walks a builder through shipping must say so and name the recovery:
6
+ // node scripts/gds/ship.js <id> --regrade
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 SKILLS = ['builder-ship', 'fix-task', 'builder-sequence'];
15
+
16
+ // .claude/skills is INSTANCE-materialized; a neutral core checkout carries none
17
+ // (task 1002470) — check what exists, with a non-vacuity guard like file_map.mjs.
18
+ const present = SKILLS
19
+ .map((name) => ({ name, file: path.join(ROOT, '.claude', 'skills', name, 'SKILL.md') }))
20
+ .filter(({ file }) => existsSync(file));
21
+
22
+ test('ship-path skills name the unavailable-vs-fail distinction and the --regrade recovery', () => {
23
+ for (const { name, file } of present) {
24
+ const md = readFileSync(file, 'utf8');
25
+ assert.ok(md.includes("panel_outcome='unavailable'"),
26
+ `${name}: must name the grader-outage outcome (panel_outcome='unavailable')`);
27
+ assert.match(md, /not a quality rejection/i,
28
+ `${name}: must say an unavailable grade is NOT a quality rejection`);
29
+ assert.match(md, /node scripts\/gds\/ship\.js <(?:task-)?id> --regrade/,
30
+ `${name}: must name the recovery command: node scripts/gds/ship.js <id> --regrade`);
31
+ }
32
+ if (existsSync(path.join(ROOT, '.claude', 'skills'))) {
33
+ assert.ok(present.length > 0, '.claude/skills exists but none of the ship-path skills were found — the check went vacuous');
34
+ }
35
+ });