@bongos/core 1.19.44 → 1.19.45
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 +19 -14
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +6 -6
- package/docs/onboarding/diagrams/04-architecture.mmd +14 -12
- package/docs/onboarding/diagrams/assertions.json +2 -6
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/diagram-facts.js +43 -15
- package/src/module-api.js +3 -1
- package/tests/diagram_facts_offset.mjs +71 -0
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.45",
|
|
6
|
+
"core_contract": "1.19.45",
|
|
7
|
+
"source_commit": "290f271a1fb465184f1e30a82938dbb9fc0edd7f",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-11T17:
|
|
9
|
+
"built_at": "2026-08-11T17:49:40.375Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 290,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1214,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1525,
|
|
20
|
+
"tree_sha256": "0722aab68f68291e412986ff75bc5c9e53914d42248afd4dd8f3fddef07c1d82",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1402,12 +1402,12 @@
|
|
|
1402
1402
|
{
|
|
1403
1403
|
"path": "docs/onboarding/diagrams/03-drachmae-karma.mmd",
|
|
1404
1404
|
"mode": "0000644",
|
|
1405
|
-
"sha256": "
|
|
1405
|
+
"sha256": "2e43c5910ed036657a32faa1ec1af7f5bb1bfcbc5f743a276b44b3b8b8157e4e"
|
|
1406
1406
|
},
|
|
1407
1407
|
{
|
|
1408
1408
|
"path": "docs/onboarding/diagrams/04-architecture.mmd",
|
|
1409
1409
|
"mode": "0000644",
|
|
1410
|
-
"sha256": "
|
|
1410
|
+
"sha256": "4af79f61f5b1871e9c2ffaa4cee703f681516f2720782b4db05b797993e0a293"
|
|
1411
1411
|
},
|
|
1412
1412
|
{
|
|
1413
1413
|
"path": "docs/onboarding/diagrams/05-versioning.mmd",
|
|
@@ -1422,7 +1422,7 @@
|
|
|
1422
1422
|
{
|
|
1423
1423
|
"path": "docs/onboarding/diagrams/assertions.json",
|
|
1424
1424
|
"mode": "0000644",
|
|
1425
|
-
"sha256": "
|
|
1425
|
+
"sha256": "2716fcbc113ef046610023ff9bfefa57fe6564ead685cc3cf507f3d893ff0cd5"
|
|
1426
1426
|
},
|
|
1427
1427
|
{
|
|
1428
1428
|
"path": "docs/onboarding/diagrams/puppeteer-config.json",
|
|
@@ -4032,12 +4032,12 @@
|
|
|
4032
4032
|
{
|
|
4033
4033
|
"path": "package-lock.json",
|
|
4034
4034
|
"mode": "0000644",
|
|
4035
|
-
"sha256": "
|
|
4035
|
+
"sha256": "b2fc08b1d386c093629982f52a456c2b79c9f3824a22a696ed6fdb37a9073def"
|
|
4036
4036
|
},
|
|
4037
4037
|
{
|
|
4038
4038
|
"path": "package.json",
|
|
4039
4039
|
"mode": "0000644",
|
|
4040
|
-
"sha256": "
|
|
4040
|
+
"sha256": "d955953100c62b9b03bec90b70122f0a35d18bed91e60af2ae78f0573509a852"
|
|
4041
4041
|
},
|
|
4042
4042
|
{
|
|
4043
4043
|
"path": "public-docs/index.html",
|
|
@@ -4667,7 +4667,7 @@
|
|
|
4667
4667
|
{
|
|
4668
4668
|
"path": "scripts/gds/diagram-facts.js",
|
|
4669
4669
|
"mode": "0000644",
|
|
4670
|
-
"sha256": "
|
|
4670
|
+
"sha256": "fe57f283f06985234123a46b5ae055f4ecd9df26834cb99e58228a5cb649e0aa"
|
|
4671
4671
|
},
|
|
4672
4672
|
{
|
|
4673
4673
|
"path": "scripts/gds/discipline-modes.json",
|
|
@@ -5657,7 +5657,7 @@
|
|
|
5657
5657
|
{
|
|
5658
5658
|
"path": "src/module-api.js",
|
|
5659
5659
|
"mode": "0000644",
|
|
5660
|
-
"sha256": "
|
|
5660
|
+
"sha256": "ef3fa14314f284d7e93062ddc0b9ee7a046885bb13b20e41f62c7ef66eb29a24"
|
|
5661
5661
|
},
|
|
5662
5662
|
{
|
|
5663
5663
|
"path": "src/module-loader/loader.js",
|
|
@@ -6259,6 +6259,11 @@
|
|
|
6259
6259
|
"mode": "0000644",
|
|
6260
6260
|
"sha256": "c8547f65d1cff189f88afa528489f11585385f7a5cee868ed2fb7e474b47b614"
|
|
6261
6261
|
},
|
|
6262
|
+
{
|
|
6263
|
+
"path": "tests/diagram_facts_offset.mjs",
|
|
6264
|
+
"mode": "0000644",
|
|
6265
|
+
"sha256": "36a3bad951c6bfb4ee7b9230a5e6114095bb3b94f1f0019ec5c61c5168dee537"
|
|
6266
|
+
},
|
|
6262
6267
|
{
|
|
6263
6268
|
"path": "tests/diff_derived_signals.mjs",
|
|
6264
6269
|
"mode": "0000644",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Credits & karma — where each comes from, where each goes (AUTO-GENERATED from live facts — do not hand-edit; see scripts/gds/gen-diagrams.js)
|
|
3
3
|
---
|
|
4
4
|
flowchart LR
|
|
5
|
-
subgraph CURRENCY["
|
|
5
|
+
subgraph CURRENCY["CREDITS (the builder reward — equity / reputation, a stake, not a spendable currency)"]
|
|
6
6
|
direction TB
|
|
7
7
|
D_TOKEN["Ship a SESSION — the main reward\nround(true_cost_usd × 1.20) cents — cost-plus on real token spend\n(LIVE: priced server-side from token counts; ADR 0054, idempotent per session)"]
|
|
8
|
-
D_SHIP["
|
|
8
|
+
D_SHIP["SHIP a TASK — books when the code lands on main, not at confirm\ncredits_reward × kind multiplier (0.8–1.5×)\n+ 25% idea-promotion bonus to the capturer\n(LIVE: ADR 0023; pay-on-land ADR 0120; withheld entirely on a cost-plus-only instance, ADR 0146)"]
|
|
9
9
|
D_ACH["Unlock an achievement\nachievements.credit_bonus (LIVE: 9 seeded)\n+ small net-negative-ship bonus"]
|
|
10
|
-
D_BOUNTY["Red-team / security bounty\
|
|
10
|
+
D_BOUNTY["Red-team / security bounty\ncredits payout by severity\n(rate card LIVE: R65 #284 — src/bongos/security.js + shown on /builders;\nawarding still PLANNED: R67 #286 + vulnerability_reports R63 #282)"]
|
|
11
11
|
|
|
12
12
|
D_BAL["builders.total_credits\n(running balance per builder)"]
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ flowchart LR
|
|
|
16
16
|
D_ACH --> D_BAL
|
|
17
17
|
D_BOUNTY -.PLANNED.-> D_BAL
|
|
18
18
|
|
|
19
|
-
D_SPEND["Spent on: nothing yet.\
|
|
19
|
+
D_SPEND["Spent on: nothing yet.\nCredits are EQUITY / reputation — a stake, not a\nspendable currency. They persist losslessly across exit (migration 025)."]
|
|
20
20
|
D_BAL --> D_SPEND
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -32,6 +32,6 @@ flowchart LR
|
|
|
32
32
|
K_BAL --> K_SURF
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
note1["
|
|
35
|
+
note1["Credits measure OUTPUT (work shipped) — most of it now a cost-plus reward on real token spend.\nKarma measures PEER TRUST (how others rate your work); it does NOT gate rank promotions.\nBoth are reputation; neither is a spendable currency today.\nSeparately, an Archon can set a per-builder monthly budget cap to limit SPEND — but builders are uncapped by default (src/bongos/cost.js)."]
|
|
36
36
|
CURRENCY --- note1
|
|
37
37
|
KARMA --- note1
|
|
@@ -13,25 +13,26 @@ flowchart TB
|
|
|
13
13
|
CODE["the code — git clone + per-builder worktrees (.claude/worktrees/*)\nfor sub-Metic builders it lives ONLY here · CLAUDE.md is DESCRIPTIVE only"]
|
|
14
14
|
LOCAL["local ~/.claude memory\nMAP = MEMORY.md — a one-line index loaded EVERY session\n(a guard + auto-tidy keep it light; ADR 0074)\n+ drawer of full notes, loaded only on demand"]
|
|
15
15
|
ORIGIN["credential-less git origin\n(NO GitHub push secret on the box — by design)"]
|
|
16
|
-
BOXTOK["box-scoped
|
|
16
|
+
BOXTOK["box-scoped Bongos token\n(deny-by-default: bootstrap endpoints only;\nrejected for branch upload → re-auth first — LIVE)"]
|
|
17
17
|
PREVIEW["sandbox preview server on :3000\n→ a per-builder sandbox URL (LIVE)"]
|
|
18
18
|
SWITCH["on / off + browser terminal\n(desktop app LIVE · ttyd-over-tunnel LIVE)"]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
subgraph SERVER["THE SERVER —
|
|
22
|
-
API["/api/
|
|
21
|
+
subgraph SERVER["THE INSTANCE SERVER — localhost:3000 (the authority AND production)"]
|
|
22
|
+
API["/api/bongos/* (same Node process as the app)"]
|
|
23
|
+
CORE["@bongos/core — the build platform, VENDORED as a pinned dependency\n(ADR 0108: central updates are a version-pin bump via `bongos upgrade`, not a merge)"]
|
|
23
24
|
DB["Postgres — tasks · claims · versions · builders.rank\ncredit_log · grades · idea_inbox · builder_memory\n(KING for work-state + permissions; CLAUDE.md only describes it)"]
|
|
24
25
|
RANK["auth.requireRank — checked per request, NO caching\n(ADR 0016: a demotion takes effect on the next request)"]
|
|
25
|
-
SRVMEM["server-side builder_memory store\nLIVE: per-builder · isolated ·
|
|
26
|
+
SRVMEM["server-side builder_memory store\nLIVE: per-builder · isolated · forensic read at Metic+ (ADR 0157)\nyour memory follows you across machines & worktrees\n/recall fetches only the relevant subset (ADR 0060)\n(R54 / R55 / R56 / R57 — all shipped)"]
|
|
26
27
|
PUSHTOK["server-held push credential\n(GitHub App, short-lived tokens — the box never holds it)"]
|
|
28
|
+
DEPLOY["auto-pull deploy (ADR 0129)\na timer pulls THIS instance repo every ~2 min →\nnpm ci if the pin changed → migrate → regenerate docs → restart → healthz"]
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
subgraph GH["GITHUB — the
|
|
30
|
-
MAIN["branch-protected main\n(no direct push; PR + required checks only)"]
|
|
31
|
-
ACTIONS["GitHub Actions — deploy-prod.yml"]
|
|
31
|
+
subgraph GH["GITHUB — the instance's own repo (github.com/lars589/cloud-bongos)"]
|
|
32
|
+
MAIN["branch-protected main\n(no direct push; PR + required checks only)\nholds config + the pinned @bongos/core — NOT the platform source"]
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
LIVEAPP["the live app — localhost:3000\n(same server · same Node process as /api/bongos/*)"]
|
|
35
36
|
|
|
36
37
|
APP -->|"1 · toggle box on / open terminal"| SWITCH
|
|
37
38
|
SWITCH --> WORKER
|
|
@@ -39,13 +40,14 @@ flowchart TB
|
|
|
39
40
|
WORKER -->|"3 · re-auth in browser"| API
|
|
40
41
|
WORKER -->|"4 · upload branch as a git bundle"| API
|
|
41
42
|
API -->|"5 · push + open PR + auto-merge (LIVE)"| MAIN
|
|
42
|
-
MAIN -->|"6 ·
|
|
43
|
-
|
|
43
|
+
MAIN -->|"6 · the instance auto-pulls its own main"| DEPLOY
|
|
44
|
+
DEPLOY -->|"7 · migrate · regenerate docs · restart"| LIVEAPP
|
|
45
|
+
CORE --- API
|
|
44
46
|
PUSHTOK -.-> API
|
|
45
47
|
ORIGIN -. "direct push blocked" .-> MAIN
|
|
46
48
|
API -. "claim / ship / cost" .-> WORKER
|
|
47
49
|
LOCAL -->|"sync on ship (push)"| SRVMEM
|
|
48
50
|
SRVMEM -->|"pull on session start"| LOCAL
|
|
49
51
|
|
|
50
|
-
NEXT["The whole box → production path is LIVE and hardware-verified end-to-end (1027 · 1028)."]
|
|
51
|
-
|
|
52
|
+
NEXT["The whole box → production path is LIVE: a credential-less box uploads its branch, the server merges it, and the instance auto-pulls + redeploys — hardware-verified end-to-end (1027 · 1028)."]
|
|
53
|
+
LIVEAPP --- NEXT
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
},
|
|
108
108
|
"architecture": {
|
|
109
109
|
"diagram": "04-architecture",
|
|
110
|
-
"_comment": "Pieces of 04-architecture gated on a backing GDS task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The device/box/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. DEV BOX + box→prod: 904/774 (on-off + browser terminal), 897 (sandbox preview), 919 (scoped box token), 1025 (server-mediated publish),
|
|
110
|
+
"_comment": "Pieces of 04-architecture gated on a backing GDS task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The device/box/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. DEV BOX + box→prod: 904/774 (on-off + browser terminal), 897 (sandbox preview), 919 (scoped box token), 1025 (server-mediated publish), 1028 (GitHub App push credential), 1027 (end-to-end hardware-verify). The deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated GitHub-Actions CI job, so 859 is intentionally absent. Supersedes the old server_repo_memory + dev_box slices.",
|
|
111
111
|
"live_tasks": [
|
|
112
112
|
{
|
|
113
113
|
"id": 273,
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"id": 276,
|
|
126
|
-
"label": "R57 — per-builder isolation +
|
|
126
|
+
"label": "R57 — per-builder isolation + forensic read at Metic+ (ADR 0157)"
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
"id": 904,
|
|
@@ -145,10 +145,6 @@
|
|
|
145
145
|
"id": 1025,
|
|
146
146
|
"label": "server-mediated branch publish (box uploads bundle, server pushes)"
|
|
147
147
|
},
|
|
148
|
-
{
|
|
149
|
-
"id": 859,
|
|
150
|
-
"label": "CI self-deploy (Actions deploys main to the droplet)"
|
|
151
|
-
},
|
|
152
148
|
{
|
|
153
149
|
"id": 1027,
|
|
154
150
|
"label": "hardware-verify the box-to-prod flow end-to-end"
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.45",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.45",
|
|
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.
|
|
3
|
+
"version": "1.19.45",
|
|
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",
|
|
@@ -149,9 +149,11 @@ async function liveVersions() {
|
|
|
149
149
|
// the two diagrams reference (authenticated, since /tasks/:id is not public) and
|
|
150
150
|
// the count of seeded achievements (for 03's "N seeded" label).
|
|
151
151
|
//
|
|
152
|
-
// The task ids below are the
|
|
153
|
-
// refs — the V3 renumber (migration 021) made
|
|
154
|
-
// ids in a couple of places
|
|
152
|
+
// The task ids below are the ORIGIN instance's live GDS ids (verified
|
|
153
|
+
// 2026-05-30), NOT the seed refs — the V3 renumber (migration 021) made
|
|
154
|
+
// seed-ref R##s diverge from live ids in a couple of places (so a source_ref
|
|
155
|
+
// lookup would be unreliable). On an instance that imported the origin backlog
|
|
156
|
+
// under new ids they resolve through the IMPORT_ID_OFFSETS probe below. In particular the bounty-AWARDING gate is the
|
|
155
157
|
// "auto-confirmation flow that awards drachmae" task (#286 / live ref R67), not
|
|
156
158
|
// the security-ADR-stub task that the old hand-authored .mmd loosely called
|
|
157
159
|
// "R68" (#287). Each entry documents the piece it drives.
|
|
@@ -242,31 +244,56 @@ function repoSlug(repo) {
|
|
|
242
244
|
return 'the project repo';
|
|
243
245
|
}
|
|
244
246
|
|
|
247
|
+
// An instance that IMPORTED the origin backlog renumbers the origin tasks by a
|
|
248
|
+
// uniform offset (the reference import renumbered +1000000: origin 276 → live
|
|
249
|
+
// 1000276), so the origin ids above 404 there and every conceptual regen
|
|
250
|
+
// silently degraded to the null sentinel (task 1002667). Probe each id at these
|
|
251
|
+
// offsets, origin-first; the first offset that resolves LOCKS for the rest of
|
|
252
|
+
// the bundle — a uniform renumber can't mix offsets, and locking means a
|
|
253
|
+
// coincidental id collision on a foreign instance degrades to the sentinel
|
|
254
|
+
// instead of blending statuses from two different task sets.
|
|
255
|
+
const IMPORT_ID_OFFSETS = [0, 1_000_000];
|
|
256
|
+
|
|
245
257
|
// Fetch live status for each conceptual task id. Authenticated (allow-unauth so
|
|
246
258
|
// a 401 returns a sentinel instead of exiting the whole process via cli-lib's
|
|
247
|
-
// guidance path).
|
|
248
|
-
//
|
|
249
|
-
|
|
259
|
+
// guidance path). Ids resolve through the IMPORT_ID_OFFSETS probe but the
|
|
260
|
+
// returned map stays keyed by the ORIGIN ids, so the templates, assertions.json,
|
|
261
|
+
// and the drift checker all keep one stable key set. Returns null on ANY failure
|
|
262
|
+
// (no session, network, non-200) so the generator can SKIP regenerating 03/04
|
|
263
|
+
// rather than emit wrong data.
|
|
264
|
+
async function conceptualFacts({ repo, migs, api }) {
|
|
250
265
|
// Reuse the caller's migrations read when provided (gatherFacts already reads
|
|
251
266
|
// them once); only fall back to a fresh read for standalone callers.
|
|
252
267
|
migs = migs || readMigrations(repo);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
268
|
+
if (!api) {
|
|
269
|
+
const { cliClient } = require('./cli-lib');
|
|
270
|
+
// allow-unauth so a 401 returns a sentinel instead of exiting the whole
|
|
271
|
+
// process via cli-lib's guidance path (see the loop's degrade-to-null below).
|
|
272
|
+
api = await cliClient({ allowUnauthenticated: true });
|
|
273
|
+
}
|
|
257
274
|
// 03's pieces + 04-architecture's memory chain + its dev-box/ship-flow pieces
|
|
258
275
|
// share one fetch + one taskStatus map; any unreachable id degrades the WHOLE
|
|
259
276
|
// conceptual bundle (so 03 + 04 skip together rather than emit partial data).
|
|
260
277
|
const ALL_TASK_IDS = { ...CONCEPTUAL_TASK_IDS, ...DEV_BOX_TASK_IDS };
|
|
261
278
|
const ids = Object.keys(ALL_TASK_IDS).map(Number);
|
|
262
279
|
const status = {};
|
|
280
|
+
let lockedOffset = null;
|
|
263
281
|
for (const id of ids) {
|
|
264
282
|
let s = null;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
283
|
+
const offsets = lockedOffset === null ? IMPORT_ID_OFFSETS : [lockedOffset];
|
|
284
|
+
for (const off of offsets) {
|
|
285
|
+
try {
|
|
286
|
+
const r = await api.tasks.getTasksId({ id: id + off });
|
|
287
|
+
if (r.ok) {
|
|
288
|
+
// A 200 means this id exists at this offset — accept it and stop
|
|
289
|
+
// probing (a missing status field still degrades below).
|
|
290
|
+
s = (r.data.task || r.data).status || null;
|
|
291
|
+
if (s !== null) lockedOffset = off;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
} catch (_) {
|
|
295
|
+
s = null;
|
|
296
|
+
}
|
|
270
297
|
}
|
|
271
298
|
if (s === null) {
|
|
272
299
|
// Any unreachable id degrades the whole conceptual bundle — partial data
|
|
@@ -315,5 +342,6 @@ module.exports = {
|
|
|
315
342
|
conceptualFacts,
|
|
316
343
|
CONCEPTUAL_TASK_IDS,
|
|
317
344
|
DEV_BOX_TASK_IDS,
|
|
345
|
+
IMPORT_ID_OFFSETS,
|
|
318
346
|
gatherFacts,
|
|
319
347
|
};
|
package/src/module-api.js
CHANGED
|
@@ -515,8 +515,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
515
515
|
// landed since 1.19.42 with no explicit bump. run 31517196176. (task 1002620)
|
|
516
516
|
// 1.19.44 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
517
517
|
// landed since 1.19.43 with no explicit bump. run 31518312009. (task 1002620)
|
|
518
|
+
// 1.19.45 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
519
|
+
// landed since 1.19.44 with no explicit bump. run 31519500533. (task 1002620)
|
|
518
520
|
// ---------------------------------------------------------------------------
|
|
519
|
-
const CORE_VERSION = '1.19.
|
|
521
|
+
const CORE_VERSION = '1.19.45'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
520
522
|
|
|
521
523
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
522
524
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// tests/diagram_facts_offset.mjs — conceptualFacts import-offset probe (task 1002667).
|
|
2
|
+
//
|
|
3
|
+
// The conceptual onboarding diagrams (03/04) gate each piece on an ORIGIN
|
|
4
|
+
// task id. An instance that imported the origin backlog renumbers those tasks
|
|
5
|
+
// by a uniform offset (+1000000 on the reference import), so the ids must
|
|
6
|
+
// resolve through the IMPORT_ID_OFFSETS probe — keyed BACK to the origin ids —
|
|
7
|
+
// or every conceptual regen silently degrades to null and 03/04 never update.
|
|
8
|
+
//
|
|
9
|
+
// Run: node --test tests/diagram_facts_offset.mjs
|
|
10
|
+
|
|
11
|
+
import { strict as assert } from 'node:assert';
|
|
12
|
+
import { test } from 'node:test';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const REPO = path.join(__dirname, '..');
|
|
20
|
+
const facts = require(path.join(REPO, 'scripts/gds/diagram-facts.js'));
|
|
21
|
+
|
|
22
|
+
const ORIGIN_IDS = Object.keys({
|
|
23
|
+
...facts.CONCEPTUAL_TASK_IDS,
|
|
24
|
+
...facts.DEV_BOX_TASK_IDS,
|
|
25
|
+
}).map(Number);
|
|
26
|
+
|
|
27
|
+
// Mock API: resolves any id present in `rows` ({id → status}); 404s the rest.
|
|
28
|
+
const mockApi = (rows) => ({
|
|
29
|
+
tasks: {
|
|
30
|
+
getTasksId: async ({ id }) => {
|
|
31
|
+
if (id in rows) return { ok: true, data: { task: { id: String(id), status: rows[id] } } };
|
|
32
|
+
return { ok: false, status: 404, data: { error: 'not found' } };
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('origin instance: ids resolve directly, map keyed by origin ids', async () => {
|
|
38
|
+
const rows = Object.fromEntries(ORIGIN_IDS.map((id) => [id, 'shipped']));
|
|
39
|
+
const c = await facts.conceptualFacts({ repo: REPO, migs: [], api: mockApi(rows) });
|
|
40
|
+
assert.ok(c, 'bundle resolved, not degraded');
|
|
41
|
+
for (const id of ORIGIN_IDS) assert.equal(c.taskStatus[id], 'shipped');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('imported instance (+1000000): ids resolve at the offset, map still keyed by origin ids', async () => {
|
|
45
|
+
const rows = Object.fromEntries(ORIGIN_IDS.map((id) => [id + 1_000_000, 'shipped']));
|
|
46
|
+
const c = await facts.conceptualFacts({ repo: REPO, migs: [], api: mockApi(rows) });
|
|
47
|
+
assert.ok(c, 'bundle resolved through the offset probe');
|
|
48
|
+
for (const id of ORIGIN_IDS) {
|
|
49
|
+
assert.equal(c.taskStatus[id], 'shipped', `origin id ${id} keyed in the returned map`);
|
|
50
|
+
assert.ok(!(id + 1_000_000 in c.taskStatus), 'no offset-id keys leak into the map');
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('offset locks on first hit: a coincidental origin-id row cannot blend task sets', async () => {
|
|
55
|
+
// Everything lives at +1000000, but one LATER id also exists bare with a
|
|
56
|
+
// conflicting status. Once the first id locks the offset, the bare row must
|
|
57
|
+
// be ignored — the +1000000 row's status wins.
|
|
58
|
+
const rows = Object.fromEntries(ORIGIN_IDS.map((id) => [id + 1_000_000, 'shipped']));
|
|
59
|
+
const later = ORIGIN_IDS[ORIGIN_IDS.length - 1];
|
|
60
|
+
rows[later] = 'backlog';
|
|
61
|
+
const c = await facts.conceptualFacts({ repo: REPO, migs: [], api: mockApi(rows) });
|
|
62
|
+
assert.ok(c, 'bundle resolved');
|
|
63
|
+
assert.equal(c.taskStatus[later], 'shipped', 'locked offset wins over the coincidental bare id');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('unresolvable id degrades the whole bundle to null (unchanged sentinel contract)', async () => {
|
|
67
|
+
const rows = Object.fromEntries(ORIGIN_IDS.map((id) => [id + 1_000_000, 'shipped']));
|
|
68
|
+
delete rows[ORIGIN_IDS[0] + 1_000_000];
|
|
69
|
+
const c = await facts.conceptualFacts({ repo: REPO, migs: [], api: mockApi(rows) });
|
|
70
|
+
assert.equal(c, null);
|
|
71
|
+
});
|