@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.
- package/.bongos-core.json +122 -122
- package/bin/bongos.js +1 -3
- package/docs/api/openapi.json +4 -4
- package/docs/module-api-changelog.md +2 -0
- package/modules/copy-desk/routes/copy-desk.js +1 -1
- package/modules/discord/ship-broadcast.js +3 -3
- package/modules/economy/credits.js +2 -2
- package/modules/ideas/projection.js +1 -1
- package/modules/lifecycle/db-ship.js +4 -4
- package/modules/lifecycle/github-push.js +12 -14
- package/modules/lifecycle/publish-reconciler.js +10 -10
- package/modules/lifecycle/routes/tasks.js +8 -8
- package/modules/lifecycle/ship-card.js +1 -1
- package/modules/lifecycle/task-visuals.js +2 -2
- package/modules/memory/routes/memory.js +2 -2
- package/modules/platform-identity/routes/sso.js +3 -2
- package/modules/provisioning/capacity.js +1 -1
- package/modules/provisioning/provisioning.js +4 -3
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/sessions/db.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/artifact-staleness.js +2 -2
- package/scripts/gds/backfill-task-rewards.js +5 -5
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +7 -7
- package/scripts/gds/codemod-rename-src-gds.js +1 -1
- package/scripts/gds/context-pack.js +5 -7
- package/scripts/gds/doctor.js +3 -3
- package/scripts/gds/fitness-checks-packaging.js +4 -4
- package/scripts/gds/fitness-ratchets.js +1 -1
- package/scripts/gds/gen-api-client.js +4 -4
- package/scripts/gds/gen-api-docs.js +1 -1
- package/scripts/gds/gen-repo-map.js +13 -14
- package/scripts/gds/gen-session-index.js +8 -8
- package/scripts/gds/leak-scan-allowlist.js +1 -1
- package/scripts/gds/plain-cards.js +1 -1
- package/scripts/gds/push-path-brief.js +10 -10
- package/scripts/gds/rename-history-check.js +1 -1
- package/scripts/gds/session-digest-build.js +2 -2
- package/scripts/gds/ship-deploy-target.js +13 -13
- package/scripts/gds/ship-flow.js +1 -1
- package/scripts/gds/ship-land.js +6 -6
- package/scripts/gds/ship-merge.js +4 -4
- package/scripts/gds/ship-preflight-steps.js +4 -4
- package/scripts/gds/ship-regen.js +9 -12
- package/scripts/gds/ship.js +9 -9
- package/scripts/gds/skill-preflight.js +9 -8
- package/src/bongos/api-prefix.js +4 -4
- package/src/bongos/platform-visibility-gate.js +1 -1
- package/src/bongos/routes/me.js +1 -1
- package/src/bongos/routes/security.js +3 -3
- package/src/bongos/routes.js +2 -2
- package/src/bongos/serve-internal.js +5 -6
- package/src/bongos-downloads.js +1 -6
- package/src/branding.js +2 -2
- package/src/build-info.js +4 -6
- package/src/instance-config.js +2 -2
- package/src/module-api.js +3 -3
- package/src/module-loader/manifest-schema.js +1 -1
- package/tests/api_alias_caller_ratchet.mjs +1 -1
- package/tests/api_alias_exceptions.mjs +1 -1
- package/tests/api_client.mjs +1 -1
- package/tests/api_path_404.mjs +1 -1
- package/tests/backfill_task_rewards.mjs +1 -1
- package/tests/{box_ship_permissions.mjs → cli_allowlist_permissions.mjs} +5 -5
- package/tests/cli_sessions.mjs +1 -1
- package/tests/context_pack.mjs +13 -13
- package/tests/copy_inventory.mjs +7 -7
- package/tests/credit_grant.mjs +2 -2
- package/tests/criterion_suggest.mjs +1 -1
- package/tests/design_tokens_sync.mjs +3 -2
- package/tests/fitness_ratchets.mjs +2 -2
- package/tests/github_push_land_proof.mjs +1 -1
- package/tests/goal_advisory.mjs +3 -3
- package/tests/goal_suggest.mjs +3 -3
- package/tests/government_abuse_matrix.mjs +4 -3
- package/tests/hall_audit.mjs +1 -1
- package/tests/helpers.mjs +1 -1
- package/tests/host_topology_skips.mjs +2 -2
- package/tests/migration_namespace.mjs +6 -6
- package/tests/module_api.mjs +2 -2
- package/tests/module_cli.mjs +5 -5
- package/tests/module_manifest.mjs +13 -13
- package/tests/module_route_rank.mjs +2 -2
- package/tests/modules_route_wiring.mjs +1 -1
- package/tests/onboarding_route_signals.mjs +1 -1
- package/tests/paste_token_session_store.mjs +1 -1
- package/tests/platform_visibility_gate.mjs +1 -1
- package/tests/project_modules_ui.mjs +17 -17
- package/tests/projects_hub_module_picker.mjs +75 -74
- package/tests/provisioning_capacity.mjs +1 -1
- package/tests/provisioning_recommendations.mjs +2 -1
- package/tests/publish_branch_route.mjs +1 -1
- package/tests/publish_manifest.mjs +1 -1
- package/tests/publish_reconciler.mjs +8 -8
- package/tests/push_path_brief.mjs +10 -9
- package/tests/regrade_eligibility.mjs +1 -1
- package/tests/rename_history_restraint.mjs +1 -1
- package/tests/repo_map.mjs +9 -9
- package/tests/seam_wiring_guard.mjs +11 -11
- package/tests/search_isolation.mjs +3 -3
- package/tests/session_records.mjs +1 -1
- package/tests/session_rename_fallback.mjs +1 -1
- package/tests/session_start_freshness.mjs +3 -3
- package/tests/session_token_hash_db.mjs +1 -1
- package/tests/ship_card.mjs +1 -1
- package/tests/ship_ci_deploy.mjs +17 -17
- package/tests/ship_premerge.mjs +1 -1
- package/tests/ship_resume.mjs +1 -1
- package/tests/skill_preflight.mjs +2 -2
- package/tests/skip_is_not_pass.mjs +1 -1
- 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
|
|
8
|
-
// strandNextStep() tells a
|
|
9
|
-
// a main checkout this machine doesn't have." So the
|
|
10
|
-
// places and neither stopped the run, because prose cannot
|
|
11
|
-
// executable copy was on a different code path. A capability a
|
|
12
|
-
// must be a declaration the machine can CHECK, not a sentence a
|
|
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
|
|
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
|
|
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
|
}
|
package/src/bongos/api-prefix.js
CHANGED
|
@@ -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
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
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
|
|
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.
|
package/src/bongos/routes/me.js
CHANGED
|
@@ -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
|
|
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+
|
|
117
|
-
// (threat-model §6.3 / ADR 0020), so a committed exploit report is an
|
|
118
|
-
// attacker cookbook on every
|
|
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
|
package/src/bongos/routes.js
CHANGED
|
@@ -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.
|
|
171
|
-
//
|
|
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
|
|
626
|
-
// cached status mirror
|
|
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
|
|
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.)
|
|
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,
|
|
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.
|
package/src/bongos-downloads.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
|
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.
|
|
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),
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
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', {
|
package/src/instance-config.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
|
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.
|
|
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
|
|
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": "
|
|
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/
|
|
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
|
|
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
|
|
package/tests/api_client.mjs
CHANGED
|
@@ -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).
|
|
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');
|
package/tests/api_path_404.mjs
CHANGED
|
@@ -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/
|
|
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 (
|
|
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/
|
|
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
|
|
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/
|
|
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
|
-
//
|
|
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(`
|
|
312
|
+
console.log(`cli_allowlist_permissions: ${passed} passed, ${failed} failed`);
|
|
313
313
|
process.exit(failed === 0 ? 0 : 1);
|
package/tests/cli_sessions.mjs
CHANGED
|
@@ -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
|
|
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'} });
|
package/tests/context_pack.mjs
CHANGED
|
@@ -339,14 +339,14 @@ t('formatContextPackCompact: two lines, worktree + prefix, no undefined', () =>
|
|
|
339
339
|
assert.ok(!/undefined/.test(out));
|
|
340
340
|
});
|
|
341
341
|
|
|
342
|
-
// ----
|
|
342
|
+
// ---- a checkout with no modules/lifecycle on disk (task 1003085) ------------
|
|
343
343
|
//
|
|
344
|
-
//
|
|
345
|
-
// claim put in scope
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
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
|
|
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
|
|
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
|
|
378
|
-
const { mod } =
|
|
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
|
|
390
|
-
const { mod } =
|
|
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: () => '',
|
package/tests/copy_inventory.mjs
CHANGED
|
@@ -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
|
|
33
|
-
assert.equal(a, inv.stableId('builders-hall', 'Close your
|
|
34
|
-
assert.equal(a, inv.stableId('builders-hall', ' Close your
|
|
35
|
-
assert.notEqual(a, inv.stableId('builders-hall', 'Close your
|
|
36
|
-
assert.notEqual(a, inv.stableId('status-dashboard', 'Close your
|
|
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
|
-
"
|
|
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
|
|
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');
|
package/tests/credit_grant.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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-
|
|
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:
|
|
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
|
|
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 —
|
|
165
|
-
//
|
|
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>
|
|
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
|
package/tests/goal_advisory.mjs
CHANGED
|
@@ -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,
|
|
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,
|
|
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,
|
|
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' },
|
package/tests/goal_suggest.mjs
CHANGED
|
@@ -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,
|
|
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
|
|
80
|
-
// matching cannot tell "search box" from "
|
|
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
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
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
|
}
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -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
|
|
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 (`
|
|
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
|
//
|