@bongos/core 1.24.65 → 1.24.67
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 +27 -27
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/ui-look-before-you-ship.md +1 -0
- package/modules/government/protected-surfaces.json +26 -0
- package/modules/grading/grader-no-artifact.js +28 -1
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/lib.js +21 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +40 -0
- package/scripts/bongos/cli-lib.js +69 -6
- package/scripts/bongos/ship-grade-steps.js +3 -1
- package/src/bongos/prelaunch-gate.js +34 -18
- package/src/module-api.js +1 -1
- package/tests/cli_sessions.mjs +82 -0
- package/tests/effective_visibility_predicate.mjs +135 -6
- package/tests/government_protected_surfaces.mjs +104 -0
- package/tests/grade_unavailable_display.mjs +47 -0
- package/tests/prelaunch_gate.mjs +22 -0
- package/tests/recall_hints.mjs +45 -0
- package/tests/session_start_freshness.mjs +97 -0
- package/tests/ui_design_kit.mjs +4 -1
- package/tests/ui_design_kit_fit.mjs +30 -0
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/bongos/package-core.js",
|
|
5
|
-
"core_version": "1.24.
|
|
6
|
-
"core_contract": "1.24.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.24.67",
|
|
6
|
+
"core_contract": "1.24.67",
|
|
7
|
+
"source_commit": "cb3a73a80b0216905417a6379f4caf4f66a159c2",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-11-
|
|
9
|
+
"built_at": "2026-11-09T20:07:37.283Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 638,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 4575,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "ab4503425d89d4eec00873f68e4ed95088cb713b9816a7a0f3cc218da4f734d1",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -3007,7 +3007,7 @@
|
|
|
3007
3007
|
{
|
|
3008
3008
|
"path": "docs/module-api-changelog.md",
|
|
3009
3009
|
"mode": "0000644",
|
|
3010
|
-
"sha256": "
|
|
3010
|
+
"sha256": "b2ea544c7a4c86e2c83d079531d105a0f8ade34933bfe96929c5bd8256dcea5c"
|
|
3011
3011
|
},
|
|
3012
3012
|
{
|
|
3013
3013
|
"path": "docs/modules-contract.md",
|
|
@@ -3262,7 +3262,7 @@
|
|
|
3262
3262
|
{
|
|
3263
3263
|
"path": "docs/recipes/ui-look-before-you-ship.md",
|
|
3264
3264
|
"mode": "0000644",
|
|
3265
|
-
"sha256": "
|
|
3265
|
+
"sha256": "691bbbf06c9622a91aa8b4f5d40a4be2e7198e9219a6a689bb7dd1ecb1430f94"
|
|
3266
3266
|
},
|
|
3267
3267
|
{
|
|
3268
3268
|
"path": "docs/recipes/upgrading-the-core.md",
|
|
@@ -5757,7 +5757,7 @@
|
|
|
5757
5757
|
{
|
|
5758
5758
|
"path": "modules/government/protected-surfaces.json",
|
|
5759
5759
|
"mode": "0000644",
|
|
5760
|
-
"sha256": "
|
|
5760
|
+
"sha256": "0b54c64b1515a2208a8094e80a891e99e374a99bc26e99f70c81d592ef125cbc"
|
|
5761
5761
|
},
|
|
5762
5762
|
{
|
|
5763
5763
|
"path": "modules/government/ranks.js",
|
|
@@ -5852,7 +5852,7 @@
|
|
|
5852
5852
|
{
|
|
5853
5853
|
"path": "modules/grading/grader-no-artifact.js",
|
|
5854
5854
|
"mode": "0000644",
|
|
5855
|
-
"sha256": "
|
|
5855
|
+
"sha256": "21009dcce99c9a18abf5f7e40f68792ff42584eb2af5d0008df7f291487c62b1"
|
|
5856
5856
|
},
|
|
5857
5857
|
{
|
|
5858
5858
|
"path": "modules/grading/grader-prompt.js",
|
|
@@ -11357,7 +11357,7 @@
|
|
|
11357
11357
|
{
|
|
11358
11358
|
"path": "modules/ui-design/kit/README.md",
|
|
11359
11359
|
"mode": "0000644",
|
|
11360
|
-
"sha256": "
|
|
11360
|
+
"sha256": "a95397538d363364347d76a875663ffc9b36eb1cba03475f11bc175e2e2787fc"
|
|
11361
11361
|
},
|
|
11362
11362
|
{
|
|
11363
11363
|
"path": "modules/ui-design/kit/check-mock.js",
|
|
@@ -11667,7 +11667,7 @@
|
|
|
11667
11667
|
{
|
|
11668
11668
|
"path": "modules/ui-design/kit/lib.js",
|
|
11669
11669
|
"mode": "0000644",
|
|
11670
|
-
"sha256": "
|
|
11670
|
+
"sha256": "e593169a194cd60838193df799dfb3b19ac72e92e2e6beadb3cd28128305abad"
|
|
11671
11671
|
},
|
|
11672
11672
|
{
|
|
11673
11673
|
"path": "modules/ui-design/kit/png.js",
|
|
@@ -11902,12 +11902,12 @@
|
|
|
11902
11902
|
{
|
|
11903
11903
|
"path": "package-lock.json",
|
|
11904
11904
|
"mode": "0000644",
|
|
11905
|
-
"sha256": "
|
|
11905
|
+
"sha256": "02030268a28d3b76dc99198d41bf1de043460b72e8dedde3caa400b65c5ea4bf"
|
|
11906
11906
|
},
|
|
11907
11907
|
{
|
|
11908
11908
|
"path": "package.json",
|
|
11909
11909
|
"mode": "0000644",
|
|
11910
|
-
"sha256": "
|
|
11910
|
+
"sha256": "f18b77185a5d2b924bf459cc5415880fea61d1118f9a358f0691b8d703565120"
|
|
11911
11911
|
},
|
|
11912
11912
|
{
|
|
11913
11913
|
"path": "public-docs/index.html",
|
|
@@ -11927,7 +11927,7 @@
|
|
|
11927
11927
|
{
|
|
11928
11928
|
"path": "release-notes.json",
|
|
11929
11929
|
"mode": "0000644",
|
|
11930
|
-
"sha256": "
|
|
11930
|
+
"sha256": "576be7645a892c65c51e1ae3c8a17d7a5ecb2a2505cf9467dfff7a26b7aa5679"
|
|
11931
11931
|
},
|
|
11932
11932
|
{
|
|
11933
11933
|
"path": "scripts/bongos/CLAUDE.md",
|
|
@@ -12197,7 +12197,7 @@
|
|
|
12197
12197
|
{
|
|
12198
12198
|
"path": "scripts/bongos/cli-lib.js",
|
|
12199
12199
|
"mode": "0000644",
|
|
12200
|
-
"sha256": "
|
|
12200
|
+
"sha256": "bbfd061637439ce86bd261fd39e243f1701247ea6669c7ae4775d1e59c94a3a9"
|
|
12201
12201
|
},
|
|
12202
12202
|
{
|
|
12203
12203
|
"path": "scripts/bongos/client-baseurl-guard.js",
|
|
@@ -13317,7 +13317,7 @@
|
|
|
13317
13317
|
{
|
|
13318
13318
|
"path": "scripts/bongos/ship-grade-steps.js",
|
|
13319
13319
|
"mode": "0000644",
|
|
13320
|
-
"sha256": "
|
|
13320
|
+
"sha256": "83a0e5ce61257c8435a58cab07accf749570f8fa51db1b9c72aaff846ac10c02"
|
|
13321
13321
|
},
|
|
13322
13322
|
{
|
|
13323
13323
|
"path": "scripts/bongos/ship-grade.js",
|
|
@@ -15612,7 +15612,7 @@
|
|
|
15612
15612
|
{
|
|
15613
15613
|
"path": "src/bongos/prelaunch-gate.js",
|
|
15614
15614
|
"mode": "0000644",
|
|
15615
|
-
"sha256": "
|
|
15615
|
+
"sha256": "a780f3d0d8b48c4b3c6e9a3fad4296cc31e1f006c5a0e7a0b62c51be793db186"
|
|
15616
15616
|
},
|
|
15617
15617
|
{
|
|
15618
15618
|
"path": "src/bongos/prelaunch-switch.js",
|
|
@@ -15877,7 +15877,7 @@
|
|
|
15877
15877
|
{
|
|
15878
15878
|
"path": "src/module-api.js",
|
|
15879
15879
|
"mode": "0000644",
|
|
15880
|
-
"sha256": "
|
|
15880
|
+
"sha256": "ab5adbd2f126b45470953b8c28545124554f69e04a1ba70494471d888288d509"
|
|
15881
15881
|
},
|
|
15882
15882
|
{
|
|
15883
15883
|
"path": "src/module-loader/catalog.js",
|
|
@@ -16902,7 +16902,7 @@
|
|
|
16902
16902
|
{
|
|
16903
16903
|
"path": "tests/cli_sessions.mjs",
|
|
16904
16904
|
"mode": "0000644",
|
|
16905
|
-
"sha256": "
|
|
16905
|
+
"sha256": "55d72cc2926ab8574e290ca16e40702759c8c6b0453a8ebd4aa9f09ae77f880d"
|
|
16906
16906
|
},
|
|
16907
16907
|
{
|
|
16908
16908
|
"path": "tests/cli_surface.mjs",
|
|
@@ -17602,7 +17602,7 @@
|
|
|
17602
17602
|
{
|
|
17603
17603
|
"path": "tests/effective_visibility_predicate.mjs",
|
|
17604
17604
|
"mode": "0000644",
|
|
17605
|
-
"sha256": "
|
|
17605
|
+
"sha256": "4a7bb44f07a87997830e44636490a4992d862d43b2620a2d583370869f6b477d"
|
|
17606
17606
|
},
|
|
17607
17607
|
{
|
|
17608
17608
|
"path": "tests/env_knobs_neutral_prefix.mjs",
|
|
@@ -18437,7 +18437,7 @@
|
|
|
18437
18437
|
{
|
|
18438
18438
|
"path": "tests/government_protected_surfaces.mjs",
|
|
18439
18439
|
"mode": "0000644",
|
|
18440
|
-
"sha256": "
|
|
18440
|
+
"sha256": "de07f6405cc18311d0e048efcbc450bb15b270387a9579b53811ae4ac506c7e0"
|
|
18441
18441
|
},
|
|
18442
18442
|
{
|
|
18443
18443
|
"path": "tests/government_rank_sync.mjs",
|
|
@@ -18572,7 +18572,7 @@
|
|
|
18572
18572
|
{
|
|
18573
18573
|
"path": "tests/grade_unavailable_display.mjs",
|
|
18574
18574
|
"mode": "0000644",
|
|
18575
|
-
"sha256": "
|
|
18575
|
+
"sha256": "63135247609c11501f0d7af190343193a5dab334f7186cd05ab02fdbf1ea3644"
|
|
18576
18576
|
},
|
|
18577
18577
|
{
|
|
18578
18578
|
"path": "tests/grader.mjs",
|
|
@@ -20697,7 +20697,7 @@
|
|
|
20697
20697
|
{
|
|
20698
20698
|
"path": "tests/prelaunch_gate.mjs",
|
|
20699
20699
|
"mode": "0000644",
|
|
20700
|
-
"sha256": "
|
|
20700
|
+
"sha256": "1eea91518ffdf65206d9b0a0094269a426268b0da4abb578a994150f7632ffd7"
|
|
20701
20701
|
},
|
|
20702
20702
|
{
|
|
20703
20703
|
"path": "tests/preview_server.mjs",
|
|
@@ -21357,7 +21357,7 @@
|
|
|
21357
21357
|
{
|
|
21358
21358
|
"path": "tests/recall_hints.mjs",
|
|
21359
21359
|
"mode": "0000644",
|
|
21360
|
-
"sha256": "
|
|
21360
|
+
"sha256": "35af628eeda697d97bfaaba5052fc0f4928ae174ef04ec3afc7d3470dfc62dfe"
|
|
21361
21361
|
},
|
|
21362
21362
|
{
|
|
21363
21363
|
"path": "tests/recent_shipped_window.mjs",
|
|
@@ -21787,7 +21787,7 @@
|
|
|
21787
21787
|
{
|
|
21788
21788
|
"path": "tests/session_start_freshness.mjs",
|
|
21789
21789
|
"mode": "0000644",
|
|
21790
|
-
"sha256": "
|
|
21790
|
+
"sha256": "acdee57f83af053e86d16ce9764647fe750ca33c7f24313a6e60b6ba9180dc49"
|
|
21791
21791
|
},
|
|
21792
21792
|
{
|
|
21793
21793
|
"path": "tests/session_token_hash.mjs",
|
|
@@ -22567,12 +22567,12 @@
|
|
|
22567
22567
|
{
|
|
22568
22568
|
"path": "tests/ui_design_kit.mjs",
|
|
22569
22569
|
"mode": "0000644",
|
|
22570
|
-
"sha256": "
|
|
22570
|
+
"sha256": "f5160521be5a2edecfe8997275cbbc65b8cf6ac8aac7cca2e90e23378d4e64f3"
|
|
22571
22571
|
},
|
|
22572
22572
|
{
|
|
22573
22573
|
"path": "tests/ui_design_kit_fit.mjs",
|
|
22574
22574
|
"mode": "0000644",
|
|
22575
|
-
"sha256": "
|
|
22575
|
+
"sha256": "010a7b1427aa28d4807093f2ff31871058b366c46daafb1818cda2eee4f404cc"
|
|
22576
22576
|
},
|
|
22577
22577
|
{
|
|
22578
22578
|
"path": "tests/ui_design_skills.mjs",
|
|
@@ -3384,5 +3384,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3384
3384
|
landed since 1.24.63 with no explicit bump. run 42477114353. (task 1002620)
|
|
3385
3385
|
1.24.65 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3386
3386
|
landed since 1.24.64 with no explicit bump. run 42516347052. (task 1002620)
|
|
3387
|
+
1.24.66 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3388
|
+
landed since 1.24.65 with no explicit bump. run 42686885661. (task 1002620)
|
|
3389
|
+
1.24.67 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3390
|
+
landed since 1.24.66 with no explicit bump. run 42689728958. (task 1002620)
|
|
3387
3391
|
---------------------------------------------------------------------------
|
|
3388
3392
|
```
|
|
@@ -48,6 +48,7 @@ node modules/ui-design/kit/render.js --page / --style expedition # the sa
|
|
|
48
48
|
| `horizontal-overflow` | `scrollWidth` wider than the viewport |
|
|
49
49
|
| `text-fit` | a word of text more than 1px past its box (the padding box of its nearest non-inline ancestor), or an SVG `<text>` past the smallest `<rect>` in its own `<g>` that holds its start — a scroller, `text-overflow: ellipsis` and a visually-hidden label are skipped ([ADR 0329](../adr/0329-the-rendered-fit-check-is-a-kit-floor-and-an-advisory-ship-check-row.md)) |
|
|
50
50
|
| `sibling-overlap` | two in-flow, block-level children of one parent that both carry text overlapping by more than 2px each way — absolute, fixed, sticky and negative-margin layering is deliberate and skipped |
|
|
51
|
+
| `empty-graphic` | a shown `<use>` that paints nothing (its referenced SVG failed to parse, or the id is missing) or an `<img>` that did not decode or renders 0×0 — the logo that silently vanishes; a sprite sheet's own `<symbol>`/`<defs>`, a hidden graphic and a lazy image not yet loaded are skipped (task 1005104) |
|
|
51
52
|
| `one-header-main-h1` | not exactly one *rendered* `<header>`, `<main>` and `<h1>` |
|
|
52
53
|
| `controls-24px` | a visible control under 24px on either side (`ignoreControls` for the skip link and the like) |
|
|
53
54
|
| `top-bar-80px` | a `<header>` taller than 80px |
|
|
@@ -39,6 +39,22 @@
|
|
|
39
39
|
{ "glob": "src/bongos/middleware/", "group": "authz", "why": "audit + any auth middleware" },
|
|
40
40
|
{ "glob": "src/bongos/routes/", "group": "authz", "why": "routes carry the requireRank gates" },
|
|
41
41
|
{ "glob": "modules/lifecycle/routes/", "group": "authz", "why": "BV1.R86: the lifecycle routes carved out of src/bongos/routes/ — the gates follow the routes" },
|
|
42
|
+
{ "glob": "modules/lifecycle/db.js", "group": "authz", "why": "task 1004382. The lifecycle data-layer facade (BV1.R86): claimTask and the claim→ship→grade→credit state machine run through it. Its own header says it carries a duplicate of src/bongos/db.js's in-tx rank change, and src/bongos/db.js is protected for exactly that." },
|
|
43
|
+
{ "glob": "modules/lifecycle/db-analytics.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
44
|
+
{ "glob": "modules/lifecycle/db-claim-reads.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
45
|
+
{ "glob": "modules/lifecycle/db-claims.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
46
|
+
{ "glob": "modules/lifecycle/db-deps-criteria.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
47
|
+
{ "glob": "modules/lifecycle/db-executor.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
48
|
+
{ "glob": "modules/lifecycle/db-goals.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
49
|
+
{ "glob": "modules/lifecycle/db-grade.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
50
|
+
{ "glob": "modules/lifecycle/db-overrides.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
51
|
+
{ "glob": "modules/lifecycle/db-rank-authz.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204): claimPermissionsFor, claimGate, deriveRequiredRank, singleRungPromotionError and _applyRankChangeTx — the claim gate and the rank-change rules. The authz-inventory tripwire in tests/government_protected_surfaces.mjs reds if one of them moves into an unprotected file." },
|
|
52
|
+
{ "glob": "modules/lifecycle/db-relevance-flags.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
53
|
+
{ "glob": "modules/lifecycle/db-shared.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
54
|
+
{ "glob": "modules/lifecycle/db-ship.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
55
|
+
{ "glob": "modules/lifecycle/db-tasks.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
56
|
+
{ "glob": "modules/lifecycle/db-versions.js", "group": "authz", "why": "task 1004382. Carved out of lifecycle/db.js (task 1003204), which registering the facade alone would have left unprotected — the 1004024 carve rule: a protected file's <name>-*.js siblings are protected too." },
|
|
57
|
+
{ "glob": "modules/lifecycle/claim-eligibility.js", "group": "authz", "why": "task 1004382. The QUEUE axis of the claim gate (ADR 0151, BV1.R106): permissionAllowsClaim and claimEligibilityAllows decide which tasks a builder's permissions let them claim at all." },
|
|
42
58
|
{ "glob": "src/bongos/routes.js", "group": "authz", "why": "the route MOUNT table (a new ungated route lands here)" },
|
|
43
59
|
{ "glob": "server.js", "group": "authz", "why": "the Express entrypoint — wires every router + host routing" },
|
|
44
60
|
{ "glob": "modules/government/", "group": "authz", "why": "the same authz core under its post-ADR-0174 name (goal 1000068, task 1003003). Listed BEFORE the folder moves, deliberately: `protected` is DERIVED from this registry, so a module key whose globs are absent here computes protected:false — which would silently drop the Archon gate on goal creation AND the pre-push block on these files for the whole window between the key landing and the folder arriving. The prospective-carve precedent is the modules/government/ entry directly above (ADR 0086 §4)." },
|
|
@@ -88,6 +104,16 @@
|
|
|
88
104
|
{ "glob": "scripts/bongos/main-audit.js", "group": "pipeline", "why": "the post-push audit — the authoritative substrate layer (self-protect)" },
|
|
89
105
|
{ "glob": "scripts/bongos/push-main.js", "group": "pipeline", "why": "the sanctioned main-push (carries the override)" },
|
|
90
106
|
{ "glob": "scripts/bongos/run-unit-tests.js", "group": "pipeline", "why": "the unit-gate test SELECTOR — muting tests here blunts the CI gate (ADR 0058)" },
|
|
107
|
+
{ "glob": "scripts/bongos/fitness.js", "group": "pipeline", "why": "task 1004383. The architecture fitness gate CI runs on every merge, including the module-migration DDL check task 1003373 extends. scripts/gds/fitness.js is only a forwarder to it (already under scripts/gds/); the real gate sat unprotected." },
|
|
108
|
+
{ "glob": "scripts/bongos/fitness-checks-error-envelope.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
109
|
+
{ "glob": "scripts/bongos/fitness-checks-identity.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
110
|
+
{ "glob": "scripts/bongos/fitness-checks-packaging.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
111
|
+
{ "glob": "scripts/bongos/fitness-checks-route-pins.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
112
|
+
{ "glob": "scripts/bongos/fitness-checks-vocabulary.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
113
|
+
{ "glob": "scripts/bongos/fitness-checks-write-validation.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
114
|
+
{ "glob": "scripts/bongos/fitness-lib.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
115
|
+
{ "glob": "scripts/bongos/fitness-migration-contraction.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
116
|
+
{ "glob": "scripts/bongos/fitness-ratchets.js", "group": "pipeline", "why": "task 1004383. A check file of the fitness CI gate, split out beside fitness.js; protected with it under the 1004024 carve rule, since muting a check here blunts the gate." },
|
|
91
117
|
{ "glob": "scripts/bongos/hard-floor.js", "group": "pipeline", "why": "task 1004859. The trust-boundary hard-floor list (self-protect). It moved here from scripts/bongos/gate-review.js when that never-run CI classifier was removed; publish-manifest.js and adopt-preflight.js read it." },
|
|
92
118
|
{ "glob": "scripts/bongos/setup.js", "group": "pipeline", "why": "/builder-setup — installs the pre-push hook" },
|
|
93
119
|
{ "glob": "scripts/bongos/install-git-hooks.js", "group": "pipeline", "why": "the hook installer (self-protect)" },
|
|
@@ -133,6 +133,8 @@ function aggregateShots(shotResults, meta = {}) {
|
|
|
133
133
|
shot_index: s.signals?.shot_index ?? null,
|
|
134
134
|
notes_md: s.notes_md,
|
|
135
135
|
issues: s.issues || [],
|
|
136
|
+
exit_code: s.signals?.exit_code ?? null,
|
|
137
|
+
stderr_tail: s.signals?.stderr_tail || null,
|
|
136
138
|
error:
|
|
137
139
|
s.signals?.parse_error
|
|
138
140
|
? 'parse_error'
|
|
@@ -186,10 +188,26 @@ function aggregateShots(shotResults, meta = {}) {
|
|
|
186
188
|
const errorCount = verdicts.filter((v) => v.error).length;
|
|
187
189
|
const allPassed = N > 0 && passCount === N;
|
|
188
190
|
|
|
191
|
+
// task 1005101: EVERY shot errored → nothing was judged. This is the
|
|
192
|
+
// single-shot twin of the panel's #943 total outage (aggregatePanel), and it
|
|
193
|
+
// carries the same `panel_outcome: 'unavailable'` tag so ship.js prints the
|
|
194
|
+
// outage card instead of "the reviewers found problems in your work" — which
|
|
195
|
+
// is what a spend-limit or expired-sign-in exit printed before. passed stays
|
|
196
|
+
// false: an outage parks at completed for a re-grade or manual-confirm.
|
|
197
|
+
const allErrored = N > 0 && errorCount === N;
|
|
198
|
+
|
|
189
199
|
let verdict;
|
|
190
200
|
let passed;
|
|
191
201
|
let aggregationKind;
|
|
192
|
-
if (
|
|
202
|
+
if (allErrored) {
|
|
203
|
+
passed = false;
|
|
204
|
+
const codes = [...new Set(verdicts.map((v) => v.exit_code).filter((c) => c != null))];
|
|
205
|
+
const tail = verdicts.map((v) => v.stderr_tail).find(Boolean);
|
|
206
|
+
const cause = [codes.length ? `exit ${codes.join('/')}` : null, tail ? `subprocess said: ${tail.trim().split('\n')[0]}` : null]
|
|
207
|
+
.filter(Boolean).join('; ');
|
|
208
|
+
verdict = `GRADER UNAVAILABLE — all ${N}/${N} shot(s) failed to run; no quality signal was produced. This is an infra/outage condition, NOT a quality rejection — manual-confirm available.${cause ? ` Diagnostic — ${cause}` : ''}`;
|
|
209
|
+
aggregationKind = 'unavailable';
|
|
210
|
+
} else if (errorCount > 0) {
|
|
193
211
|
passed = false;
|
|
194
212
|
verdict = `FAIL — ${errorCount}/${N} shot(s) errored`;
|
|
195
213
|
aggregationKind = 'errored';
|
|
@@ -272,6 +290,15 @@ function aggregateShots(shotResults, meta = {}) {
|
|
|
272
290
|
fail_count: failCount,
|
|
273
291
|
error_count: errorCount,
|
|
274
292
|
shot_aggregation: aggregationKind,
|
|
293
|
+
// task 1005101: the outage tag + the per-shot exit codes ship.js's outage
|
|
294
|
+
// card prints. Absent on any judged grade, so those keep their exact shape.
|
|
295
|
+
...(allErrored ? {
|
|
296
|
+
panel_outcome: 'unavailable',
|
|
297
|
+
grader_unavailable_reason: 'all_shots_errored',
|
|
298
|
+
outage_shots: verdicts.map((v, i) => ({
|
|
299
|
+
kind: `shot ${v.shot_index ?? i}`, error: v.error, exit_code: v.exit_code, stderr_tail: v.stderr_tail,
|
|
300
|
+
})),
|
|
301
|
+
} : {}),
|
|
275
302
|
...meta,
|
|
276
303
|
},
|
|
277
304
|
notes_md: lines.join('\n'),
|
|
@@ -12,7 +12,7 @@ node modules/ui-design/kit/contrast.js --pack [look] | --url <url> [--glyphs sel
|
|
|
12
12
|
|
|
13
13
|
A **look** is a branding pack from the module's style library (`modules/ui-design/styles/<name>/`, task 1003323): `STYLE=<name>` lays it over the instance's own pack through `GDS_BRANDING_FILE`, so any surface renders under any look with no page edit.
|
|
14
14
|
|
|
15
|
-
Two of the floors are about fit ([task 1004203](https://cloudbongos.com/builders#/task/1004203), [ADR 0329](../../../docs/adr/0329-the-rendered-fit-check-is-a-kit-floor-and-an-advisory-ship-check-row.md)): `text-fit` (text past its box, SVG text past its card) and `sibling-overlap` (text-bearing siblings on top of each other). `scripts/bongos/render-check.js` runs just those two over the pages a branch changed, and `/ship-check` shows them as an advisory row.
|
|
15
|
+
Two of the floors are about fit ([task 1004203](https://cloudbongos.com/builders#/task/1004203), [ADR 0329](../../../docs/adr/0329-the-rendered-fit-check-is-a-kit-floor-and-an-advisory-ship-check-row.md)): `text-fit` (text past its box, SVG text past its card) and `sibling-overlap` (text-bearing siblings on top of each other). `scripts/bongos/render-check.js` runs just those two over the pages a branch changed, and `/ship-check` shows them as an advisory row. A third in-page floor, `empty-graphic` ([task 1005104](https://cloudbongos.com/builders#/task/1005104)), reports a shown `<use>` or `<img>` that paints nothing — a referenced SVG that failed to parse leaves no console error and no failed request, so the logo vanished under ALL CLEAN.
|
|
16
16
|
|
|
17
17
|
A surface describes itself in two JSON files beside it — `<page>.states.json` and `<page>.probes.json` — in the vocabularies `lib.js` defines and validates. A page served at the surface root (the hall's Home, `/builders/`) reads `index.states.json`. A file (or one state) may carry `"storage": { "session": {…}, "local": {…} }`, written into the browser before the page's own scripts run, so a first-visit question renders as a returning visitor sees it: every hall file seeds `hall-craft-asked` so the mode picker stays out of the shots ([task 1005080](https://cloudbongos.com/builders#/task/1005080)). A hall state that needs real data (a goal's detail, the Settings sections) passes against the hall-preview harness with `--base-in`/`--base-out`, one render at a time. The full loop, the file shapes and the failure table: [`docs/recipes/ui-look-before-you-ship.md`](../../../docs/recipes/ui-look-before-you-ship.md). Exit codes everywhere: 0 clean · 1 problems · 2 a broken file or usage · 3 no browser.
|
|
18
18
|
|
|
@@ -28,6 +28,7 @@ const FLOORS = Object.freeze([
|
|
|
28
28
|
'horizontal-overflow', // documentElement.scrollWidth fits the viewport
|
|
29
29
|
'text-fit', // no text runs past its box (HTML) or its card rect (SVG)
|
|
30
30
|
'sibling-overlap', // no two in-flow, block-level, text-bearing siblings overlap
|
|
31
|
+
'empty-graphic', // no shown <use> or <img> that paints nothing (a referenced SVG that failed to parse)
|
|
31
32
|
'one-header-main-h1', // exactly one <header>, one <main>, one <h1>
|
|
32
33
|
'controls-24px', // every visible control clears 24px on both sides
|
|
33
34
|
'top-bar-80px', // the page's <header> is at most 80px tall
|
|
@@ -287,6 +288,25 @@ function factsInPage(spec) {
|
|
|
287
288
|
}
|
|
288
289
|
});
|
|
289
290
|
out.textFit = textFit; out.overlaps = overlaps;
|
|
291
|
+
// The empty-graphic floor (task 1005104): a referenced SVG that fails to parse leaves its <use> empty, or its
|
|
292
|
+
// <img> with no natural size, and the logo vanishes with no console error and no failed request. A sprite's own
|
|
293
|
+
// <symbol>/<defs>, a 0x0 sprite sheet, a hidden graphic and a lazy image not yet loaded are not judged.
|
|
294
|
+
const ref = (el) => quote(String(el.getAttribute('href') || el.getAttribute('xlink:href') || el.getAttribute('src') || '').replace(/^data:([^,;]{0,24})[^]*?(#[\w-]*)?$/, 'data:$1…$2'));
|
|
295
|
+
const emptyGraphics = [];
|
|
296
|
+
qa('svg use').forEach((u) => {
|
|
297
|
+
if (u.closest('symbol, defs, mask, clipPath, pattern, marker') || !vis(u)) return;
|
|
298
|
+
const svg = u.ownerSVGElement; if (!svg) return;
|
|
299
|
+
const s = svg.getBoundingClientRect(); if (s.width < 1 || s.height < 1) return;
|
|
300
|
+
const r = u.getBoundingClientRect();
|
|
301
|
+
if (r.width < 1 || r.height < 1) emptyGraphics.push(`${where(u)} → "${ref(u)}" paints nothing`);
|
|
302
|
+
});
|
|
303
|
+
qa('img').forEach((im) => {
|
|
304
|
+
if (!vis(im) || (!im.complete && im.loading === 'lazy')) return;
|
|
305
|
+
const r = im.getBoundingClientRect();
|
|
306
|
+
if (im.complete && !im.naturalWidth) emptyGraphics.push(`${where(im)} → "${ref(im)}" did not decode`);
|
|
307
|
+
else if (r.width < 1 || r.height < 1) emptyGraphics.push(`${where(im)} → "${ref(im)}" renders ${Math.round(r.width)}x${Math.round(r.height)}`);
|
|
308
|
+
});
|
|
309
|
+
out.emptyGraphics = emptyGraphics;
|
|
290
310
|
return out;
|
|
291
311
|
}
|
|
292
312
|
// Pure: compare collected facts against an expectation. Returns problem strings.
|
|
@@ -324,6 +344,7 @@ function auditFloors(facts, { width, mode } = {}) {
|
|
|
324
344
|
const some = (list) => list.slice(0, 5).join(' | ') + (list.length > 5 ? ` (+${list.length - 5} more)` : '');
|
|
325
345
|
if (facts.textFit && facts.textFit.length) p.push(`text-fit: ${some(facts.textFit)}`);
|
|
326
346
|
if (facts.overlaps && facts.overlaps.length) p.push(`sibling-overlap: ${some(facts.overlaps)}`);
|
|
347
|
+
if (facts.emptyGraphics && facts.emptyGraphics.length) p.push(`empty-graphic: ${some(facts.emptyGraphics)}`);
|
|
327
348
|
if (facts.headers !== 1 || facts.mains !== 1 || facts.h1s !== 1) p.push(`one-header-main-h1: header ${facts.headers} main ${facts.mains} h1 ${facts.h1s}`);
|
|
328
349
|
if (facts.smallControls && facts.smallControls.length) p.push(`controls-24px: ${facts.smallControls.join(' | ')}`);
|
|
329
350
|
if (facts.bar && facts.bar.h > 80) p.push(`top-bar-80px: header is ${facts.bar.h}px`);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.67",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.67",
|
|
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.24.
|
|
3
|
+
"version": "1.24.67",
|
|
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/release-notes.json
CHANGED
|
@@ -11365,5 +11365,45 @@
|
|
|
11365
11365
|
"id": "1002985",
|
|
11366
11366
|
"text": "The community page now has one test proving, on a real database, that hidden, private and new accounts never show on any leaderboard, search, crown or mover card, and that every week, month and year total is exact."
|
|
11367
11367
|
}
|
|
11368
|
+
],
|
|
11369
|
+
"1.24.66": [
|
|
11370
|
+
{
|
|
11371
|
+
"id": "1004382",
|
|
11372
|
+
"text": "The files that decide who may claim a task and how ranks change are now protected again: a builder below Metic can no longer edit them without review, and a new test fails if a future file split moves those rules somewhere unp"
|
|
11373
|
+
},
|
|
11374
|
+
{
|
|
11375
|
+
"id": "1005117",
|
|
11376
|
+
"text": "Signing in to a second Bongos site no longer signs you out of the first: each command now uses the sign-in for the site it is talking to."
|
|
11377
|
+
},
|
|
11378
|
+
{
|
|
11379
|
+
"id": "1005014",
|
|
11380
|
+
"text": "A worktree session now gets told its own branch and how far behind it is, instead of the main folder's — even when hooks fall back to running from the main folder."
|
|
11381
|
+
},
|
|
11382
|
+
{
|
|
11383
|
+
"id": "1005007",
|
|
11384
|
+
"text": "The privacy check that stops private accounts leaking now also catches database reads written in ordinary quotes, which it used to miss entirely."
|
|
11385
|
+
},
|
|
11386
|
+
{
|
|
11387
|
+
"id": "1004380",
|
|
11388
|
+
"text": "The pre-launch password page can no longer be used to crash the server: a wrong password with an accented letter used to take the whole site down; now it is just refused like any other wrong guess."
|
|
11389
|
+
},
|
|
11390
|
+
{
|
|
11391
|
+
"id": "1005104",
|
|
11392
|
+
"text": "The design checker now flags a logo or image that silently fails to show, instead of reporting everything clean."
|
|
11393
|
+
},
|
|
11394
|
+
{
|
|
11395
|
+
"id": "1005101",
|
|
11396
|
+
"text": "When the automatic reviewers could not run at all on a small change, Bongos now says it was an outage and names the cause, instead of telling the builder their work had problems."
|
|
11397
|
+
},
|
|
11398
|
+
{
|
|
11399
|
+
"id": "1004384",
|
|
11400
|
+
"text": "Sessions that start on a claimed task no longer receive raw idea titles from Discord: hint titles are now cleaned and boxed off as data, and outside ideas are left out, so a planted instruction can't steer the agent. The hints"
|
|
11401
|
+
}
|
|
11402
|
+
],
|
|
11403
|
+
"1.24.67": [
|
|
11404
|
+
{
|
|
11405
|
+
"id": "1004383",
|
|
11406
|
+
"text": "The CI check that guards every merge is now protected, so a builder below Metic can no longer quietly switch its checks off. Protecting the credit-minting file was held back for the owner to decide, because it would lock the w"
|
|
11407
|
+
}
|
|
11368
11408
|
]
|
|
11369
11409
|
}
|
|
@@ -54,7 +54,8 @@ const SESSION_READ_PATHS = SESSION_FILENAMES.flatMap((n) => ic.configReadPaths(n
|
|
|
54
54
|
// default, so module-level consumers (reauth's Settings URL, the hall links
|
|
55
55
|
// hallBase() prints) named the checkout's own instance instead of the one the
|
|
56
56
|
// builder is signed into.
|
|
57
|
-
|
|
57
|
+
// Resolved from the session for the TARGET instance (task 1005117), not the bare active pointer.
|
|
58
|
+
const API_BASE = ic.resolveApiBase({ session: loadSessionSync() });
|
|
58
59
|
|
|
59
60
|
// User-Agent for the GDS CLI's HTTP calls — the branding-derived neutral platform UA
|
|
60
61
|
// from the single source, src/branding.js userAgent() (`<repo.name|envPrefix>-gds/1.0`,
|
|
@@ -251,7 +252,9 @@ async function migrateSessionFile(session) {
|
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
|
|
254
|
-
|
|
255
|
+
// The raw ACTIVE pointer — the single file every pre-store reader looked at, whatever instance it
|
|
256
|
+
// belongs to. saveSession archives THIS (it is what an outgoing sign-in would otherwise lose).
|
|
257
|
+
async function loadActiveSession() {
|
|
255
258
|
// Try each read path in priority order (configured dir's bongos-session.json,
|
|
256
259
|
// legacy otb dir, then each superseded filename across the same dirs). A hit on
|
|
257
260
|
// anything OTHER than SESSION_PATH is migrated forward — SESSION_READ_PATHS[0]
|
|
@@ -267,6 +270,50 @@ async function loadSession() {
|
|
|
267
270
|
return null;
|
|
268
271
|
}
|
|
269
272
|
|
|
273
|
+
// The host this run TARGETS, or null when it names none (task 1005117). An explicit
|
|
274
|
+
// <PREFIX>_API_BASE wins (the operator is steering the base); otherwise a BRANDED checkout targets
|
|
275
|
+
// its own publicOrigin. A neutral checkout with no override targets nothing in particular, so it
|
|
276
|
+
// keeps reading the active pointer exactly as before.
|
|
277
|
+
// The env lookup honours an injected brand's own prefix, so a test can drive this in isolation.
|
|
278
|
+
function sessionTargetBase({ env = process.env, brand, hasInstanceConfig = defaultHasInstanceConfig } = {}) {
|
|
279
|
+
const b = brand || safeBranding();
|
|
280
|
+
const fromEnv = ic.resolveEnv('API_BASE', { env, prefix: ic.envPrefix(b) });
|
|
281
|
+
if (fromEnv) return fromEnv;
|
|
282
|
+
return brandedInstanceOrigin({ brand: b, hasInstanceConfig });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Pick the session for the TARGET instance (task 1005117). The active pointer is ONE slot, so
|
|
286
|
+
// signing in to a second instance mid-run (toast.cloudbongos.com) rewrote it, and a run steered at
|
|
287
|
+
// the first then sent the second instance's token there — every claim and ship 401'd. When the
|
|
288
|
+
// target's host differs from the active session's and the per-instance store (below) holds a
|
|
289
|
+
// session for the target, that one wins. Otherwise the active session is returned unchanged —
|
|
290
|
+
// the backward compatibility with the single file, including a session from before the store.
|
|
291
|
+
//
|
|
292
|
+
// Split in two so the async path reads with fsp and the sync path with fs: targetStorePath() says
|
|
293
|
+
// WHICH store file to try (null = keep the active session), chooseSession() decides with what it read.
|
|
294
|
+
function targetStorePath(active, opts) {
|
|
295
|
+
const targetKey = sessionHostKey(sessionTargetBase(opts));
|
|
296
|
+
if (!targetKey) return null;
|
|
297
|
+
if (active && sessionHostKey(active.api_base) === targetKey) return null;
|
|
298
|
+
return path.join(sessionStoreDir(), `${targetKey}.json`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function chooseSession(active, stored) {
|
|
302
|
+
return stored && stored.token ? stored : active;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function readJsonSync(p) {
|
|
306
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch (_) { return null; }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function loadSession(opts) {
|
|
310
|
+
const active = await loadActiveSession();
|
|
311
|
+
const p = targetStorePath(active, opts);
|
|
312
|
+
if (!p) return active;
|
|
313
|
+
const stored = await readSessionFile(p).catch(() => null);
|
|
314
|
+
return chooseSession(active, stored);
|
|
315
|
+
}
|
|
316
|
+
|
|
270
317
|
// ---- the shared session store (task 1003741, ADR 0269) ---------------------
|
|
271
318
|
// ONE FILE PER INSTANCE, at a FIXED anchor — deliberately NOT configHome().
|
|
272
319
|
//
|
|
@@ -336,7 +383,7 @@ async function saveSession(session) {
|
|
|
336
383
|
await fsp.mkdir(dir, { recursive: true });
|
|
337
384
|
|
|
338
385
|
try {
|
|
339
|
-
const prev = await
|
|
386
|
+
const prev = await loadActiveSession();
|
|
340
387
|
const prevKey = prev && prev.token ? sessionHostKey(prev.api_base) : null;
|
|
341
388
|
const nextKey = sessionHostKey(session && session.api_base);
|
|
342
389
|
if (prevKey && prevKey !== nextKey) await writeSessionToStore(prev);
|
|
@@ -379,7 +426,7 @@ function listStoredSessions() {
|
|
|
379
426
|
return out.sort((a, b) => a.host.localeCompare(b.host));
|
|
380
427
|
}
|
|
381
428
|
|
|
382
|
-
function
|
|
429
|
+
function loadActiveSessionSync() {
|
|
383
430
|
for (const p of SESSION_READ_PATHS) {
|
|
384
431
|
if (!fs.existsSync(p)) continue;
|
|
385
432
|
try {
|
|
@@ -391,6 +438,13 @@ function loadSessionSync() {
|
|
|
391
438
|
return null;
|
|
392
439
|
}
|
|
393
440
|
|
|
441
|
+
// Synchronous twin of loadSession — the session for the TARGET instance (task 1005117).
|
|
442
|
+
function loadSessionSync(opts) {
|
|
443
|
+
const active = loadActiveSessionSync();
|
|
444
|
+
const p = targetStorePath(active, opts);
|
|
445
|
+
return p ? chooseSession(active, readJsonSync(p)) : active;
|
|
446
|
+
}
|
|
447
|
+
|
|
394
448
|
// ---- argv helpers (C11) ---------------------------------------------------
|
|
395
449
|
// One home for the flag parsing every GDS CLI rolled by hand. Two contracts:
|
|
396
450
|
// arg(name) → the flag's VALUE ('--out x' or '--out=x'), else null
|
|
@@ -564,6 +618,14 @@ function defaultHasInstanceConfig() {
|
|
|
564
618
|
} catch { return false; }
|
|
565
619
|
}
|
|
566
620
|
|
|
621
|
+
// THIS checkout's declared instance origin, or null for a neutral checkout / no publicOrigin. The
|
|
622
|
+
// one place both the session picker (sessionTargetBase) and the wrong-instance guard
|
|
623
|
+
// (detectInstanceMismatch) read it, so the two cannot drift (task 1005117).
|
|
624
|
+
function brandedInstanceOrigin({ brand = safeBranding(), hasInstanceConfig = defaultHasInstanceConfig } = {}) {
|
|
625
|
+
if (!hasInstanceConfig()) return null;
|
|
626
|
+
return (brand && brand.domains && brand.domains.publicOrigin) || null;
|
|
627
|
+
}
|
|
628
|
+
|
|
567
629
|
// Host of an origin string, or null if unparseable. Comparing HOSTS (not full
|
|
568
630
|
// origins) tolerates scheme / trailing-slash noise between branding + session.
|
|
569
631
|
function instanceOriginHost(origin) {
|
|
@@ -586,8 +648,7 @@ function instanceOriginHost(origin) {
|
|
|
586
648
|
function detectInstanceMismatch({ session, brand = safeBranding(), env = process.env, hasInstanceConfig = defaultHasInstanceConfig } = {}) {
|
|
587
649
|
if (ic.resolveEnv('API_BASE', { env })) return { mismatch: false };
|
|
588
650
|
if (!session || !session.api_base) return { mismatch: false };
|
|
589
|
-
|
|
590
|
-
const expected = brand && brand.domains && brand.domains.publicOrigin;
|
|
651
|
+
const expected = brandedInstanceOrigin({ brand, hasInstanceConfig });
|
|
591
652
|
if (!expected) return { mismatch: false };
|
|
592
653
|
const expectedHost = instanceOriginHost(expected);
|
|
593
654
|
const actualHost = instanceOriginHost(session.api_base);
|
|
@@ -1184,6 +1245,8 @@ module.exports = {
|
|
|
1184
1245
|
SESSION_READ_PATHS,
|
|
1185
1246
|
loadSession,
|
|
1186
1247
|
loadSessionSync,
|
|
1248
|
+
sessionTargetBase,
|
|
1249
|
+
targetStorePath,
|
|
1187
1250
|
saveSession,
|
|
1188
1251
|
sessionStoreDir,
|
|
1189
1252
|
sessionHostKey,
|
|
@@ -294,7 +294,9 @@ async function gradeAndRecord({ taskId, task, summary, baseline, committedFiles,
|
|
|
294
294
|
// spawned here) so formatGradeOutcome stays unit-testable without a subprocess.
|
|
295
295
|
function formatOutageDiagnostic(g, cliFinding) {
|
|
296
296
|
const out = [];
|
|
297
|
-
|
|
297
|
+
// task 1005101: a single-shot outage (trivial diff / no-artifact) has no
|
|
298
|
+
// panel workers; its dead shots ride signals.outage_shots in the same shape.
|
|
299
|
+
const dead = (g.signals?.workers || g.signals?.outage_shots || []).filter((w) => w && w.error);
|
|
298
300
|
const detailed = dead.filter((w) => w.exit_code != null || w.stderr_tail);
|
|
299
301
|
if (detailed.length) {
|
|
300
302
|
out.push('');
|