@bongos/core 1.19.46 → 1.19.48
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 +40 -25
- package/docs/canonical-permissions.md +3 -3
- package/docs/file-map.md +1 -1
- package/modules/economy/achievements.js +1 -1
- package/modules/grading/grader-rubric.json +3 -2
- package/modules/grading/grader.js +22 -18
- package/modules/lifecycle/publish-reconciler.js +2 -2
- package/modules/lifecycle/routes/gate-approvals.js +1 -1
- package/modules/status-ui/public/status.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/audit-authorship.sh +71 -3
- package/scripts/gds/provision.js +1 -1
- package/scripts/gds/ship.js +49 -9
- package/scripts/setup-builder.sh +3 -3
- package/src/bongos/auth.js +1 -1
- package/src/bongos/routes/modules.js +1 -1
- package/src/module-api.js +5 -1
- package/tests/authorship_audit.mjs +44 -0
- package/tests/grader.mjs +26 -22
- package/tests/linkify_check.mjs +39 -0
- package/tests/ship_builder_model.mjs +76 -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.48",
|
|
6
|
+
"core_contract": "1.19.48",
|
|
7
|
+
"source_commit": "55b3c049db775bb60d3749a998a44110a340f3fa",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-11T18:
|
|
9
|
+
"built_at": "2026-08-11T18:28:57.362Z",
|
|
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": 1217,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1528,
|
|
20
|
+
"tree_sha256": "be96d3ccab1f445af630ea28b9609a9dc3dfba76aa75b5bc394a97e9d9a1d166",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1327,7 +1327,7 @@
|
|
|
1327
1327
|
{
|
|
1328
1328
|
"path": "docs/canonical-permissions.md",
|
|
1329
1329
|
"mode": "0000644",
|
|
1330
|
-
"sha256": "
|
|
1330
|
+
"sha256": "3cc48fe7b291976d819450202090b0df0c165d2470f5895a16febb7cdd5834a2"
|
|
1331
1331
|
},
|
|
1332
1332
|
{
|
|
1333
1333
|
"path": "docs/design-contract.md",
|
|
@@ -1372,7 +1372,7 @@
|
|
|
1372
1372
|
{
|
|
1373
1373
|
"path": "docs/file-map.md",
|
|
1374
1374
|
"mode": "0000644",
|
|
1375
|
-
"sha256": "
|
|
1375
|
+
"sha256": "01f8ff6aaa583d333b466f4d5cff849c09327653c33dcb3d82d60cc46f913cb8"
|
|
1376
1376
|
},
|
|
1377
1377
|
{
|
|
1378
1378
|
"path": "docs/handoff-template.md",
|
|
@@ -2687,7 +2687,7 @@
|
|
|
2687
2687
|
{
|
|
2688
2688
|
"path": "modules/economy/achievements.js",
|
|
2689
2689
|
"mode": "0000644",
|
|
2690
|
-
"sha256": "
|
|
2690
|
+
"sha256": "387c37df8b65bab755e8f006722e6665d9e5aafea29eabf868d1e2dfc4720e99"
|
|
2691
2691
|
},
|
|
2692
2692
|
{
|
|
2693
2693
|
"path": "modules/economy/cost-classify.js",
|
|
@@ -2857,7 +2857,7 @@
|
|
|
2857
2857
|
{
|
|
2858
2858
|
"path": "modules/grading/grader-rubric.json",
|
|
2859
2859
|
"mode": "0000644",
|
|
2860
|
-
"sha256": "
|
|
2860
|
+
"sha256": "2e080d8d50383a24d77f9cf166f6e93e05e7bccc8775d14bbf68f2d6733eecd4"
|
|
2861
2861
|
},
|
|
2862
2862
|
{
|
|
2863
2863
|
"path": "modules/grading/grader-workers/efficiency.js",
|
|
@@ -2912,7 +2912,7 @@
|
|
|
2912
2912
|
{
|
|
2913
2913
|
"path": "modules/grading/grader.js",
|
|
2914
2914
|
"mode": "0000644",
|
|
2915
|
-
"sha256": "
|
|
2915
|
+
"sha256": "f6b80df54bb7634a9effb1cbe51b3ff99b05b0b7912be5a06690f63d237689f9"
|
|
2916
2916
|
},
|
|
2917
2917
|
{
|
|
2918
2918
|
"path": "modules/grading/module.json",
|
|
@@ -3447,7 +3447,7 @@
|
|
|
3447
3447
|
{
|
|
3448
3448
|
"path": "modules/lifecycle/publish-reconciler.js",
|
|
3449
3449
|
"mode": "0000644",
|
|
3450
|
-
"sha256": "
|
|
3450
|
+
"sha256": "3f7f65007955c419ccce1c89cb1f6da6eeca6daa8a76a738540db2858ebff4e6"
|
|
3451
3451
|
},
|
|
3452
3452
|
{
|
|
3453
3453
|
"path": "modules/lifecycle/routes/analytics.js",
|
|
@@ -3472,7 +3472,7 @@
|
|
|
3472
3472
|
{
|
|
3473
3473
|
"path": "modules/lifecycle/routes/gate-approvals.js",
|
|
3474
3474
|
"mode": "0000644",
|
|
3475
|
-
"sha256": "
|
|
3475
|
+
"sha256": "e65687656da193f9d23b398a94061f927cfbf8a63b5263c7e6ee48c5ef50f6e5"
|
|
3476
3476
|
},
|
|
3477
3477
|
{
|
|
3478
3478
|
"path": "modules/lifecycle/routes/goals.js",
|
|
@@ -4022,7 +4022,7 @@
|
|
|
4022
4022
|
{
|
|
4023
4023
|
"path": "modules/status-ui/public/status.js",
|
|
4024
4024
|
"mode": "0000644",
|
|
4025
|
-
"sha256": "
|
|
4025
|
+
"sha256": "bd0fcd32548c7b2cc7c6d36b0f31cba3e0575c95ff3dc85fdb15b11decbdfe42"
|
|
4026
4026
|
},
|
|
4027
4027
|
{
|
|
4028
4028
|
"path": "modules/status-ui/public/style.css",
|
|
@@ -4032,12 +4032,12 @@
|
|
|
4032
4032
|
{
|
|
4033
4033
|
"path": "package-lock.json",
|
|
4034
4034
|
"mode": "0000644",
|
|
4035
|
-
"sha256": "
|
|
4035
|
+
"sha256": "132fd0ff1d19c5dc9ed4fddbcab4d6ca1754214e00da32fcd4bf06fd42cf0b05"
|
|
4036
4036
|
},
|
|
4037
4037
|
{
|
|
4038
4038
|
"path": "package.json",
|
|
4039
4039
|
"mode": "0000644",
|
|
4040
|
-
"sha256": "
|
|
4040
|
+
"sha256": "f8aec45507128cd1060537a8aae479db0806b9adbc9f2b1024e601fe181bce32"
|
|
4041
4041
|
},
|
|
4042
4042
|
{
|
|
4043
4043
|
"path": "public-docs/index.html",
|
|
@@ -4457,7 +4457,7 @@
|
|
|
4457
4457
|
{
|
|
4458
4458
|
"path": "scripts/gds/audit-authorship.sh",
|
|
4459
4459
|
"mode": "0000644",
|
|
4460
|
-
"sha256": "
|
|
4460
|
+
"sha256": "6922f6008c72db47855172fce7e40df0953575b7bead0504758648e2cf2859e9"
|
|
4461
4461
|
},
|
|
4462
4462
|
{
|
|
4463
4463
|
"path": "scripts/gds/audit-deps.js",
|
|
@@ -5007,7 +5007,7 @@
|
|
|
5007
5007
|
{
|
|
5008
5008
|
"path": "scripts/gds/provision.js",
|
|
5009
5009
|
"mode": "0000644",
|
|
5010
|
-
"sha256": "
|
|
5010
|
+
"sha256": "93a5c4a6d6f76cf1fa8ca4c902b19d6ef94f9147488f1c01d3413f692a196942"
|
|
5011
5011
|
},
|
|
5012
5012
|
{
|
|
5013
5013
|
"path": "scripts/gds/publish-manifest.js",
|
|
@@ -5227,7 +5227,7 @@
|
|
|
5227
5227
|
{
|
|
5228
5228
|
"path": "scripts/gds/ship.js",
|
|
5229
5229
|
"mode": "0000644",
|
|
5230
|
-
"sha256": "
|
|
5230
|
+
"sha256": "94f343beba33727cb83367555c35a699f53c8efa5d59e3b65d3f6a1cf3e0e3b0"
|
|
5231
5231
|
},
|
|
5232
5232
|
{
|
|
5233
5233
|
"path": "scripts/gds/smoke-batch-claim.sh",
|
|
@@ -5352,7 +5352,7 @@
|
|
|
5352
5352
|
{
|
|
5353
5353
|
"path": "scripts/setup-builder.sh",
|
|
5354
5354
|
"mode": "0000644",
|
|
5355
|
-
"sha256": "
|
|
5355
|
+
"sha256": "5860bf9daef3dd83a5ff4d3ee5e017e3cbd465ebda0c00df4ca622d629468ec4"
|
|
5356
5356
|
},
|
|
5357
5357
|
{
|
|
5358
5358
|
"path": "scripts/snapshots/backfill-history.mjs",
|
|
@@ -5422,7 +5422,7 @@
|
|
|
5422
5422
|
{
|
|
5423
5423
|
"path": "src/bongos/auth.js",
|
|
5424
5424
|
"mode": "0000644",
|
|
5425
|
-
"sha256": "
|
|
5425
|
+
"sha256": "a8215fa2659638d5850acb787eb3b193b18b545bc7e86f801764cab328b0c837"
|
|
5426
5426
|
},
|
|
5427
5427
|
{
|
|
5428
5428
|
"path": "src/bongos/cascade-dispatch.js",
|
|
@@ -5597,7 +5597,7 @@
|
|
|
5597
5597
|
{
|
|
5598
5598
|
"path": "src/bongos/routes/modules.js",
|
|
5599
5599
|
"mode": "0000644",
|
|
5600
|
-
"sha256": "
|
|
5600
|
+
"sha256": "30be4d5a235cc704d4e1db264fb4e976ef6c614bc680fe90e05f3a199b62fd3d"
|
|
5601
5601
|
},
|
|
5602
5602
|
{
|
|
5603
5603
|
"path": "src/bongos/routes/optimizer.js",
|
|
@@ -5657,7 +5657,7 @@
|
|
|
5657
5657
|
{
|
|
5658
5658
|
"path": "src/module-api.js",
|
|
5659
5659
|
"mode": "0000644",
|
|
5660
|
-
"sha256": "
|
|
5660
|
+
"sha256": "59777605e64fb494479971b94c25cf91ef601c21b5f4153d0856d6281c31db39"
|
|
5661
5661
|
},
|
|
5662
5662
|
{
|
|
5663
5663
|
"path": "src/module-loader/loader.js",
|
|
@@ -5854,6 +5854,11 @@
|
|
|
5854
5854
|
"mode": "0000644",
|
|
5855
5855
|
"sha256": "92b7033ab16d9d4472fcca4695d9df47938a45bdc62feda87b8771f5a2d59ff2"
|
|
5856
5856
|
},
|
|
5857
|
+
{
|
|
5858
|
+
"path": "tests/authorship_audit.mjs",
|
|
5859
|
+
"mode": "0000644",
|
|
5860
|
+
"sha256": "4697e7e264023c148d1f18cac9695157de56b5dd3db4b60f2ab94d73a8d7af65"
|
|
5861
|
+
},
|
|
5857
5862
|
{
|
|
5858
5863
|
"path": "tests/autonomous_fit_protected_paths.mjs",
|
|
5859
5864
|
"mode": "0000644",
|
|
@@ -6637,7 +6642,7 @@
|
|
|
6637
6642
|
{
|
|
6638
6643
|
"path": "tests/grader.mjs",
|
|
6639
6644
|
"mode": "0000644",
|
|
6640
|
-
"sha256": "
|
|
6645
|
+
"sha256": "227d71aaab363d9346c3034f41cfb518e866b49bb460802b32626f288ecdcd76"
|
|
6641
6646
|
},
|
|
6642
6647
|
{
|
|
6643
6648
|
"path": "tests/grader_bypass.mjs",
|
|
@@ -6789,6 +6794,11 @@
|
|
|
6789
6794
|
"mode": "0000644",
|
|
6790
6795
|
"sha256": "7fa36152b6dbb17b2855d261ddbea96af63d26f97001273cd131104235ea431d"
|
|
6791
6796
|
},
|
|
6797
|
+
{
|
|
6798
|
+
"path": "tests/linkify_check.mjs",
|
|
6799
|
+
"mode": "0000644",
|
|
6800
|
+
"sha256": "810683a5283bd5fd7ef66b4f183b4fe8f978b03e156736c5c7cb91fc4f906c99"
|
|
6801
|
+
},
|
|
6792
6802
|
{
|
|
6793
6803
|
"path": "tests/linkify_refs.mjs",
|
|
6794
6804
|
"mode": "0000644",
|
|
@@ -7424,6 +7434,11 @@
|
|
|
7424
7434
|
"mode": "0000644",
|
|
7425
7435
|
"sha256": "2bb81a2649402bc2c17b04d17a4d749ff674f1bf5ac101d168412e709bea8550"
|
|
7426
7436
|
},
|
|
7437
|
+
{
|
|
7438
|
+
"path": "tests/ship_builder_model.mjs",
|
|
7439
|
+
"mode": "0000644",
|
|
7440
|
+
"sha256": "afdb09a3ee072fea82390a187c12c42d8249fce0ce84644a4b8dff6a643dd009"
|
|
7441
|
+
},
|
|
7427
7442
|
{
|
|
7428
7443
|
"path": "tests/ship_card.mjs",
|
|
7429
7444
|
"mode": "0000644",
|
|
@@ -299,6 +299,6 @@ identity. Use `req.builder.*` server-side, the active session client-side, `$HOM
|
|
|
299
299
|
- **Infra config, not impersonation:** the droplet's operational identity (`User=lars`,
|
|
300
300
|
`lars@REDACTED_IP`, `/home/lars/example`, `lars@example.com`).
|
|
301
301
|
- **Enforcement:** [`scripts/gds/audit-authorship.sh`](../scripts/gds/audit-authorship.sh) — exits 0
|
|
302
|
-
clean, exits 1 with a hit list + remediation hints if a new hardcode shows up.
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
clean, exits 1 with a hit list + remediation hints if a new hardcode shows up. Runs on every PR
|
|
303
|
+
inside the required `unit` check via `tests/authorship_audit.mjs` (task 1002640 — it previously had
|
|
304
|
+
no caller and its exclusions had rotted to 342 stale hits). State clean (0 hits) as of that rewire.
|
package/docs/file-map.md
CHANGED
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
│ ├── dev.js ← `bongos dev`: one-command LOCAL launcher for a standalone instance (Postgres → createdb → migrate → server on the branding port → sign-in); up/stop/status (ADR 0149)
|
|
158
158
|
│ ├── dev-lib.js ← pure + injectable core of `bongos dev` (branding→port/db/env resolution, managed detached server pid/health); unit-tested by tests/dev_lib.mjs (ADR 0149)
|
|
159
159
|
│ ├── session-eval.js ← BFG session-inefficiency analysis engine (6D.3): classify turns, compute ceremony-vs-work, detect footguns, file systemic findings (ADR 0027)
|
|
160
|
-
│ ├── linkify-refs.js ← rewrite bare `#NNN` task refs across ALL docs → status-dashboard permalinks (stops GitHub's PR/issue autolinker mis-linking them); conservative classifier, token-free id list via /public/ref-ids; --apply / --check (CI gate:
|
|
160
|
+
│ ├── linkify-refs.js ← rewrite bare `#NNN` task refs across ALL docs → status-dashboard permalinks (stops GitHub's PR/issue autolinker mis-linking them); conservative classifier, token-free id list via /public/ref-ids; --apply / --check (CI gate: tests/linkify_check.mjs, in the required `unit` check) / --dry-run default ([#604](https://example.com/builders#/task/604), [#653](https://example.com/builders#/task/653)).
|
|
161
161
|
│ ├── release.js ← resolve claim as abandoned (`/builder-release`)
|
|
162
162
|
│ ├── cost.js ← log a cost entry (`/builder-cost`)
|
|
163
163
|
│ ├── me.js ← current builder profile + leaderboard
|
|
@@ -239,7 +239,7 @@ async function evaluateAfterCreditAward(client, builderId, totalBefore, totalAft
|
|
|
239
239
|
// with grader_bypass on only ever unlocked first-claim (the claim path works).
|
|
240
240
|
// And breaking-the-100c-bar was boundary-only (totalBefore<100 && totalAfter>=100)
|
|
241
241
|
// on a credit branch the per-session cost-plus reward (ADR 0054) never hits, so
|
|
242
|
-
// it fired for NOBODY — even
|
|
242
|
+
// it fired for NOBODY — even the founding Archon at 22k credits. The whole board looked dead.
|
|
243
243
|
//
|
|
244
244
|
// reconcileBuilderAchievements evaluates every achievement against CURRENT DB
|
|
245
245
|
// state and unlocks any the builder is eligible for but hasn't. It is:
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
"grader_kind": "subagent-panel",
|
|
9
9
|
"threshold": 6.0,
|
|
10
10
|
"gate": "All workers in the active panel must independently report verdict='pass' for confirmed→credits. Any worker reporting 'fail' (or erroring out) keeps the task at completed. Any worker reporting 'concerns' (low confidence, partial AC, advisory economy below threshold, major non-blocker issue, etc.) falls through to the manual-confirm path (POST /tasks/:id/confirm) — same release-valve as v3 low-confidence. The 'concerns' verdict is forwarded to the server-side validateAndScore as confidence=fallthrough_confidence (0.3 by default, below LOW_CONFIDENCE=0.4) so the existing server gate aligns without protocol changes.",
|
|
11
|
-
"default_model": "claude-sonnet-
|
|
12
|
-
"assumed_builder_model": "claude-
|
|
11
|
+
"default_model": "claude-sonnet-5",
|
|
12
|
+
"assumed_builder_model": "claude-fable-5",
|
|
13
|
+
"model_refresh_note": "task 1002659 (2026-08-11): pins refreshed to the Claude 5 family. Assumed builder = claude-fable-5 (the owner-top-model default per CLAUDE.md §2); default grader = claude-sonnet-5 (cross-model per ADR 0024, cost-parity with the prior sonnet-4-6 posture — opus-5 grades the sonnet/haiku builders where the grader should be the stronger model). selectGraderModel no longer throws on an unknown builder id: it warns loudly and falls back to default_model (the #117 throw stranded ships whose builder id lacked an opus/sonnet/haiku substring — exactly what claude-fable-5 is).",
|
|
13
14
|
"active_panel": ["narc", "quality", "hacker", "efficiency"],
|
|
14
15
|
"fallthrough_confidence": 0.3,
|
|
15
16
|
"low_confidence_threshold": 0.4,
|
|
@@ -99,8 +99,8 @@ const LOW_CONFIDENCE = V4.low_confidence_threshold;
|
|
|
99
99
|
// DEFAULT_SHOTS is kept for the v3 multi-shot path (still exported for
|
|
100
100
|
// diagnostics); v4 doesn't use shots — it uses workers.
|
|
101
101
|
const DEFAULT_SHOTS = V3.shots || 3;
|
|
102
|
-
const ASSUMED_BUILDER_MODEL = V4.assumed_builder_model || 'claude-
|
|
103
|
-
const DEFAULT_GRADER_MODEL = V4.default_model || 'claude-sonnet-
|
|
102
|
+
const ASSUMED_BUILDER_MODEL = V4.assumed_builder_model || 'claude-fable-5';
|
|
103
|
+
const DEFAULT_GRADER_MODEL = V4.default_model || 'claude-sonnet-5';
|
|
104
104
|
const FALLTHROUGH_CONFIDENCE = V4.fallthrough_confidence ?? 0.3;
|
|
105
105
|
const workers = require('./grader-workers');
|
|
106
106
|
// Carved into modules/grading/ (BV1.R73 / ADR 0091 §1+§4): the two deterministic
|
|
@@ -208,27 +208,31 @@ function isTrivialDiff({ changedFiles, diffStats } = {}) {
|
|
|
208
208
|
|
|
209
209
|
// ---------- v3: cross-family model selection (ADR 0024) ----------
|
|
210
210
|
|
|
211
|
-
// Returns the grader model given the builder's model — a DIFFERENT
|
|
212
|
-
// the builder is the cross-
|
|
213
|
-
// the caller
|
|
214
|
-
//
|
|
215
|
-
// live callers (selectGraderModel({})) take.
|
|
211
|
+
// Returns the grader model given the builder's model — a DIFFERENT model from
|
|
212
|
+
// the builder is the cross-model adversarial guarantee (ADR 0024). With no
|
|
213
|
+
// builderModel the caller assumes the owner-top-model builder (CLAUDE.md §2),
|
|
214
|
+
// resolving to the default grader — the path selectGraderModel({}) takes.
|
|
216
215
|
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
// grade
|
|
221
|
-
//
|
|
216
|
+
// task 1002659 (Claude 5 refresh): top-tier builders (fable/mythos, opus)
|
|
217
|
+
// grade on the current mid tier; sonnet/haiku builders grade on the tier
|
|
218
|
+
// above. #117's throw-on-unknown is reversed: the throw fired INSIDE the
|
|
219
|
+
// grade step, stranding any ship whose builder id lacked an opus/sonnet/haiku
|
|
220
|
+
// substring (claude-fable-5 was exactly that) — a worse failure than the
|
|
221
|
+
// misconfiguration it guarded. Unknown ids now warn loudly and grade on
|
|
222
|
+
// DEFAULT_GRADER_MODEL; the id still reaches signals.builder_model_assumed,
|
|
223
|
+
// so /grader-health can spot the drift.
|
|
222
224
|
function selectGraderModel({ builderModel } = {}) {
|
|
223
225
|
const bm = (builderModel || ASSUMED_BUILDER_MODEL).toLowerCase();
|
|
224
|
-
if (bm.includes('
|
|
225
|
-
if (bm.includes('
|
|
226
|
-
if (bm.includes('
|
|
227
|
-
|
|
226
|
+
if (bm.includes('fable') || bm.includes('mythos')) return 'claude-sonnet-5';
|
|
227
|
+
if (bm.includes('opus')) return 'claude-sonnet-5';
|
|
228
|
+
if (bm.includes('sonnet')) return 'claude-opus-5';
|
|
229
|
+
if (bm.includes('haiku')) return 'claude-opus-5';
|
|
230
|
+
console.error(
|
|
228
231
|
`selectGraderModel: unrecognized builder model ${JSON.stringify(builderModel)} — ` +
|
|
229
|
-
`
|
|
230
|
-
|
|
232
|
+
`no 'fable'/'mythos'/'opus'/'sonnet'/'haiku' token; grading on the default ` +
|
|
233
|
+
`${DEFAULT_GRADER_MODEL} (cross-model unverifiable — task 1002659, was a #117 throw)`
|
|
231
234
|
);
|
|
235
|
+
return DEFAULT_GRADER_MODEL;
|
|
232
236
|
}
|
|
233
237
|
|
|
234
238
|
// ---------- v3: prompt builder (default-fail framing) ----------
|
|
@@ -122,8 +122,8 @@ function couldBeStaleGenerated(m) {
|
|
|
122
122
|
|
|
123
123
|
// task 1002461 — render the reconciler's operating target: which repo it resolved
|
|
124
124
|
// and whether it holds a credential. This is the line that makes a WRONG repo bind
|
|
125
|
-
// visible without owner access (the live failure: the hub bound
|
|
126
|
-
//
|
|
125
|
+
// visible without owner access (the live failure: the hub bound the instance
|
|
126
|
+
// repo while the PRs were in the core repo).
|
|
127
127
|
// Pure — takes the resolved facts, returns the string — so the wording is
|
|
128
128
|
// unit-tested without a boot.
|
|
129
129
|
function describeTarget({ configured, repo } = {}) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// WHY. The gate-review CI check (ADR 0058) clears an ESCALATE (hard floor) ONLY on
|
|
5
5
|
// an owner approval, and a NEEDS_TRUST without a Metic+ author likewise blocks until
|
|
6
6
|
// the owner approves. Historically that owner approval was a GitHub APPROVED review
|
|
7
|
-
// by
|
|
7
|
+
// by the repo owner on the head sha — i.e. the owner had to leave a GitHub/terminal review.
|
|
8
8
|
// This router gives Archons a browser-only equivalent (feedback: auth flows must be
|
|
9
9
|
// UI-first), trust-consistent with the existing server-set `gate-author-trust`
|
|
10
10
|
// status: the server re-checks the caller's Archon rank server-side (ADR 0016) and
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// API base for the public read endpoints (#737). Empty = same-origin, the case
|
|
5
5
|
// when this file is served by the droplet itself (status.amazonprimea.com
|
|
6
6
|
// proxies /api/* to the same Node process). The OFF-BOX GitHub Pages mirror
|
|
7
|
-
// (
|
|
7
|
+
// (the owner's status-mirror repo, ADR 0029) injects window.STATUS_API_BASE =
|
|
8
8
|
// 'https://amazonprimea.com' in its <head> so the same byte-identical file
|
|
9
9
|
// fetches cross-origin from the droplet there (CORS is open). Keeping this file
|
|
10
10
|
// identical on both hosts is what lets the mirror sync be a pure copy.
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.48",
|
|
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.48",
|
|
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.48",
|
|
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",
|
|
@@ -34,14 +34,24 @@
|
|
|
34
34
|
# * GitHub remote `lars589/amazonprimea` — actual repo owner; cloning
|
|
35
35
|
# anything else would clone a different project.
|
|
36
36
|
# * Comments that document who decided something or who originally ran a
|
|
37
|
-
# one-time backfill — historical decision-attribution.
|
|
37
|
+
# one-time backfill — historical decision-attribution. Allowlisted
|
|
38
|
+
# PER FILE below (a content-level "looks like a comment" filter would
|
|
39
|
+
# also match live code — a JS private class field, a //host literal —
|
|
40
|
+
# so it could be gamed; file entries with justifications can't).
|
|
41
|
+
# * Test data that pins a real contract, allowlisted PER FILE below: owner
|
|
42
|
+
# approval RECORDS the Archon's login, the terminal-URL guard validates
|
|
43
|
+
# term-<login> hostnames — identity in an assertion is the contract.
|
|
44
|
+
# New test files are still scanned (no blanket tests/ exclusion).
|
|
45
|
+
# * Sample API payloads (fixtures/) and gitignored build output (dist/).
|
|
38
46
|
#
|
|
39
47
|
# Usage:
|
|
40
48
|
# bash scripts/gds/audit-authorship.sh # check, exit 1 on new hits
|
|
41
49
|
# bash scripts/gds/audit-authorship.sh --verbose # print every hit (incl.
|
|
42
50
|
# # allowlisted) for review
|
|
43
51
|
#
|
|
44
|
-
#
|
|
52
|
+
# CI lane: tests/authorship_audit.mjs runs this inside the required `unit`
|
|
53
|
+
# check (task 1002640 — it previously had no caller and 342 stale hits).
|
|
54
|
+
# No writes, no network.
|
|
45
55
|
|
|
46
56
|
set -uo pipefail
|
|
47
57
|
|
|
@@ -72,6 +82,10 @@ done
|
|
|
72
82
|
EXCLUDE_DIRS=(
|
|
73
83
|
--exclude-dir=node_modules
|
|
74
84
|
--exclude-dir=.git
|
|
85
|
+
--exclude-dir=dist # gitignored build/package output (a stale
|
|
86
|
+
# core tarball tree re-reports every hit in
|
|
87
|
+
# it — task 1002640); grep -R walks the
|
|
88
|
+
# filesystem, not the git index.
|
|
75
89
|
--exclude-dir=iterations # under art/, historical cost ledger
|
|
76
90
|
--exclude-dir=generated # under art/, generated PNGs / metadata
|
|
77
91
|
--exclude-dir=docs # human prose: ADRs, recipes, plans,
|
|
@@ -79,6 +93,9 @@ EXCLUDE_DIRS=(
|
|
|
79
93
|
# naming the founder by intent
|
|
80
94
|
--exclude-dir=blockers # blockers ARE the things stuck on Lars
|
|
81
95
|
--exclude-dir=limitations # archives + scope-truth docs
|
|
96
|
+
--exclude-dir=fixtures # sample API payloads (hall-preview) mirror
|
|
97
|
+
# real prod rows, which name real builders —
|
|
98
|
+
# frozen sample data, not runtime code.
|
|
82
99
|
--exclude-dir=worktrees # .claude/worktrees/ holds N full repo
|
|
83
100
|
# copies (one per builder worktree); from
|
|
84
101
|
# the main checkout, recursing them is slow
|
|
@@ -151,6 +168,53 @@ ALLOWLIST=(
|
|
|
151
168
|
# leaks in setup.js — the string appearing in the comment IS the
|
|
152
169
|
# specification of what's forbidden in implementation code.
|
|
153
170
|
'tests/setup_smoke.mjs'
|
|
171
|
+
|
|
172
|
+
# Test DATA that pins a real contract (task 1002640) — identity in an
|
|
173
|
+
# assertion IS the contract, not a mis-attribution: owner approval RECORDS
|
|
174
|
+
# the Archon's login (gate_approvals), the terminal-URL SSRF guard validates
|
|
175
|
+
# term-<login> hostnames (box_terminal_ssrf), packaging/provisioning tests
|
|
176
|
+
# exercise the real repo owner + the mirror-redaction floor. Listed per file
|
|
177
|
+
# (never a blanket tests/ exclusion) so a NEW test file is still scanned.
|
|
178
|
+
'tests/provision.mjs'
|
|
179
|
+
'tests/package_core.mjs'
|
|
180
|
+
'tests/init.mjs'
|
|
181
|
+
'tests/box_terminal_ssrf.mjs'
|
|
182
|
+
'tests/gate_approvals.mjs'
|
|
183
|
+
'tests/publish_reconciler.mjs'
|
|
184
|
+
'tests/dev_lib.mjs'
|
|
185
|
+
'tests/ship_card.mjs'
|
|
186
|
+
'tests/scrubber_corpus.mjs'
|
|
187
|
+
'tests/module_submissions.mjs'
|
|
188
|
+
'tests/boxes.mjs'
|
|
189
|
+
'tests/provisioning_onboard_mode.mjs'
|
|
190
|
+
'tests/instance_manifest.mjs'
|
|
191
|
+
'tests/bongos_downloads.mjs'
|
|
192
|
+
'tests/status_mirror_sync.mjs'
|
|
193
|
+
'tests/search_isolation.mjs'
|
|
194
|
+
'tests/grade_aggregates_unavailable.mjs'
|
|
195
|
+
'tests/first_admin_bootstrap.mjs'
|
|
196
|
+
'tests/discord_approvals.mjs'
|
|
197
|
+
'tests/currency_label.mjs'
|
|
198
|
+
'tests/cloud_bongos_pack.mjs'
|
|
199
|
+
'tests/bump_version.mjs'
|
|
200
|
+
'tests/box_scope_session.mjs'
|
|
201
|
+
'tests/auth_inactive_builder.mjs'
|
|
202
|
+
'tests/auth_anthropic_identity.mjs'
|
|
203
|
+
'modules/dev-box/app/test/unit.mjs'
|
|
204
|
+
|
|
205
|
+
# The public landing page links the open-source repo by URL — repo-hosting
|
|
206
|
+
# identity (the provision-staging.sh class), not a builder identity.
|
|
207
|
+
'public-landing/create.html'
|
|
208
|
+
|
|
209
|
+
# The mirror-redaction floor: PRIOR_INSTANCE_FLOOR carries the prior
|
|
210
|
+
# instance's owner literal precisely so packaging can SCRUB it from every
|
|
211
|
+
# published core — the anti-leak spec, not a runtime hardcode (task 1002640).
|
|
212
|
+
'scripts/gds/package-core.js'
|
|
213
|
+
|
|
214
|
+
# Frozen seed script — a task DESCRIPTION quotes migration 023's founding-
|
|
215
|
+
# Archon literal as the thing that task exists to remove (the
|
|
216
|
+
# seed-v3-tasks.js class: stripping it would lose the task history).
|
|
217
|
+
'scripts/gds/seed-medusa-tasks.js'
|
|
154
218
|
)
|
|
155
219
|
|
|
156
220
|
# ----------------------------------------------------------------------------
|
|
@@ -212,7 +276,11 @@ else
|
|
|
212
276
|
fi
|
|
213
277
|
|
|
214
278
|
# Apply operational-identity + spec-string pattern allowlists (both match on
|
|
215
|
-
# line content, not file path).
|
|
279
|
+
# line content, not file path). NB deliberately NO comment-line filter: a
|
|
280
|
+
# content pattern like `^\s*(//|#)` also matches live code (a JS private class
|
|
281
|
+
# field `#name = ...`, a protocol-relative `//host` continuation line), so a
|
|
282
|
+
# real hardcode could dress as a comment to slip through. Historical-
|
|
283
|
+
# attribution comments get per-file ALLOWLIST entries instead (task 1002640).
|
|
216
284
|
grep -vE "$OPS_ALLOWLIST_PATTERN" "$TMP_HITS" \
|
|
217
285
|
| grep -vE "$SPEC_ALLOWLIST_PATTERN" > "$TMP_FILTERED" || true
|
|
218
286
|
|
package/scripts/gds/provision.js
CHANGED
|
@@ -851,7 +851,7 @@ ${prefix}_OAUTH_ORIGIN=${origin}
|
|
|
851
851
|
// <prefix>_FIRST_ADMIN before branding.firstAdmin). WITHOUT it a provisioned instance
|
|
852
852
|
// has NO owner — first sign-in is refused / lands rankless and the owner can never
|
|
853
853
|
// claim their own instance (goal 35 / criterion 165 regression, caught live 2026-07-07:
|
|
854
|
-
// demo had every origin override but no FIRST_ADMIN, so
|
|
854
|
+
// demo had every origin override but no FIRST_ADMIN, so the owner could not be seated).
|
|
855
855
|
// Derived from the instance repo owner (target_ref = "<owner>/<repo>"), the SAME source
|
|
856
856
|
// instanceInitSpec uses for the scaffold's branding.firstAdmin — so this env override and
|
|
857
857
|
// the scaffolded config/branding.json agree. (Org-owned repo: the org login is not a
|
package/scripts/gds/ship.js
CHANGED
|
@@ -251,6 +251,39 @@ function resolveTranscriptPath(sessionId, projectDirs, opts = {}) {
|
|
|
251
251
|
return null;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// resolveBuilderModel — the REAL model that authored this session's work, read
|
|
255
|
+
// from the session transcript (dominant by output tokens, synthetic turns
|
|
256
|
+
// skipped), so selectGraderModel gets a true cross-model input instead of the
|
|
257
|
+
// ASSUMED_BUILDER_MODEL fallback (task 1002659 — everything graded on the
|
|
258
|
+
// assumed-builder default before this). Best-effort: no session id, no
|
|
259
|
+
// transcript, or any read error → null → the grader assumes, as before.
|
|
260
|
+
function resolveBuilderModel(opts = {}) {
|
|
261
|
+
try {
|
|
262
|
+
const sessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
263
|
+
// Same path-traversal guard as uploadSessionOnShip: the id lands in a path.
|
|
264
|
+
if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId)) return null;
|
|
265
|
+
const transcriptPath = resolveTranscriptPath(sessionId, [process.cwd(), REPO_ROOT].filter(Boolean), opts);
|
|
266
|
+
if (!transcriptPath) return null;
|
|
267
|
+
const perModel = new Map();
|
|
268
|
+
for (const line of fs.readFileSync(transcriptPath, 'utf8').split('\n')) {
|
|
269
|
+
if (!line.trim()) continue;
|
|
270
|
+
let o;
|
|
271
|
+
try { o = JSON.parse(line); } catch (_) { continue; }
|
|
272
|
+
const msg = o && o.message;
|
|
273
|
+
if (!msg || !msg.model || msg.model === '<synthetic>' || !msg.usage) continue;
|
|
274
|
+
perModel.set(msg.model, (perModel.get(msg.model) || 0) + (Number(msg.usage.output_tokens) || 0));
|
|
275
|
+
}
|
|
276
|
+
let best = null;
|
|
277
|
+
let bestTokens = 0;
|
|
278
|
+
for (const [model, tokens] of perModel) {
|
|
279
|
+
if (tokens > bestTokens) { best = model; bestTokens = tokens; }
|
|
280
|
+
}
|
|
281
|
+
return best;
|
|
282
|
+
} catch (_) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
254
287
|
function resolveMemoryDir() {
|
|
255
288
|
// REPO_ROOT is the main worktree (canonical project checkout). The memory dir
|
|
256
289
|
// is keyed to that path, not the per-task feature worktree we're shipping from.
|
|
@@ -1812,11 +1845,10 @@ function mergeMainIntoBranch() {
|
|
|
1812
1845
|
|
|
1813
1846
|
// ---------- Pre-verify: auto-linkify the ship's own changed docs ----------
|
|
1814
1847
|
//
|
|
1815
|
-
// The doc-ref-linkify CI gate (
|
|
1816
|
-
//
|
|
1817
|
-
//
|
|
1818
|
-
//
|
|
1819
|
-
// pure noise: it can't prevent the leak, only complain about it (#747).
|
|
1848
|
+
// The doc-ref-linkify CI gate (tests/linkify_check.mjs, in the required `unit`
|
|
1849
|
+
// check — task 1002639; a standalone doc-ref-linkify.yml workflow never
|
|
1850
|
+
// actually existed) red-Xes a PR carrying a bare "#NNN" task ref in a doc. A
|
|
1851
|
+
// red-X after the builder walked away still strands the land (#747).
|
|
1820
1852
|
//
|
|
1821
1853
|
// The structural cure is to fix the ship's OWN changed docs here, before the
|
|
1822
1854
|
// merge, and commit the rewrite. The gate then stays as the backstop for refs
|
|
@@ -2062,6 +2094,9 @@ async function runSubagentGrader({ task, valueSummary, baseline, changedFiles: p
|
|
|
2062
2094
|
timeoutMs: GRADER_TIMEOUT_MS,
|
|
2063
2095
|
cwd: process.cwd(), // grade the FEATURE worktree, not REPO_ROOT
|
|
2064
2096
|
};
|
|
2097
|
+
// task 1002659: the real session model (transcript-derived) drives the
|
|
2098
|
+
// cross-model grader pick; null → the grader assumes, as before.
|
|
2099
|
+
const builderModel = resolveBuilderModel();
|
|
2065
2100
|
const t0 = Date.now();
|
|
2066
2101
|
let result;
|
|
2067
2102
|
// #513: a small copy/docs-only diff gets a SINGLE generalist grader shot
|
|
@@ -2071,7 +2106,7 @@ async function runSubagentGrader({ task, valueSummary, baseline, changedFiles: p
|
|
|
2071
2106
|
// inefficiency of running a 4-worker panel on a +1/-1 copy tweak.
|
|
2072
2107
|
if (grader.isTrivialDiff({ changedFiles, diffStats: shortstat })) {
|
|
2073
2108
|
const totalLines = (shortstat.lines_added || 0) + (shortstat.lines_removed || 0);
|
|
2074
|
-
console.log(` subagent grader ${grader.VERSION}: TRIVIAL diff (${shortstat.files_changed} file(s), ${totalLines} line(s), copy/docs only) → single generalist shot, model=${grader.selectGraderModel({})} — instead of the ${grader.WORKERS.DEFAULT_PANEL.length}-worker panel (#513)`);
|
|
2109
|
+
console.log(` subagent grader ${grader.VERSION}: TRIVIAL diff (${shortstat.files_changed} file(s), ${totalLines} line(s), copy/docs only) → single generalist shot, model=${grader.selectGraderModel({ builderModel })} — instead of the ${grader.WORKERS.DEFAULT_PANEL.length}-worker panel (#513)`);
|
|
2075
2110
|
console.log(` gate: the shot must return functional_fidelity>=${grader.THRESHOLD} AND code_quality>=${grader.THRESHOLD} AND confidence>=0.4`);
|
|
2076
2111
|
result = await grader.gradeSubagentV3MultiShot({
|
|
2077
2112
|
task,
|
|
@@ -2080,11 +2115,12 @@ async function runSubagentGrader({ task, valueSummary, baseline, changedFiles: p
|
|
|
2080
2115
|
changedFiles,
|
|
2081
2116
|
diffStats: shortstat,
|
|
2082
2117
|
shots: 1,
|
|
2118
|
+
builderModel, // task 1002659: real session model → cross-model grader pick
|
|
2083
2119
|
runOpts,
|
|
2084
2120
|
});
|
|
2085
2121
|
} else {
|
|
2086
2122
|
const panel = grader.WORKERS.DEFAULT_PANEL;
|
|
2087
|
-
console.log(` subagent grader ${grader.VERSION}: panel of ${panel.length} workers (${panel.join(', ')}), model=${grader.
|
|
2123
|
+
console.log(` subagent grader ${grader.VERSION}: panel of ${panel.length} workers (${panel.join(', ')}), model=${grader.selectGraderModel({ builderModel })} (builder ${builderModel || `${grader.ASSUMED_BUILDER_MODEL} assumed`}), changed_files=${changedFiles.length}, diff_chars=${diff.length}, per-worker timeout=${GRADER_TIMEOUT_MS}ms`);
|
|
2088
2124
|
console.log(` gate: every worker must return verdict='pass'; 'concerns' → manual-confirm fall-through; 'fail' blocks`);
|
|
2089
2125
|
if (priorRound) {
|
|
2090
2126
|
console.log(` delta re-grade (task 1002662): prior round's findings + builder response ride along, fenced; answered findings can only re-veto with NEW evidence`);
|
|
@@ -2096,6 +2132,7 @@ async function runSubagentGrader({ task, valueSummary, baseline, changedFiles: p
|
|
|
2096
2132
|
changedFiles,
|
|
2097
2133
|
diffStats: shortstat, // V3.R92 #353: feed diff size into the economy axis
|
|
2098
2134
|
builderRank, // SEC #863: drives the permission-path pre-pass
|
|
2135
|
+
builderModel, // task 1002659: real session model → cross-model grader pick
|
|
2099
2136
|
priorRound, // task 1002662: delta-aware re-grade context (panel path only)
|
|
2100
2137
|
runOpts,
|
|
2101
2138
|
});
|
|
@@ -3647,8 +3684,8 @@ async function shipMain() {
|
|
|
3647
3684
|
|
|
3648
3685
|
// ---- Pre-verify: auto-linkify this ship's own changed docs (#747) ----
|
|
3649
3686
|
// Fix bare "#NNN" task refs in the docs this ship changed BEFORE the merge,
|
|
3650
|
-
// so they never
|
|
3651
|
-
// Best-effort.
|
|
3687
|
+
// so they never red-X the doc-ref-linkify gate (tests/linkify_check.mjs in
|
|
3688
|
+
// the `unit` check). Best-effort.
|
|
3652
3689
|
autoLinkifyChangedDocs(ac);
|
|
3653
3690
|
|
|
3654
3691
|
// ---- Empty-diff / phantom-ship guard (must run BEFORE the review gate + resolve) ----
|
|
@@ -4221,6 +4258,9 @@ async function regradeMain() {
|
|
|
4221
4258
|
// exported: it does I/O + network and is covered by the live smoke at ship.)
|
|
4222
4259
|
module.exports = {
|
|
4223
4260
|
scrubSecrets, snippetOf, buildSessionDigest, resolveTranscriptPath,
|
|
4261
|
+
// task 1002659: transcript-derived real builder model (dominant by output
|
|
4262
|
+
// tokens) — exported pure-ish (fs read only, opts-injectable) for the test.
|
|
4263
|
+
resolveBuilderModel,
|
|
4224
4264
|
// task 1002471: the project-path encoder, exported pure so the win32 shapes
|
|
4225
4265
|
// (drive colon, backslash separators, spaces) are pinned cross-platform —
|
|
4226
4266
|
// resolveTranscriptPath itself runs path.resolve, which can't carry a Windows
|
package/scripts/setup-builder.sh
CHANGED
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
# ./scripts/setup-builder.sh [--skip-keys] [--no-auth] [--help]
|
|
15
15
|
#
|
|
16
16
|
# Run this from INSIDE a clone of the repo. If you don't have the code yet,
|
|
17
|
-
# do "Step 0 — get the project onto your machine" first (see README.md)
|
|
18
|
-
#
|
|
17
|
+
# do "Step 0 — get the project onto your machine" first (see README.md) —
|
|
18
|
+
# clone YOUR instance's repo, then cd into it.
|
|
19
19
|
set -uo pipefail
|
|
20
20
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
21
|
# Guard: this shim must run from inside the repo (where setup-builder.js lives).
|
|
22
22
|
if [ ! -f "$SCRIPT_DIR/setup-builder.js" ]; then
|
|
23
23
|
echo "setup-builder: can't find setup-builder.js next to this script." >&2
|
|
24
24
|
echo "Did you clone the repo and cd into it? See README.md 'Step 0 — get the project onto your machine'." >&2
|
|
25
|
-
echo "
|
|
25
|
+
echo " (clone YOUR instance's repo, then cd into it and re-run this script)" >&2
|
|
26
26
|
exit 1
|
|
27
27
|
fi
|
|
28
28
|
exec node "$SCRIPT_DIR/setup-builder.js" "$@"
|
package/src/bongos/auth.js
CHANGED
|
@@ -63,7 +63,7 @@ const SCOPE = 'read:user';
|
|
|
63
63
|
// support is needed". It is needed. `public_repo` grants write to PUBLIC repos
|
|
64
64
|
// only, so ADOPTING an existing PRIVATE repo dies at the provisioner's scaffold
|
|
65
65
|
// push with GitHub 403 "Write access to repository not granted" — observed live on
|
|
66
|
-
//
|
|
66
|
+
// a private repo of the owner's. ADR 0155 takes the reversal at the owner's
|
|
67
67
|
// explicit call, accepting the cost it names: a token we persist can now write to
|
|
68
68
|
// EVERY repo the user owns, not just their public ones.
|
|
69
69
|
const REPO_SCOPE = 'repo';
|
|
@@ -65,7 +65,7 @@ function ownProjectName() {
|
|
|
65
65
|
try { return require('../../branding').branding().identity.productName; } catch { return null; }
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// "owner/name", e.g. "
|
|
68
|
+
// "owner/name", e.g. "some-owner/their-repo" — the provenance a submission
|
|
69
69
|
// carries (ADR 0107 §2: attribution/provenance travels WITH the module as a
|
|
70
70
|
// portable record, not a cross-GDS link). null if the branding pack has none.
|
|
71
71
|
function sourceRepoLabel() {
|
package/src/module-api.js
CHANGED
|
@@ -519,8 +519,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
519
519
|
// landed since 1.19.44 with no explicit bump. run 31519500533. (task 1002620)
|
|
520
520
|
// 1.19.46 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
521
521
|
// landed since 1.19.45 with no explicit bump. run 31520432748. (task 1002620)
|
|
522
|
+
// 1.19.47 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
523
|
+
// landed since 1.19.46 with no explicit bump. run 31521404176. (task 1002620)
|
|
524
|
+
// 1.19.48 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
525
|
+
// landed since 1.19.47 with no explicit bump. run 31522865111. (task 1002620)
|
|
522
526
|
// ---------------------------------------------------------------------------
|
|
523
|
-
const CORE_VERSION = '1.19.
|
|
527
|
+
const CORE_VERSION = '1.19.48'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
524
528
|
|
|
525
529
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
526
530
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// tests/authorship_audit.mjs — the no-hardcoded-Archon-identity gate (task 1002640).
|
|
2
|
+
//
|
|
3
|
+
// scripts/gds/audit-authorship.sh (named as THE enforcement in
|
|
4
|
+
// docs/canonical-permissions.md) had no caller — no CI lane, no test, no skill
|
|
5
|
+
// — and 342 stale hits on a clean tree. This test makes it a real gate: it runs
|
|
6
|
+
// inside the required `unit` check, so a new hardcoded founder-identity
|
|
7
|
+
// reference in implementation code fails the PR. The script is local-only (grep
|
|
8
|
+
// over the tree — no DB, no network). Fail-open only where the RUNTIME is
|
|
9
|
+
// missing: a builder machine without bash on PATH (the gen-diagrams render-step
|
|
10
|
+
// precedent) skips with a warning; Linux CI always has bash, so the gate is
|
|
11
|
+
// always live where it matters.
|
|
12
|
+
|
|
13
|
+
import { strict as assert } from 'node:assert';
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const REPO = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const SCRIPT = path.join(REPO, 'scripts', 'gds', 'audit-authorship.sh');
|
|
20
|
+
const SPAWN_TIMEOUT_MS = 75_000; // under run-unit-tests' 90s cap
|
|
21
|
+
|
|
22
|
+
const r = spawnSync('bash', [SCRIPT], {
|
|
23
|
+
cwd: REPO,
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
26
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (r.error && r.error.code === 'ENOENT') {
|
|
30
|
+
console.warn('authorship_audit: bash not on PATH — skipping (the gate runs on Linux CI; a bash-less builder box cannot run a .sh).');
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// NO timeout fail-open: the audit is local-only (grep over the tree — no DB,
|
|
35
|
+
// no network), so unlike an infra-dependent check there is no innocent reason
|
|
36
|
+
// for it to hang; a diff engineered to make the scan slow must FAIL the gate,
|
|
37
|
+
// not slip through it. spawnSync's ETIMEDOUT falls through to the assertions.
|
|
38
|
+
assert.equal(r.error, undefined, `audit-authorship.sh failed to run: ${r.error && r.error.message}`);
|
|
39
|
+
assert.equal(
|
|
40
|
+
r.status,
|
|
41
|
+
0,
|
|
42
|
+
`audit-authorship.sh found hardcoded founder-identity references in implementation code:\n${r.stdout}\n${r.stderr}`
|
|
43
|
+
);
|
|
44
|
+
console.log('authorship_audit: clean — no hardcoded Archon-identity references in implementation code.');
|
package/tests/grader.mjs
CHANGED
|
@@ -192,8 +192,8 @@ test('v4 exports the expected surface (current panel-based gate)', () => {
|
|
|
192
192
|
assert.ok(grader.THRESHOLD >= 5.0 && grader.THRESHOLD <= 10.0);
|
|
193
193
|
assert.ok(grader.VERSION.startsWith('v4'), `expected v4.*, got ${grader.VERSION}`);
|
|
194
194
|
assert.equal(grader.DEFAULT_SHOTS, 3); // kept for v3 multi-shot diagnostic path
|
|
195
|
-
assert.equal(grader.DEFAULT_GRADER_MODEL, 'claude-sonnet-
|
|
196
|
-
assert.equal(grader.ASSUMED_BUILDER_MODEL, 'claude-
|
|
195
|
+
assert.equal(grader.DEFAULT_GRADER_MODEL, 'claude-sonnet-5'); // task 1002659: Claude 5 family pin
|
|
196
|
+
assert.equal(grader.ASSUMED_BUILDER_MODEL, 'claude-fable-5');
|
|
197
197
|
assert.equal(grader.FALLTHROUGH_CONFIDENCE, 0.3);
|
|
198
198
|
// Worker registry sanity — Phase 2.2 added efficiency as the 4th worker
|
|
199
199
|
// (after Phase 2.1's hacker).
|
|
@@ -819,32 +819,36 @@ test('v2 extractAssistantResult: passes-through non-envelope output', () => {
|
|
|
819
819
|
// v3 — cross-family selection + multi-shot aggregation (MAS Phase 1 / #395)
|
|
820
820
|
// ========================================================================
|
|
821
821
|
|
|
822
|
-
test('
|
|
823
|
-
assert.equal(grader.selectGraderModel({ builderModel: 'claude-
|
|
824
|
-
assert.equal(grader.selectGraderModel({ builderModel: 'claude-
|
|
825
|
-
assert.equal(grader.selectGraderModel({ builderModel: 'opus' }), 'claude-sonnet-4-6');
|
|
822
|
+
test('selectGraderModel: builder Fable/Mythos → grader Sonnet 5 (cross-model, task 1002659)', () => {
|
|
823
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-fable-5' }), 'claude-sonnet-5');
|
|
824
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-mythos-5' }), 'claude-sonnet-5');
|
|
826
825
|
});
|
|
827
826
|
|
|
828
|
-
test('
|
|
829
|
-
assert.equal(grader.selectGraderModel({ builderModel: 'claude-
|
|
830
|
-
assert.equal(grader.selectGraderModel({ builderModel: '
|
|
827
|
+
test('selectGraderModel: builder Opus → grader Sonnet (cross-family)', () => {
|
|
828
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-opus-5' }), 'claude-sonnet-5');
|
|
829
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-opus-4-8' }), 'claude-sonnet-5');
|
|
830
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'opus' }), 'claude-sonnet-5');
|
|
831
831
|
});
|
|
832
832
|
|
|
833
|
-
test('
|
|
834
|
-
assert.equal(grader.selectGraderModel({ builderModel: 'claude-
|
|
835
|
-
assert.equal(grader.selectGraderModel({ builderModel: '
|
|
833
|
+
test('selectGraderModel: builder Sonnet → grader Opus (cross-family)', () => {
|
|
834
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-sonnet-5' }), 'claude-opus-5');
|
|
835
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'sonnet' }), 'claude-opus-5');
|
|
836
836
|
});
|
|
837
837
|
|
|
838
|
-
test('selectGraderModel:
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
assert.
|
|
847
|
-
assert.
|
|
838
|
+
test('selectGraderModel: builder Haiku → grader Opus', () => {
|
|
839
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'claude-haiku-4-5-20251001' }), 'claude-opus-5');
|
|
840
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'haiku' }), 'claude-opus-5');
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
test('selectGraderModel: missing builder → default (assumed-Fable builder → Sonnet 5); unknown id FALLS BACK, never throws (task 1002659)', () => {
|
|
844
|
+
// No builderModel is the legitimate default path (the caller assumes the
|
|
845
|
+
// owner-top-model builder per CLAUDE.md §2), resolving to the default grader.
|
|
846
|
+
assert.equal(grader.selectGraderModel({}), 'claude-sonnet-5');
|
|
847
|
+
assert.equal(grader.selectGraderModel(), 'claude-sonnet-5');
|
|
848
|
+
// task 1002659 reverses #117's throw: an unknown id used to strand the ship
|
|
849
|
+
// inside the grade step. Now it warns loudly and grades on the default model.
|
|
850
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'gpt-4' }), grader.DEFAULT_GRADER_MODEL);
|
|
851
|
+
assert.equal(grader.selectGraderModel({ builderModel: 'llama-3' }), grader.DEFAULT_GRADER_MODEL);
|
|
848
852
|
});
|
|
849
853
|
|
|
850
854
|
// --- aggregateShots: helpers ---
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// tests/linkify_check.mjs — the doc-ref linkify CI gate (task 1002639).
|
|
2
|
+
//
|
|
3
|
+
// docs/file-map.md long claimed a `.github/workflows/doc-ref-linkify.yml` CI
|
|
4
|
+
// gate that never existed (the G-20 phantom-reference shape). This test IS the
|
|
5
|
+
// gate, made real the cheap way: it runs inside the required `unit` check, so a
|
|
6
|
+
// committed doc with a bare `#NNN` ref (which GitHub would mis-autolink to its
|
|
7
|
+
// own issues) fails the PR. linkify-refs.js --check already fails OPEN when the
|
|
8
|
+
// /public/ref-ids allow-list is unreachable (a network blip must not red-X an
|
|
9
|
+
// unrelated build); the spawn timeout below mirrors that posture — infra
|
|
10
|
+
// slowness is a warn + skip, never a false red.
|
|
11
|
+
|
|
12
|
+
import { strict as assert } from 'node:assert';
|
|
13
|
+
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const REPO = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
18
|
+
const SCRIPT = path.join(REPO, 'scripts', 'gds', 'linkify-refs.js');
|
|
19
|
+
const SPAWN_TIMEOUT_MS = 60_000; // under run-unit-tests' 90s so WE decide the timeout outcome
|
|
20
|
+
|
|
21
|
+
const r = spawnSync(process.execPath, [SCRIPT, '--check'], {
|
|
22
|
+
cwd: REPO,
|
|
23
|
+
encoding: 'utf8',
|
|
24
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
25
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (r.error && r.error.code === 'ETIMEDOUT') {
|
|
29
|
+
console.warn(`linkify_check: --check did not finish in ${SPAWN_TIMEOUT_MS}ms — skipping (fail-open on infra, matching the script's own --check posture).`);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
assert.equal(r.error, undefined, `linkify-refs.js --check failed to spawn: ${r.error && r.error.message}`);
|
|
34
|
+
assert.equal(
|
|
35
|
+
r.status,
|
|
36
|
+
0,
|
|
37
|
+
`linkify-refs.js --check found bare/upgradeable refs in committed docs — run \`node scripts/gds/linkify-refs.js --apply\` and commit.\n${r.stdout}\n${r.stderr}`
|
|
38
|
+
);
|
|
39
|
+
console.log('linkify_check: committed docs carry no bare #NNN refs (linkify-refs.js --check exit 0).');
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// tests/ship_builder_model.mjs — task 1002659: resolveBuilderModel reads the
|
|
2
|
+
// session transcript and returns the dominant model by output tokens, so the
|
|
3
|
+
// grader's cross-model pick keys on what actually authored the work.
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const ship = require('../scripts/gds/ship.js');
|
|
12
|
+
|
|
13
|
+
let passed = 0;
|
|
14
|
+
let failed = 0;
|
|
15
|
+
function test(name, fn) {
|
|
16
|
+
try {
|
|
17
|
+
fn();
|
|
18
|
+
console.log(` ok ${name}`);
|
|
19
|
+
passed++;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error(`FAIL ${name}\n ${e.message}`);
|
|
22
|
+
failed++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Mirror ship.js's transcript layout: <base>/projects/<encoded cwd>/<sessionId>.jsonl
|
|
27
|
+
function writeTranscript(base, sessionId, lines) {
|
|
28
|
+
const encoded = ship.encodeProjectPath(path.resolve(process.cwd()));
|
|
29
|
+
const dir = path.join(base, 'projects', encoded);
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
const p = path.join(dir, `${sessionId}.jsonl`);
|
|
32
|
+
fs.writeFileSync(p, lines.map((l) => JSON.stringify(l)).join('\n'));
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const msg = (model, outputTokens) => ({
|
|
37
|
+
message: { model, usage: { output_tokens: outputTokens } },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'ship-builder-model-'));
|
|
41
|
+
const SESSION = 'test-session-1002659';
|
|
42
|
+
const prevSession = process.env.CLAUDE_CODE_SESSION_ID;
|
|
43
|
+
process.env.CLAUDE_CODE_SESSION_ID = SESSION;
|
|
44
|
+
|
|
45
|
+
writeTranscript(base, SESSION, [
|
|
46
|
+
msg('claude-fable-5', 900),
|
|
47
|
+
msg('claude-haiku-4-5-20251001', 50),
|
|
48
|
+
msg('claude-fable-5', 800),
|
|
49
|
+
{ message: { model: '<synthetic>', usage: { output_tokens: 99999 } } }, // skipped
|
|
50
|
+
{ not_a_message: true },
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
test('dominant-by-output-tokens model wins; synthetic turns are skipped', () => {
|
|
54
|
+
assert.equal(ship.resolveBuilderModel({ base }), 'claude-fable-5');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('no transcript for the session → null (grader assumes)', () => {
|
|
58
|
+
process.env.CLAUDE_CODE_SESSION_ID = 'no-such-session';
|
|
59
|
+
assert.equal(ship.resolveBuilderModel({ base }), null);
|
|
60
|
+
process.env.CLAUDE_CODE_SESSION_ID = SESSION;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('missing or path-hostile session id → null', () => {
|
|
64
|
+
delete process.env.CLAUDE_CODE_SESSION_ID;
|
|
65
|
+
assert.equal(ship.resolveBuilderModel({ base }), null);
|
|
66
|
+
process.env.CLAUDE_CODE_SESSION_ID = '../evil';
|
|
67
|
+
assert.equal(ship.resolveBuilderModel({ base }), null);
|
|
68
|
+
process.env.CLAUDE_CODE_SESSION_ID = SESSION;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (prevSession === undefined) delete process.env.CLAUDE_CODE_SESSION_ID;
|
|
72
|
+
else process.env.CLAUDE_CODE_SESSION_ID = prevSession;
|
|
73
|
+
fs.rmSync(base, { recursive: true, force: true });
|
|
74
|
+
|
|
75
|
+
console.log(`\nship_builder_model: ${passed} passed, ${failed} failed`);
|
|
76
|
+
process.exit(failed ? 1 : 0);
|