@bongos/core 1.19.1074 → 1.19.1075

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.
Files changed (114) hide show
  1. package/.bongos-core.json +122 -122
  2. package/bin/bongos.js +1 -3
  3. package/docs/api/openapi.json +4 -4
  4. package/docs/module-api-changelog.md +2 -0
  5. package/modules/copy-desk/routes/copy-desk.js +1 -1
  6. package/modules/discord/ship-broadcast.js +3 -3
  7. package/modules/economy/credits.js +2 -2
  8. package/modules/ideas/projection.js +1 -1
  9. package/modules/lifecycle/db-ship.js +4 -4
  10. package/modules/lifecycle/github-push.js +12 -14
  11. package/modules/lifecycle/publish-reconciler.js +10 -10
  12. package/modules/lifecycle/routes/tasks.js +8 -8
  13. package/modules/lifecycle/ship-card.js +1 -1
  14. package/modules/lifecycle/task-visuals.js +2 -2
  15. package/modules/memory/routes/memory.js +2 -2
  16. package/modules/platform-identity/routes/sso.js +3 -2
  17. package/modules/provisioning/capacity.js +1 -1
  18. package/modules/provisioning/provisioning.js +4 -3
  19. package/modules/provisioning/routes/provisioning.js +4 -4
  20. package/modules/sessions/db.js +1 -1
  21. package/package-lock.json +2 -2
  22. package/package.json +1 -1
  23. package/release-notes.json +6 -0
  24. package/scripts/gds/artifact-staleness.js +2 -2
  25. package/scripts/gds/backfill-task-rewards.js +5 -5
  26. package/scripts/gds/claude-materialize.js +2 -2
  27. package/scripts/gds/cli-lib.js +7 -7
  28. package/scripts/gds/codemod-rename-src-gds.js +1 -1
  29. package/scripts/gds/context-pack.js +5 -7
  30. package/scripts/gds/doctor.js +3 -3
  31. package/scripts/gds/fitness-checks-packaging.js +4 -4
  32. package/scripts/gds/fitness-ratchets.js +1 -1
  33. package/scripts/gds/gen-api-client.js +4 -4
  34. package/scripts/gds/gen-api-docs.js +1 -1
  35. package/scripts/gds/gen-repo-map.js +13 -14
  36. package/scripts/gds/gen-session-index.js +8 -8
  37. package/scripts/gds/leak-scan-allowlist.js +1 -1
  38. package/scripts/gds/plain-cards.js +1 -1
  39. package/scripts/gds/push-path-brief.js +10 -10
  40. package/scripts/gds/rename-history-check.js +1 -1
  41. package/scripts/gds/session-digest-build.js +2 -2
  42. package/scripts/gds/ship-deploy-target.js +13 -13
  43. package/scripts/gds/ship-flow.js +1 -1
  44. package/scripts/gds/ship-land.js +6 -6
  45. package/scripts/gds/ship-merge.js +4 -4
  46. package/scripts/gds/ship-preflight-steps.js +4 -4
  47. package/scripts/gds/ship-regen.js +9 -12
  48. package/scripts/gds/ship.js +9 -9
  49. package/scripts/gds/skill-preflight.js +9 -8
  50. package/src/bongos/api-prefix.js +4 -4
  51. package/src/bongos/platform-visibility-gate.js +1 -1
  52. package/src/bongos/routes/me.js +1 -1
  53. package/src/bongos/routes/security.js +3 -3
  54. package/src/bongos/routes.js +2 -2
  55. package/src/bongos/serve-internal.js +5 -6
  56. package/src/bongos-downloads.js +1 -6
  57. package/src/branding.js +2 -2
  58. package/src/build-info.js +4 -6
  59. package/src/instance-config.js +2 -2
  60. package/src/module-api.js +3 -3
  61. package/src/module-loader/manifest-schema.js +1 -1
  62. package/tests/api_alias_caller_ratchet.mjs +1 -1
  63. package/tests/api_alias_exceptions.mjs +1 -1
  64. package/tests/api_client.mjs +1 -1
  65. package/tests/api_path_404.mjs +1 -1
  66. package/tests/backfill_task_rewards.mjs +1 -1
  67. package/tests/{box_ship_permissions.mjs → cli_allowlist_permissions.mjs} +5 -5
  68. package/tests/cli_sessions.mjs +1 -1
  69. package/tests/context_pack.mjs +13 -13
  70. package/tests/copy_inventory.mjs +7 -7
  71. package/tests/credit_grant.mjs +2 -2
  72. package/tests/criterion_suggest.mjs +1 -1
  73. package/tests/design_tokens_sync.mjs +3 -2
  74. package/tests/fitness_ratchets.mjs +2 -2
  75. package/tests/github_push_land_proof.mjs +1 -1
  76. package/tests/goal_advisory.mjs +3 -3
  77. package/tests/goal_suggest.mjs +3 -3
  78. package/tests/government_abuse_matrix.mjs +4 -3
  79. package/tests/hall_audit.mjs +1 -1
  80. package/tests/helpers.mjs +1 -1
  81. package/tests/host_topology_skips.mjs +2 -2
  82. package/tests/migration_namespace.mjs +6 -6
  83. package/tests/module_api.mjs +2 -2
  84. package/tests/module_cli.mjs +5 -5
  85. package/tests/module_manifest.mjs +13 -13
  86. package/tests/module_route_rank.mjs +2 -2
  87. package/tests/modules_route_wiring.mjs +1 -1
  88. package/tests/onboarding_route_signals.mjs +1 -1
  89. package/tests/paste_token_session_store.mjs +1 -1
  90. package/tests/platform_visibility_gate.mjs +1 -1
  91. package/tests/project_modules_ui.mjs +17 -17
  92. package/tests/projects_hub_module_picker.mjs +75 -74
  93. package/tests/provisioning_capacity.mjs +1 -1
  94. package/tests/provisioning_recommendations.mjs +2 -1
  95. package/tests/publish_branch_route.mjs +1 -1
  96. package/tests/publish_manifest.mjs +1 -1
  97. package/tests/publish_reconciler.mjs +8 -8
  98. package/tests/push_path_brief.mjs +10 -9
  99. package/tests/regrade_eligibility.mjs +1 -1
  100. package/tests/rename_history_restraint.mjs +1 -1
  101. package/tests/repo_map.mjs +9 -9
  102. package/tests/seam_wiring_guard.mjs +11 -11
  103. package/tests/search_isolation.mjs +3 -3
  104. package/tests/session_records.mjs +1 -1
  105. package/tests/session_rename_fallback.mjs +1 -1
  106. package/tests/session_start_freshness.mjs +3 -3
  107. package/tests/session_token_hash_db.mjs +1 -1
  108. package/tests/ship_card.mjs +1 -1
  109. package/tests/ship_ci_deploy.mjs +17 -17
  110. package/tests/ship_premerge.mjs +1 -1
  111. package/tests/ship_resume.mjs +1 -1
  112. package/tests/skill_preflight.mjs +2 -2
  113. package/tests/skip_is_not_pass.mjs +1 -1
  114. package/tests/terms_acceptance.mjs +1 -1
@@ -4,12 +4,13 @@
4
4
  // 1000077; criterion guards-skill-declares-machine).
5
5
  //
6
6
  // THE INCIDENT. /merge-mode states in its own prose that it assumes the Archon's
7
- // Mac or the droplet. It was run on a dev box anyway. Worse, ship.js ALREADY knew:
8
- // strandNextStep() tells a box "/merge-mode is NOT available here — it needs gh +
9
- // a main checkout this machine doesn't have." So the knowledge existed in two
10
- // places and neither stopped the run, because prose cannot refuse and the one
11
- // executable copy was on a different code path. A capability a procedure needs
12
- // must be a declaration the machine can CHECK, not a sentence a reader can skim.
7
+ // Mac or the droplet. It was run on a machine with no push credential anyway. Worse,
8
+ // ship.js ALREADY knew: strandNextStep() tells such a machine "/merge-mode is NOT
9
+ // available here — it needs gh + a main checkout this machine doesn't have." So the
10
+ // knowledge existed in two places and neither stopped the run, because prose cannot
11
+ // refuse and the one executable copy was on a different code path. A capability a
12
+ // procedure needs must be a declaration the machine can CHECK, not a sentence a
13
+ // reader can skim.
13
14
  //
14
15
  // THREE PARTS, matching that diagnosis:
15
16
  // 1. `requires:` in the skill's own YAML frontmatter — the declaration.
@@ -74,7 +75,7 @@ const CAPABILITIES = {
74
75
  what: 'an authenticated GitHub CLI',
75
76
  probe: () => ghAuthed(),
76
77
  missing: 'no authenticated `gh` on this machine. Run `gh auth login`, or use a machine that has it — '
77
- + 'a dev box normally does not, by design (it publishes through the server instead, ADR 0055).',
78
+ + 'a machine with no push credential normally does not, by design (it publishes through the server instead, ADR 0055).',
78
79
  },
79
80
  'main-checkout': {
80
81
  what: 'the main checkout, not a linked worktree',
@@ -318,7 +319,7 @@ function checkOpsSkillsDeclareMachine({ dir = SKILLS_DIR, read = null, names: in
318
319
  violations.push(
319
320
  `${n} fences commands needing [${undeclared.join(', ')}] but does not declare them. `
320
321
  + 'Add them to `requires:` in the frontmatter so the preflight can refuse early — '
321
- + 'prose cannot refuse, which is how /merge-mode came to be run on a dev box (task 1003167).'
322
+ + 'prose cannot refuse, which is how /merge-mode came to be run on a machine that could not run it (task 1003167).'
322
323
  );
323
324
  }
324
325
  }
@@ -9,10 +9,10 @@
9
9
  //
10
10
  // The URL rename (Part B / B1, task 1919) flipped the canonical here: the API is
11
11
  // now served at '/api/bongos', and '/api/gds' stays mounted FOREVER as a silent
12
- // alias (LEGACY_API_PREFIXES). "Forever" is deliberate — shipped Dev Box desktop
13
- // binaries, the separately-hosted status-dashboard mirror, and live dev boxes all
14
- // call '/api/gds' and can't be force-updated. serve-internal.js mounts the router
15
- // + discovery index at API_PREFIX and every LEGACY_API_PREFIXES entry.
12
+ // alias (LEGACY_API_PREFIXES). "Forever" is deliberate — shipped older CLI
13
+ // releases and the separately-hosted status-dashboard mirror all call '/api/gds'
14
+ // and can't be force-updated. serve-internal.js mounts the router + discovery
15
+ // index at API_PREFIX and every LEGACY_API_PREFIXES entry.
16
16
  const API_PREFIX = '/api/bongos';
17
17
 
18
18
  // Prefixes the server ALSO answers on, for callers pinned to the previous path.
@@ -65,7 +65,7 @@ const API = `(?:${ALL_API_PREFIXES.map(escRe).join('|')})`;
65
65
  // Caddy's on-demand-TLS `ask` and the provisioning probe —
66
66
  // server-to-server, cookie-less, and on a control plane a
67
67
  // refused ask means no project ever gets a certificate.
68
- // /downloads/* the installer script and the Dev Box updater — a
68
+ // /downloads/* the installer script and the release downloads — a
69
69
  // cookie-less `curl … | sh`, and release redirects carry
70
70
  // no project data.
71
71
  // /__prelaunch the pre-launch gate's own submit, when both gates are on.
@@ -510,7 +510,7 @@ module.exports = function buildMeRouter() {
510
510
  };
511
511
  // Module→core contribution seam (BV1.R42, ADR 0083). Let each enabled module
512
512
  // add its own profile slice instead of core hardcoding it — the strangler
513
- // prerequisite that lets dev-box/discord/art leave this route. ADD-ONLY (core
513
+ // prerequisite that lets discord/art leave this route. ADD-ONLY (core
514
514
  // keys win); a collision means a move task took over a core key without
515
515
  // dropping the core copy. Empty until a module registers → byte-identical today.
516
516
  const slices = await seams.collectContributions('profile.fields', {
@@ -113,9 +113,9 @@ module.exports = function buildSecurityRouter() {
113
113
  //
114
114
  // The DATED security engagement reports (exploit detail — full repro,
115
115
  // evidence, the attacker cookbook) are served from the DB, NOT committed to
116
- // the git clone: the repo is cloned in full onto every Metic+ dev box
117
- // (threat-model §6.3 / ADR 0020), so a committed exploit report is an
118
- // attacker cookbook on every box. The SAFE conceptual docs (threat-model.md,
116
+ // the git clone: the repo is cloned in full onto every Metic+ builder's
117
+ // machine (threat-model §6.3 / ADR 0020), so a committed exploit report is an
118
+ // attacker cookbook on every machine. The SAFE conceptual docs (threat-model.md,
119
119
  // the README) stay in docs/security/; only the dated reports live here, and
120
120
  // ALL three routes gate on requirePermission('security.report.adjudicate')
121
121
  // — an archon-floored atom (BV1.R104; was requireRank('archon') and admits
@@ -167,8 +167,8 @@ function buildGdsRouter() {
167
167
  // core to every instance, not an OTB feature.
168
168
  router.use(buildLiveRouter());
169
169
  // Feature-module-gated mounts (R54, task 1193, ADR 0062 §4). Off on a vanilla
170
- // instance; ON for OTB via config/modules.json so prod is unchanged. dev-box,
171
- // art-pipeline, and discord (with its bug-attachments route) have all graduated
170
+ // instance; ON for OTB via config/modules.json so prod is unchanged. art-pipeline
171
+ // and discord (with its bug-attachments route) have both graduated
172
172
  // into real modules (modules/<key>/) — they are discovered + mounted by
173
173
  // moduleLoader.mountModuleRoutes below, not here (BV1.R43 / R48 / R47 / R72).
174
174
  // gate-approvals likewise moved to the lifecycle module (BV1.R86).
@@ -622,8 +622,8 @@ function mountInternalSurfaces(app) {
622
622
  //
623
623
  // Dual-mount (task 1919): the router + this discovery index are mounted at the
624
624
  // canonical API_PREFIX (/api/bongos) AND every LEGACY_API_PREFIXES entry
625
- // (/api/gds) — the legacy alias stays FOREVER so shipped Dev Box binaries, the
626
- // cached status mirror, and live boxes keep working (src/bongos/api-prefix.js). The
625
+ // (/api/gds) — the legacy alias stays FOREVER so older CLI releases and the
626
+ // cached status mirror keep working (src/bongos/api-prefix.js). The
627
627
  // SAME router instance is mounted at each path. Each discovery GET is registered
628
628
  // BEFORE its router mount so the exact base-path GET wins; every other
629
629
  // method/subpath falls through to the router.
@@ -670,7 +670,7 @@ function mountInternalSurfaces(app) {
670
670
  // below only prefixes non-/api paths, so without this placement /downloads/*
671
671
  // would be rewritten to /builders/downloads/* and 404). The installer scripts are served with the
672
672
  // server's PINNED origin substituted for __GDS_API_BASE__ — never a request
673
- // header, so a spoofed Host can't redirect a `curl | sh` (the box-connect.sh rule).
673
+ // header, so a spoofed Host can't redirect a `curl | sh`.
674
674
  const bongosCfg = tryLoadInstanceConfig('bongos-cli.json');
675
675
  if (bongosCfg) {
676
676
  const bongosDownloads = buildBongosDownloads(bongosCfg);
@@ -894,8 +894,7 @@ function mountInternalSurfaces(app) {
894
894
  // them to the hall, and an unauthenticated visitor into sign-in). The subdomain
895
895
  // shim above has already rewritten the bare /watch + /gate onto /builders/*.
896
896
  // (Defense in depth: every data endpoint these pages call carries its own
897
- // server-side gate.) /harbor sat here too until task 1003890 removed the dev
898
- // box: its page is gone, and a gate in front of nothing is a dead guard.
897
+ // server-side gate.)
899
898
  const WATCH_PAGE_RE = /^\/builders\/watch(?:\.html)?\/?$/;
900
899
  const GATE_PAGE_RE = /^\/builders\/gate(?:\.html)?\/?$/;
901
900
  // /sessions (the session-log ledger) belongs to that same Governance group and
@@ -1144,7 +1143,7 @@ function mountInternalSurfaces(app) {
1144
1143
  // the landing card + GitHub button render for a stranger.
1145
1144
  //
1146
1145
  // ONE tier of gate: ANY SIGNED-IN BUILDER — the Work Board, Roadmap, the Goals
1147
- // page, the task-detail page, the Repo Atlas, the Dev Box pair page, Settings
1146
+ // page, the task-detail page, the Repo Atlas, Settings
1148
1147
  // (#748, #904, task 1293, task 1757, task 751) and, since task 1002710, the
1149
1148
  // orientation reading rooms (primer, diagrams). A Xenos IS allowed; an
1150
1149
  // unauthenticated visitor is bounced into sign-in rather than served the shell.
@@ -4,8 +4,7 @@
4
4
  // The compiled per-platform binaries can't ride the git-based deploy (private
5
5
  // repo, no LFS, GitHub's 100MB cap), so CI publishes them as release assets on a
6
6
  // PUBLIC releases repo (config/bongos-cli.json — the same off-repo pattern as the
7
- // Dev Box app, src/devbox-downloads.js, and the ADR 0029 status mirror). This
8
- // module turns that committed config into:
7
+ // ADR 0029 status mirror). This module turns that committed config into:
9
8
  //
10
9
  // GET /downloads/bongos/latest.json → { version, assets: { target: {file,url} } }
11
10
  // GET /downloads/bongos/version → { version, assets: { target: url } }
@@ -14,10 +13,6 @@
14
13
  // so every URL a user sees or the installer fetches stays on the instance apex,
15
14
  // and a release bump is a one-file config change that rides the normal ship flow.
16
15
  // Pure (config in, urls out) so tests need no server.
17
- //
18
- // (A near-twin of src/devbox-downloads.js by design — same off-repo release
19
- // pattern, different artifact. Kept as its own module for a clear name + its own
20
- // config pointer; the two are independent instance features.)
21
16
 
22
17
  function buildBongosDownloads(cfg) {
23
18
  if (!cfg || typeof cfg !== 'object') throw new Error('bongos-downloads: missing config');
package/src/branding.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/branding.js — the branding contract (ADR 0062 §3, task 1192 / V4.R53).
2
2
  //
3
3
  // The single source for every instance-identity string the platform renders:
4
- // product/world name, company, domains/origins, cookie + OAuth + dev-box
4
+ // product/world name, company, domains/origins, cookie + OAuth
5
5
  // hosts, currency label, the track set, theme, and the project name injected
6
- // into LLM prompts. Generalizes devbox-app/src/brand.js to the whole platform.
6
+ // into LLM prompts.
7
7
  //
8
8
  // Resolution precedence (highest wins):
9
9
  // env override -> instance config (config/branding.json) -> neutral
package/src/build-info.js CHANGED
@@ -41,12 +41,10 @@ function readGitSha() {
41
41
  }
42
42
  }
43
43
 
44
- // The deployed commit's date (ISO 8601), for the dev-box staleness comparison
45
- // (boxes.computeCodeStaleness, idea 332). A box reports its own HEAD commit date;
46
- // if it's > 2 days behind THIS, the box is flagged stale. Best-effort: after a
47
- // `git reset --hard origin/main` deploy, HEAD is the deployed commit, so its
48
- // committer date is the right reference. Null when git is unavailable (then the
49
- // "behind" check is skipped and only never-reported boxes flag).
44
+ // The deployed commit's date (ISO 8601), served on /version beside the commit.
45
+ // Best-effort: after a `git reset --hard origin/main` deploy, HEAD is the
46
+ // deployed commit, so its committer date is the right reference. Null when git
47
+ // is unavailable.
50
48
  function readGitCommitDate() {
51
49
  try {
52
50
  return execSync('git show -s --format=%cI HEAD', {
@@ -40,7 +40,7 @@ const LEGACY_DIRS = ['otb'];
40
40
  //
41
41
  // WHY THESE MUST OUTLIVE THIS TASK: these names are set OUTSIDE the repo —
42
42
  // /etc/cloudbongos/web.env on the droplet, systemd units, CI secrets, shipped
43
- // Dev Box binaries, live dev boxes. Deleting a spelling here is an outage the
43
+ // CLI binaries, builders' own machines. Deleting a spelling here is an outage the
44
44
  // next time one of those restarts. Task 1003706 (C3d) retires them deliberately,
45
45
  // after evidence — and asserts their ABSENCE, which is why the fallback needs a
46
46
  // test that it FIRES rather than one that merely tolerates it.
@@ -140,7 +140,7 @@ function configPath(name, b = safeBrand()) {
140
140
  // Lives here, not in cli-lib.js which owns the session STORE, because this module
141
141
  // is the lowest one that has to read the file (resolveApiBase below) and cli-lib
142
142
  // imports this — so this is the only place both can share. A rename APPENDS a
143
- // name; dropping one signs out every builder machine and every live dev box at
143
+ // name; dropping one signs out every builder machine at
144
144
  // once. Part 4 (task 1003706) retires the tail. (task 1003704)
145
145
  const SESSION_FILENAMES = Object.freeze([
146
146
  'bongos-session.json',
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.19.1074'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.19.1075'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -137,8 +137,8 @@ module.exports = {
137
137
  get requireBfgPrincipal() { return require('./bongos/auth').requireBfgPrincipal; },
138
138
  // allowBoxScope — DEPRECATED no-op, kept so the doorway loses no export inside
139
139
  // 1.x (removing one is a MAJOR, and every module declares ^1). It used to opt
140
- // a route in to the narrowing box-scoped session; that scope and its tokens
141
- // were retired (task 1003894), so an undeclared session source is now denied
140
+ // a route in to the narrowing machine-baked session scope; that scope and its
141
+ // tokens were retired (goal 1000120, task 1003894), so an undeclared session source is now denied
142
142
  // on every route whatever a route mounts. Passing it changes nothing. Drop it
143
143
  // at the next MAJOR.
144
144
  get allowBoxScope() { return deprecatedNoopMiddleware; },
@@ -10,7 +10,7 @@
10
10
  // THE SCHEMA (module.json):
11
11
  // {
12
12
  // "key": "art-pipeline", // REQUIRED. ^[a-z][a-z0-9-]*$ ; must match the module's dir name.
13
- // "title": "Dev boxes", // REQUIRED. Human label.
13
+ // "title": "Art pipeline", // REQUIRED. Human label.
14
14
  // "description": "…", // REQUIRED. One sentence.
15
15
  // "version": "1.4.2", // REQUIRED. The module's OWN exact version "X.Y.Z" (task 1003781) —
16
16
  // // what update, rollout channels and re-assessment name.
@@ -66,7 +66,7 @@ test('the exclusions are real and each one is reachable', () => {
66
66
  'scripts/gds/fitness-ratchets.js',
67
67
  'scripts/gds/api-alias-caller-scan.js',
68
68
  'tests/api_alias_exceptions.mjs', // 5 tests
69
- 'modules/dev-box/app/test/thing.mjs',
69
+ 'modules/agents/app/test/thing.mjs',
70
70
  'docs/adr/0016-permissions.md', // 6 the FROZEN RECORD under docs/ (narrowed, task 1004113)
71
71
  'docs/session-logs/2026-09-16-x.md',
72
72
  'docs/audits/2026-08-01-x.md',
@@ -27,7 +27,7 @@ const ALIAS = '/api/gds';
27
27
  test('the alias is still mounted — everything below depends on it', () => {
28
28
  const { API_PREFIX, LEGACY_API_PREFIXES, ALL_API_PREFIXES } = require(path.join(ROOT, 'src', 'bongos', 'api-prefix.js'));
29
29
  assert.equal(API_PREFIX, '/api/bongos', 'the canonical prefix is what in-repo callers now ask for');
30
- assert.ok(LEGACY_API_PREFIXES.includes(ALIAS), 'shipped Dev Box binaries and live dev boxes call the alias');
30
+ assert.ok(LEGACY_API_PREFIXES.includes(ALIAS), 'shipped binaries and live instances call the alias');
31
31
  assert.ok(ALL_API_PREFIXES.includes(ALIAS), 'and the router still mounts it');
32
32
  });
33
33
 
@@ -208,7 +208,7 @@ test('generated .d.ts types success responses (task 2061)', () => {
208
208
  });
209
209
 
210
210
  // The CJS + browser-global builds are pure transforms of the ESM source (task
211
- // 1996 / R09). devbox (require) and classic <script> web pages consume them, so
211
+ // 1996 / R09). CommonJS callers (require) and classic <script> web pages consume them, so
212
212
  // guard that they load and shape requests IDENTICALLY to the ESM build.
213
213
  test('CJS build behaves identically to the ESM build', async () => {
214
214
  const cjs = require('../clients/bongos-client/index.cjs');
@@ -38,7 +38,7 @@ t('API-shaped: /api/*, /gds/*, and bare GDS roots (/tasks, /me, …) match', ()
38
38
 
39
39
  t('NOT API-shaped: game/hall/static paths fall through to HTML/static', () => {
40
40
  for (const p of ['/', '/index.html', '/builders', '/builders/work', '/atlas-ab12.png',
41
- '/vendor/phaser.min.js', '/downloads/devbox/mac', '/game', '/healthz', '/version']) {
41
+ '/vendor/phaser.min.js', '/downloads/bongos/latest.json', '/game', '/healthz', '/version']) {
42
42
  assert.equal(isApiShapedPath(p), false, `${p} should NOT be API-shaped`);
43
43
  }
44
44
  });
@@ -77,7 +77,7 @@ await test('computeTaskReward: 0 raw → 0 effective (a 0-mapping pays nothing)'
77
77
 
78
78
  await test('netForBuilder: would-be minus already-paid', () => {
79
79
  assert.equal(netForBuilder(100, 30), 70);
80
- assert.equal(netForBuilder(100, 0), 100); // cost-plus paid nothing (dev-box/CI case)
80
+ assert.equal(netForBuilder(100, 0), 100); // cost-plus paid nothing (the CI case)
81
81
  });
82
82
 
83
83
  await test('netForBuilder: cost-plus over-paid → floored at 0 (never claws back)', () => {
@@ -1,7 +1,7 @@
1
- // tests/box_ship_permissions.mjs
1
+ // tests/cli_allowlist_permissions.mjs
2
2
  //
3
3
  // task 1051 (D): the blessed methodology CLI scripts must stay pre-allowed in the
4
- // project .claude/settings.json so a dev-box Claude session (auto permission mode)
4
+ // project .claude/settings.json so a Claude session in auto permission mode
5
5
  // does not hard-DENY the very command it exists to run. The #1027 nightmare
6
6
  // dead-ended on `node scripts/gds/ship.js` at the auto classifier; this guards
7
7
  // against that allow-rule being silently dropped.
@@ -12,7 +12,7 @@
12
12
  // on every routine read. The list of tools is `.claude/hooks/shell-tools.js`, the same
13
13
  // one the guards read: a rule may only exist for a tool git-guard and secret-gate see.
14
14
  //
15
- // Run: node tests/box_ship_permissions.mjs
15
+ // Run: node tests/cli_allowlist_permissions.mjs
16
16
 
17
17
  import { strict as assert } from 'node:assert';
18
18
  import fs from 'node:fs';
@@ -62,7 +62,7 @@ perTool('permissions.allow pre-allows the generic GDS API channel api.js (auto-m
62
62
  // idea-triage promote, create-task, blocker-resolve, criterion ratify, goal
63
63
  // create, cost-via-API, security reports — rides the generic caller
64
64
  // `node scripts/gds/api.js <METHOD> /api/gds/...`. If it is not pre-allowed, a
65
- // dev-box (or laptop) session in AUTO permission mode has the classifier
65
+ // session in AUTO permission mode has the classifier
66
66
  // hard-DENY those writes as "[External System Writes] ... beyond the user's
67
67
  // request" — mistaking a sanctioned idea-triage session for scope creep. The
68
68
  // GDS server enforces builders.rank on every request (ADR 0016), so this rule
@@ -309,5 +309,5 @@ test('the instance starter mirrors this list in verb form (claude-materialize.js
309
309
  });
310
310
 
311
311
  console.log('');
312
- console.log(`box_ship_permissions: ${passed} passed, ${failed} failed`);
312
+ console.log(`cli_allowlist_permissions: ${passed} passed, ${failed} failed`);
313
313
  process.exit(failed === 0 ? 0 : 1);
@@ -201,7 +201,7 @@ test('signing into a second instance PRESERVES the first — including one writt
201
201
  });
202
202
 
203
203
  test('the ACTIVE pointer still lands exactly where every existing reader looks', () => {
204
- // In-repo skills, hooks and the dev box all read the active session file in the configured dir. The
204
+ // In-repo skills and hooks all read the active session file in the configured dir. The
205
205
  // store is additive; if this moved, every one of them would silently stop finding a session.
206
206
  const { out } = inSandbox(`
207
207
  await lib.saveSession({ token:'T', api_base:'https://a.example.com', builder:{github_login:'x'} });
@@ -339,14 +339,14 @@ t('formatContextPackCompact: two lines, worktree + prefix, no undefined', () =>
339
339
  assert.ok(!/undefined/.test(out));
340
340
  });
341
341
 
342
- // ---- the task-scoped box with no claim (task 1003085) ----------------------
342
+ // ---- a checkout with no modules/lifecycle on disk (task 1003085) ------------
343
343
  //
344
- // A box scoped to a claim materializes modules/<key> only for the modules that
345
- // claim put in scope (ADR 0148). With NO active claim, modules/lifecycle is not
346
- // on disk — and this file's hard `require` of task-classifier threw
347
- // MODULE_NOT_FOUND at IMPORT time, which killed /builder-start and
348
- // /builder-claim: the two commands needed to escape the state, because a claim
349
- // is what fetches the module. The live escape was POST /claims by curl.
344
+ // When modules/lifecycle is not on disk (it was found on a checkout that held only
345
+ // the modules an active claim put in scope, so with no claim it was absent), this
346
+ // file's hard `require` of task-classifier threw MODULE_NOT_FOUND at IMPORT time,
347
+ // which killed /builder-start and /builder-claim: the two commands needed to escape
348
+ // the state, because a claim is what fetches the module. The live escape was
349
+ // POST /claims by curl.
350
350
  //
351
351
  // This reproduces the condition for real rather than asserting on source text:
352
352
  // a temp tree holding context-pack.js and the one other thing it imports, with
@@ -357,7 +357,7 @@ const os = require('node:os');
357
357
  const path = require('node:path');
358
358
  const url = require('node:url');
359
359
 
360
- function boxWithoutLifecycle() {
360
+ function checkoutWithoutLifecycle() {
361
361
  const here = path.dirname(url.fileURLToPath(import.meta.url));
362
362
  const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ctxpack-nolifecycle-'));
363
363
  fs.mkdirSync(path.join(root, 'scripts', 'gds'), { recursive: true });
@@ -369,13 +369,13 @@ function boxWithoutLifecycle() {
369
369
  // coupling has to be looked at — which is the point.
370
370
  fs.copyFileSync(path.join(here, '..', 'src', 'bongos', 'api-prefix.js'),
371
371
  path.join(root, 'src', 'bongos', 'api-prefix.js'));
372
- // No modules/ — that IS the box condition.
372
+ // No modules/ — that IS the condition.
373
373
  assert.ok(!fs.existsSync(path.join(root, 'modules')), 'the fixture must not carry modules/');
374
374
  return { root, mod: require(path.join(root, 'scripts', 'gds', 'context-pack.js')) };
375
375
  }
376
376
 
377
- t('a box with no modules/lifecycle can still LOAD the pack (the deadlock)', () => {
378
- const { mod } = boxWithoutLifecycle();
377
+ t('a checkout with no modules/lifecycle can still LOAD the pack (the deadlock)', () => {
378
+ const { mod } = checkoutWithoutLifecycle();
379
379
  const api = mod.apiContract();
380
380
  assert.equal(api.enums_available, false, 'it must say so, not pretend');
381
381
  assert.equal(api.api_prefix, '/api/bongos', 'everything not behind the module still works');
@@ -386,8 +386,8 @@ t('a box with no modules/lifecycle can still LOAD the pack (the deadlock)', () =
386
386
  assert.equal(api.manual_degree, null);
387
387
  });
388
388
 
389
- t('both formatters render on that box instead of throwing', () => {
390
- const { mod } = boxWithoutLifecycle();
389
+ t('both formatters render on that checkout instead of throwing', () => {
390
+ const { mod } = checkoutWithoutLifecycle();
391
391
  const pack = mod.buildContextPack({
392
392
  activeClaims: [], cwd: '/fallback', platform: 'linux',
393
393
  runGit: () => '/work/tree\n', runWhich: () => '',
@@ -29,11 +29,11 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
29
29
  const inv = require(path.join(ROOT, 'scripts', 'gds', 'copy-inventory.js'));
30
30
 
31
31
  test('stable ids survive a move and change with the text', () => {
32
- const a = inv.stableId('builders-hall', 'Close your box');
33
- assert.equal(a, inv.stableId('builders-hall', 'Close your box'), 'same surface + text → same id, whatever the file/line');
34
- assert.equal(a, inv.stableId('builders-hall', ' Close your box '), 'whitespace is normalized before hashing');
35
- assert.notEqual(a, inv.stableId('builders-hall', 'Close your dev box'), 'a text change must break the id — that is the signal a flag is stale');
36
- assert.notEqual(a, inv.stableId('status-dashboard', 'Close your box'), 'the same words on another surface are a different piece of copy');
32
+ const a = inv.stableId('builders-hall', 'Close your tab');
33
+ assert.equal(a, inv.stableId('builders-hall', 'Close your tab'), 'same surface + text → same id, whatever the file/line');
34
+ assert.equal(a, inv.stableId('builders-hall', ' Close your tab '), 'whitespace is normalized before hashing');
35
+ assert.notEqual(a, inv.stableId('builders-hall', 'Close your browser tab'), 'a text change must break the id — that is the signal a flag is stale');
36
+ assert.notEqual(a, inv.stableId('status-dashboard', 'Close your tab'), 'the same words on another surface are a different piece of copy');
37
37
  });
38
38
 
39
39
  test('the JS extractor refuses code — the 2144-row failure mode cannot come back', () => {
@@ -55,14 +55,14 @@ test('the JS extractor keeps copy, and names how a person sees it', () => {
55
55
  const src = [
56
56
  "toast('Priority order saved.');",
57
57
  "el.textContent = 'How the whole build is doing.';",
58
- "box.innerHTML = `<p class=\"setup__box-msg\">Closing your box… (a few seconds)</p>`;",
58
+ "panel.innerHTML = `<p class=\"setup__msg\">Closing your session… (a few seconds)</p>`;",
59
59
  "row.innerHTML = `<span class=\"pill\">Release task ${id}</span>`;",
60
60
  ].join('\n');
61
61
  const rows = inv.fromJs({ source: src, rel: 'x.js' });
62
62
  const byOrigin = Object.fromEntries(rows.map((r) => [r.origin, r.text]));
63
63
  assert.equal(byOrigin['js-notice'], 'Priority order saved.');
64
64
  assert.equal(byOrigin['js-text-assign'], 'How the whole build is doing.');
65
- assert.ok(rows.some((r) => r.origin === 'js-markup' && r.text === 'Closing your box… (a few seconds)'),
65
+ assert.ok(rows.some((r) => r.origin === 'js-markup' && r.text === 'Closing your session… (a few seconds)'),
66
66
  'text inside script-built markup is copy; the tags around it are not');
67
67
  assert.ok(rows.some((r) => r.text === 'Release task {…}'),
68
68
  'a template interpolation becomes a placeholder so the sentence stays readable and the hole stays visible');
@@ -435,7 +435,7 @@ await test('1002474 the INSERT still carries a DB-level double-pay backstop', as
435
435
  assert.match(insert.sql, /reward_basis/, 'and it must be keyed on (session_id, reward_basis)');
436
436
  });
437
437
 
438
- // task 1676 — the cost-plus zero-out fix for the dev-box/CI agent ship path.
438
+ // task 1676 — the cost-plus zero-out fix for the CI agent ship path.
439
439
  // Symptom (ADR 0097): those sessions uploaded empty/unpriceable model_usage, so
440
440
  // priceModelUsage returned $0 and the reward rounded to 0 — even though real
441
441
  // token spend happened. The fix prices the session's real total_tokens at the
@@ -444,7 +444,7 @@ await test('awardSessionTokenReward (task 1676): empty model_usage + real total_
444
444
  const client = fakeClient();
445
445
  client.enqueue([]); // idempotency SELECT → no prior row
446
446
  client.enqueue([{ id: 9 }]); // INSERT ... RETURNING id
447
- // The exact dev-box/CI shape: no priceable per-model usage, but the digest
447
+ // The exact CI shape: no priceable per-model usage, but the digest
448
448
  // carried a real total token count (here 73M, an opus-marathon session).
449
449
  const res = await awardSessionTokenReward(client, 7, 'sess-ci', {}, 73_000_000);
450
450
  assert.equal(res.awarded, true, 'reward awarded despite empty model_usage');
@@ -30,7 +30,7 @@ const CRITERIA = [
30
30
  crit(138, 'bx-hall', 'Core task actions work in the builders hall without invoking the LLM.'),
31
31
  crit(139, 'bx-identity', 'A builder can view and edit their own profile and read a leaderboard.'),
32
32
  crit(140, 'bx-onramp', 'Newcomers never dead-end: auth and re-auth are browser-clickable.'),
33
- crit(141, 'bx-box', 'A builder dev box and sandbox is robust: uncommitted work is not lost.'),
33
+ crit(141, 'bx-recovery', 'A builder can resume after a crash: uncommitted work is not lost.'),
34
34
  ];
35
35
 
36
36
  test('ranks the most lexically-relevant criterion first', () => {
@@ -7,7 +7,8 @@
7
7
  // Claude Design round-trip). Nothing tied them together, so the design file kept
8
8
  // the pre-1002749 palette — including the exact accents removed for failing WCAG
9
9
  // AA — and every prototype started from colours the product no longer paints.
10
- // Model: the fallback-equals-pack assertions in tests/devbox_dmg_backdrop.mjs.
10
+ // Model: fallback-equals-pack assertions — a copy of the pack's values is asserted
11
+ // equal to the pack, never trusted.
11
12
  //
12
13
  // Run: node tests/design_tokens_sync.mjs
13
14
 
@@ -25,7 +26,7 @@ const tokens = readJson('config', 'design-tokens.neutral.json');
25
26
 
26
27
  // Strip comments so rationale prose — which must be free to name a removed
27
28
  // colour — never trips a scan of the CODE (the stripCommentsForIdentity
28
- // contract, as applied in tests/devbox_dmg_backdrop.mjs).
29
+ // contract).
29
30
  function codeOnly(text) {
30
31
  return text
31
32
  .replace(/\/\*[\s\S]*?\*\//g, '')
@@ -161,8 +161,8 @@ test('the committed baseline file parses and covers every hermetic metric', () =
161
161
  // `floor === undefined`, which compareToBaselines classifies as a WARNING — so a
162
162
  // checkout with no config/ reported the ratchet PASSED. Measured before the fix: a
163
163
  // planted 5,000-line + 94-cycle regression produced ZERO violations. That was not a
164
- // hypothetical checkout — config/ is absent from every dev box's sparse slice, so the
165
- // builders who most needed the gate were the ones silently exempt from it.
164
+ // hypothetical checkout — a sparse checkout without config/ once left the builders
165
+ // who most needed the gate silently exempt from it.
166
166
  //
167
167
  // checkQualityRatchets() reads the real repo, so these tests drive compareToBaselines
168
168
  // (pure) for the metric-level rules and exercise the file-level rule by pointing at a
@@ -154,7 +154,7 @@ test('landProvenForTask: a non-sha published tip ("HEAD") falls back to the PR c
154
154
 
155
155
  // --- task 1002645: findTaskPullRequest — the PR-title fallback locator --------
156
156
  //
157
- // The reconciler derives claude/<worktree_name> (dev-box convention); a laptop/wave
157
+ // The reconciler derives claude/<worktree_name> first; a laptop/wave
158
158
  // ship pushes worktree-<worktree_name>, so the derived branch finds no PR and the
159
159
  // sweep never engaged (wave PRs 34/35/36 stranded at 'confirmed'). This locates the
160
160
  // task's PR by the "task <id>: …" TITLE-PREFIX convention — deliberately TIGHTER
@@ -72,16 +72,16 @@ await test("kind='bug' with no goal at all is quiet too — a fix needs no home
72
72
  });
73
73
 
74
74
  await test('a REAL goal is silent — null, not an empty object', () => {
75
- assert.equal(adviseGoal({ versionId: V, goalId: 1000025, goalTitle: 'Builder experience — hall, dev box, and skills' }), null);
75
+ assert.equal(adviseGoal({ versionId: V, goalId: 1000025, goalTitle: 'Builder experience — hall, onboarding, and skills' }), null);
76
76
  });
77
77
 
78
78
  await test("kind='bug' under a real goal is silent too — already silent, kind changes nothing", () => {
79
- assert.equal(adviseGoal({ versionId: V, goalId: 1000025, goalTitle: 'Builder experience — hall, dev box, and skills', kind: 'bug' }), null);
79
+ assert.equal(adviseGoal({ versionId: V, goalId: 1000025, goalTitle: 'Builder experience — hall, onboarding, and skills', kind: 'bug' }), null);
80
80
  });
81
81
 
82
82
  // ---- adviseGoal: the third reason — no_criterion_link (task 1003157) ----
83
83
 
84
- const REAL_GOAL_TITLE = 'Builder experience — hall, dev box, and skills';
84
+ const REAL_GOAL_TITLE = 'Builder experience — hall, onboarding, and skills';
85
85
  const SOME_CRITERIA = [
86
86
  { id: 11, criterion_md: 'Every onboarding page renders under 2s' },
87
87
  { id: 12, criterion_md: 'The hall shows a builder their own karma trend' },
@@ -30,7 +30,7 @@ const { test, summary } = makeRunner();
30
30
  const V = 'BONGOS-V1';
31
31
  const GOALS = [
32
32
  { id: 1000024, title: 'Build-pipeline integrity', scope_modules: ['lifecycle', 'grading'] },
33
- { id: 1000025, title: 'Builder experience — hall, dev box, and skills', scope_modules: ['hall-ui', 'dev-box'] },
33
+ { id: 1000025, title: 'Builder experience — hall, tool box, and skills', scope_modules: ['hall-ui', 'specialities'] },
34
34
  { id: 1000071, title: 'Idea routing — work that has a home lands in it', scope_modules: ['ideas', 'discord'] },
35
35
  { id: 1000003, title: `${V} — general`, scope_modules: [] },
36
36
  ];
@@ -76,8 +76,8 @@ await test('RANKS: ties break by ascending id so a picker renders stably', () =>
76
76
 
77
77
  await test('LIMIT (known + accepted): token overlap can match on an incidental word', () => {
78
78
  // Real example found while building this: "the goals page search BOX is broken"
79
- // scores the DEV BOX goal, because both contain the token 'box'. Bag-of-words
80
- // matching cannot tell "search box" from "dev box".
79
+ // scores the "tool box" goal, because both contain the token 'box'. Bag-of-words
80
+ // matching cannot tell "search box" from "tool box".
81
81
  //
82
82
  // This is NOT hidden — it is why every suggestion carries `score` and
83
83
  // `matched_terms`: a caller can show WHY it matched and a human decides. A
@@ -365,9 +365,10 @@ test('V8: a forced principal grant resolves to nothing (structurally dropped)',
365
365
  });
366
366
 
367
367
  test('V9: an undeclared session source is refused outright, never treated as "full"', () => {
368
- // Rows with source 'box' stay in builder_sessions until task 1003895 deletes them.
369
- // They must be undeclared to BOTH loaders, which is what makes requireBuilder and
370
- // the page gate refuse them (tests/government_session_scope.mjs runs the gate).
368
+ // core_258 (task 1003895) deleted the rows with source 'box' and narrowed the source
369
+ // CHECK to cli/web. 'box' must still be undeclared to BOTH loaders, so a stray row of
370
+ // that source is refused by requireBuilder and the page gate rather than honoured
371
+ // (tests/government_session_scope.mjs runs the gate).
371
372
  for (const source of ['box', 'not-a-source']) {
372
373
  assert.equal(sessionScopes.scopeFor(source), null, `"${source}" must not be a declared session source`);
373
374
  }
@@ -464,7 +464,7 @@ test('the colour-literal guard CATCHES a reintroduction, and tolerates a task re
464
464
  assert.ok('a { color: #a04040; }'.match(re), 'a bare hex in a colour value is caught');
465
465
  assert.ok('x = \'<span style="color:var(--olive,#5b7a4d)">\''.match(colourLiteralRe()),
466
466
  'a hex FALLBACK behind a var() is caught — that is how --olive hid');
467
- assert.equal('// #807: the dev-box panel'.match(colourLiteralRe()), null,
467
+ assert.equal('// #807: the ship panel'.match(colourLiteralRe()), null,
468
468
  'a task reference in a comment is not a colour');
469
469
  assert.equal('a { color: var(--ok); }'.match(colourLiteralRe()), null,
470
470
  'a token is exactly what this rule wants');
package/tests/helpers.mjs CHANGED
@@ -58,7 +58,7 @@ export function makeSqlAwareClient(handler) {
58
58
  // tests/ is PUBLISHABLE, but the paths some tests assert are on
59
59
  // publish-manifest.js's PROTECTED_TOPOLOGY_EXCLUDE list (`.claude/hooks/`,
60
60
  // `.claude/settings.json`, `infra/`, `.github/`, `scripts/deploy/`) or are
61
- // instance-only content (`devbox-app/`, `config/<instance>.json`). A neutral
61
+ // instance-only content (`config/<instance>.json`). A neutral
62
62
  // core checkout carries none of it, so an unconditional load CRASHED the file
63
63
  // at import and red-X'd the unit gate for every consumer.
64
64
  //