@bongos/core 1.17.0 → 1.17.2
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 +363 -312
- package/.claude/skills/blocker-review/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +2 -0
- package/.claude/skills/builder-exit/SKILL.md +2 -2
- package/.claude/skills/builder-redteam/SKILL.md +3 -3
- package/.claude/skills/builder-setup/SKILL.md +2 -2
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/dev/SKILL.md +1 -1
- package/.claude/skills/idea-triage/SKILL.md +2 -2
- package/.claude/skills/ideate/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/new-project/SKILL.md +3 -0
- package/.claude/skills/paint/SKILL.md +1 -1
- package/CLAUDE.md +11 -238
- package/GOVERNANCE.md +1 -1
- package/README.md +1 -1
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +4 -4
- package/clients/bongos-client/bongos-client.global.js +14 -1
- package/clients/bongos-client/index.cjs +14 -1
- package/clients/bongos-client/index.d.ts +19 -2
- package/clients/bongos-client/index.mjs +11 -1
- package/clients/bongos-client/package.json +1 -1
- package/docs/adr/0001-stack-choice-single-vps.md +1 -1
- package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
- package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
- package/docs/adr/0020-security-model-public-stub.md +1 -1
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
- package/docs/adr/0030-builders-hall-live-updates.md +1 -1
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
- package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
- package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
- package/docs/adr/0044-per-box-live-game-preview.md +1 -1
- package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
- package/docs/adr/0057-container-cost-ledger.md +3 -3
- package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
- package/docs/adr/0096-require-reward-before-workable.md +9 -9
- package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
- package/docs/adr/0100-two-repo-instance-model.md +1 -1
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -0
- package/docs/adr/0134-private-first-npm-distribution.md +20 -6
- package/docs/adr/0135-module-upstream-submission-interim-queue.md +34 -0
- package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
- package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
- package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
- package/docs/adr/README.md +14 -10
- package/docs/api/openapi.json +315 -6
- package/docs/api-reference.md +10 -5
- package/docs/architecture.md +3 -3
- package/docs/branding-contract.md +5 -4
- package/docs/canonical-permissions.md +1 -1
- package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
- package/docs/file-map.md +20 -19
- package/docs/handoff-template.md +2 -2
- package/docs/modules-contract.md +8 -1
- package/docs/onboarding/diagrams/README.md +7 -7
- package/docs/onboarding/drachmae-and-karma.md +18 -18
- package/docs/onboarding/primer.md +9 -9
- package/docs/recipes/audit-replay.md +2 -2
- package/docs/recipes/builder-box-lifecycle.md +2 -2
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/instance-deploy.md +4 -4
- package/docs/recipes/instance-first-run.md +51 -0
- package/docs/recipes/ops-gotchas.md +1 -1
- package/docs/recipes/packaging-the-core.md +5 -5
- package/docs/recipes/private-npm-distribution.md +39 -12
- package/docs/recipes/self-host.md +1 -1
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/upgrading-the-core.md +63 -8
- package/docs/research/cursor-competitive-analysis.md +1 -1
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
- package/migrations/core_191_module_submissions.sql +45 -0
- package/modules/autonomy/CLAUDE.md +11 -45
- package/modules/builder-settings/CLAUDE.md +11 -54
- package/modules/dev-box/CLAUDE.md +11 -34
- package/modules/discord/CLAUDE.md +11 -46
- package/modules/discord/module.json +2 -1
- package/modules/economy/CLAUDE.md +11 -44
- package/modules/economy/achievements.js +9 -1
- package/modules/economy/credits.js +10 -1
- package/modules/grading/CLAUDE.md +11 -38
- package/modules/hall-ui/CLAUDE.md +11 -57
- package/modules/hall-ui/public/builders.js +71 -4
- package/modules/hall-ui/public/goal-inbox.js +191 -0
- package/modules/hall-ui/public/goals-page.js +227 -22
- package/modules/hall-ui/public/goals.html +16 -0
- package/modules/hall-ui/public/hall-widgets.js +14 -1
- package/modules/hall-ui/public/index.html +3 -0
- package/modules/hall-ui/public/new-project-wizard.js +6 -4
- package/modules/hall-ui/public/style.css +43 -0
- package/modules/ideas/CLAUDE.md +11 -33
- package/modules/lifecycle/CLAUDE.md +11 -59
- package/modules/lifecycle/routes/goals.js +80 -0
- package/modules/memory/CLAUDE.md +11 -36
- package/modules/onboarding/CLAUDE.md +11 -51
- package/modules/provisioning/CLAUDE.md +15 -0
- package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
- package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
- package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
- package/modules/provisioning/module.json +17 -0
- package/modules/provisioning/onboard-plan.js +37 -0
- package/modules/provisioning/provisioning.js +539 -0
- package/modules/provisioning/rate-limit.js +35 -0
- package/modules/provisioning/routes/provisioning.js +607 -0
- package/modules/provisioning/tests/provisioning.mjs +232 -0
- package/modules/provisioning/ui/.gitkeep +0 -0
- package/modules/security/CLAUDE.md +11 -40
- package/modules/security/secret-scrub.js +10 -0
- package/modules/sessions/CLAUDE.md +11 -35
- package/modules/status-ui/CLAUDE.md +11 -43
- package/package-lock.json +2 -2
- package/package.json +7 -2
- package/public-landing/create.html +58 -12
- package/public-landing/index.html +40 -21
- package/scripts/discord/README.md +1 -1
- package/scripts/gds/CLAUDE.md +11 -140
- package/scripts/gds/claim.js +98 -48
- package/scripts/gds/claude-materialize.js +174 -20
- package/scripts/gds/claude-md-template.js +84 -0
- package/scripts/gds/cli-lib.js +112 -2
- package/scripts/gds/cutover-codemod.js +241 -0
- package/scripts/gds/fitness.js +54 -0
- package/scripts/gds/gen-api-client.js +10 -10
- package/scripts/gds/init.js +112 -16
- package/scripts/gds/lint-infra-exec.js +70 -0
- package/scripts/gds/lint-workflow-exec.js +63 -0
- package/scripts/gds/migration-namespace.js +1 -1
- package/scripts/gds/mirror-lineage-check.js +14 -5
- package/scripts/gds/mirror-redact.js +8 -0
- package/scripts/gds/module.js +118 -4
- package/scripts/gds/npmrc.js +93 -0
- package/scripts/gds/onboard.js +17 -24
- package/scripts/gds/package-core.js +74 -17
- package/scripts/gds/provision.js +102 -17
- package/scripts/gds/publish-manifest.js +60 -2
- package/scripts/gds/run-routine.js +51 -13
- package/scripts/gds/run-unit-tests.js +4 -0
- package/scripts/gds/seed-adopt-tasks.js +1 -1
- package/scripts/gds/status.js +6 -1
- package/scripts/gds/task.js +166 -0
- package/scripts/gds/update-channel.js +186 -0
- package/scripts/gds/upgrade.js +172 -25
- package/src/bongos/CLAUDE.md +11 -60
- package/src/bongos/auth.js +24 -0
- package/src/bongos/module-submissions.js +56 -0
- package/src/bongos/route-rank-check.js +6 -0
- package/src/bongos/routes/CLAUDE.md +11 -39
- package/src/bongos/routes/auth.js +6 -1
- package/src/bongos/routes/modules.js +123 -21
- package/src/bongos/serve-internal.js +10 -1
- package/src/branding.js +1 -0
- package/src/instance-config.js +1 -1
- package/src/module-api.js +24 -1
- package/src/module-loader/loader.js +36 -4
- package/src/module-loader/manifest-schema.js +16 -1
- package/tests/branding.mjs +11 -0
- package/tests/claim_error_surface.mjs +107 -0
- package/tests/claude_materialize.mjs +125 -0
- package/tests/consumer_layout_boot.mjs +302 -0
- package/tests/cutover_codemod.mjs +184 -0
- package/tests/device_flow_start.mjs +96 -0
- package/tests/first_run_engine_check.mjs +79 -0
- package/tests/goal_membership_requests.mjs +33 -3
- package/tests/hook_core_resolve.mjs +125 -0
- package/tests/hook_user_agents.mjs +62 -0
- package/tests/infra_exec_paths.mjs +104 -0
- package/tests/init.mjs +106 -6
- package/tests/instance_mismatch_guard.mjs +101 -0
- package/tests/mirror_lineage_proof.mjs +14 -0
- package/tests/module_cli.mjs +130 -2
- package/tests/module_loader.mjs +42 -0
- package/tests/module_manifest.mjs +19 -0
- package/tests/module_submissions.mjs +135 -0
- package/tests/npmrc.mjs +105 -0
- package/tests/onboard.mjs +62 -0
- package/tests/package_core.mjs +76 -22
- package/tests/platform_boot.mjs +5 -2
- package/tests/provision.mjs +107 -8
- package/tests/provisioning_github_app_routes.mjs +44 -2
- package/tests/publish_manifest.mjs +52 -1
- package/tests/run_routine.mjs +26 -1
- package/tests/run_routine_two_root.mjs +77 -0
- package/tests/scrubber_corpus.mjs +17 -0
- package/tests/served_paths_shipped.mjs +2 -2
- package/tests/task_cli.mjs +99 -0
- package/tests/update_channel.mjs +186 -0
- package/tests/upgrade.mjs +220 -25
- package/tests/workflow_exec_paths.mjs +98 -0
- package/docs/design/container-exterior-tiles.md +0 -49
- package/docs/design/container-exterior.md +0 -81
- package/docs/design/container-interior.md +0 -77
- package/docs/design/world-map-scroll.md +0 -41
- package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
- package/docs/recipes/cloudbongos-staging-gate.md +0 -139
- package/docs/recipes/cloudbongos-standup.md +0 -111
- package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
- package/docs/recipes/parallel-load-test.md +0 -32
- package/docs/recipes/pixel-art-pipeline.md +0 -411
- package/docs/recipes/staging-data.md +0 -50
- package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
- package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
- package/migrations/instance/110_medusa_planning_seed.sql +0 -70
- package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
- package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
- package/scripts/gds/art-stage.js +0 -152
- package/server.js +0 -212
- package/tests/art_key.mjs +0 -121
- package/tests/day_cycle.mjs +0 -197
- package/tests/snapshot_picker.mjs +0 -233
- package/tests/sprite_facing.mjs +0 -35
- package/tests/terrain_picker.mjs +0 -298
- package/tests/turn_in_place.mjs +0 -280
- package/tests/walkability.mjs +0 -188
package/scripts/gds/upgrade.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// pre-flight clean git tree + module-compat vs the TARGET core version
|
|
9
9
|
// → bump pin vendor the new core .tgz, rewrite the package.json `file:` dependency
|
|
10
|
-
// → npm install node_modules/@
|
|
10
|
+
// → npm install node_modules/@bongos/core becomes the new version
|
|
11
11
|
// → migrate `npm run migrate` → the core's migrate.sh applies additive `core_*` migrations
|
|
12
12
|
// → materialize refresh .claude/ skills+settings+hooks from the new core (claude-materialize / W3)
|
|
13
13
|
// → restart restart the instance's service so the new core is live
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// SCOPE (ADR 0100 §2, reshaped by ADR 0103 §4): the SMALLEST channel the FIRST real consumer needs
|
|
17
17
|
// on its first core bump — driven by that consumer, not designed up front. That consumer is
|
|
18
18
|
// emersonian-circles (task 1917): a greenfield npm-dependency instance that pins the core as
|
|
19
|
-
// "@
|
|
19
|
+
// "@bongos/core": "file:vendor/bongos-core-<ver>.tgz"
|
|
20
20
|
// with `start`/`migrate` npm scripts and a materialized .claude/. Every step below maps to something
|
|
21
21
|
// that consumer actually has. A published-registry source, rollback, and multi-consumer shapes are
|
|
22
22
|
// deliberately OUT — they are follow-ups a second consumer's needs will define.
|
|
@@ -30,9 +30,10 @@ const fs = require('node:fs');
|
|
|
30
30
|
const path = require('node:path');
|
|
31
31
|
const { spawnSync } = require('node:child_process');
|
|
32
32
|
const { arg, hasFlag } = require('./cli-lib');
|
|
33
|
+
const { writeNpmrc, hasNpmToken } = require('./npmrc'); // ADR 0134 (task 2090): the --registry channel's env-fed .npmrc
|
|
33
34
|
|
|
34
|
-
const CORE_PKG = '@
|
|
35
|
-
const ARTIFACT = '
|
|
35
|
+
const CORE_PKG = '@bongos/core';
|
|
36
|
+
const ARTIFACT = 'bongos-core';
|
|
36
37
|
|
|
37
38
|
// ---- pure helpers (no I/O side effects beyond the injected fs) --------------
|
|
38
39
|
|
|
@@ -44,6 +45,16 @@ function versionFromTgzPath(p) {
|
|
|
44
45
|
return m ? m[1] : null;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
// The version from a REGISTRY dep spec — a bare semver, optionally range-prefixed
|
|
49
|
+
// (`1.17.0`, `^1.17.0`, `~1.17.0`). Returns null for a `file:` tarball spec (that's
|
|
50
|
+
// versionFromTgzPath's job) so the two never both match. ADR 0134 (task 2090).
|
|
51
|
+
function versionFromDep(dep) {
|
|
52
|
+
const s = String(dep || '').trim();
|
|
53
|
+
if (!s || /^file:/.test(s)) return null;
|
|
54
|
+
const m = s.match(/^[\^~>=<\s]*v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)$/);
|
|
55
|
+
return m ? m[1] : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
function readJson(file, fsImpl = fs) {
|
|
48
59
|
try { return JSON.parse(fsImpl.readFileSync(file, 'utf8')); } catch { return null; }
|
|
49
60
|
}
|
|
@@ -54,11 +65,13 @@ function readInstalledCoreVersion(instanceDir, fsImpl = fs) {
|
|
|
54
65
|
return pkg && pkg.version ? pkg.version : null;
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
// The version the consumer's package.json currently pins
|
|
68
|
+
// The version the consumer's package.json currently pins — from either the `file:`
|
|
69
|
+
// tarball path (vendored) or a bare registry semver (ADR 0134). A best-effort "from"
|
|
70
|
+
// hint; readInstalledCoreVersion (node_modules) is the authoritative source.
|
|
58
71
|
function readPinnedCoreVersion(instanceDir, fsImpl = fs) {
|
|
59
72
|
const pkg = readJson(path.join(instanceDir, 'package.json'), fsImpl);
|
|
60
73
|
const dep = pkg && ((pkg.dependencies || {})[CORE_PKG] || (pkg.devDependencies || {})[CORE_PKG]);
|
|
61
|
-
return versionFromTgzPath(dep);
|
|
74
|
+
return versionFromTgzPath(dep) || versionFromDep(dep);
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
// Rewrite the consumer package.json to pin `file:<vendorRel>`. Returns the previous dep string.
|
|
@@ -73,6 +86,35 @@ function writePin(instanceDir, vendorRel, fsImpl = fs) {
|
|
|
73
86
|
return prev;
|
|
74
87
|
}
|
|
75
88
|
|
|
89
|
+
// Rewrite the consumer package.json to pin an EXACT registry version (ADR 0134, task
|
|
90
|
+
// 2090): the registry sibling of writePin. `npm install` then resolves @bongos/core@version
|
|
91
|
+
// from the registry (auth via the env-fed .npmrc). Returns the previous dep string.
|
|
92
|
+
function writeRegistryPin(instanceDir, version, fsImpl = fs) {
|
|
93
|
+
const pkgPath = path.join(instanceDir, 'package.json');
|
|
94
|
+
const pkg = readJson(pkgPath, fsImpl);
|
|
95
|
+
if (!pkg) throw new Error(`no readable package.json at ${pkgPath}`);
|
|
96
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
97
|
+
const prev = pkg.dependencies[CORE_PKG];
|
|
98
|
+
pkg.dependencies[CORE_PKG] = version;
|
|
99
|
+
fsImpl.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
100
|
+
return prev;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Restore the consumer package.json's @bongos/core pin to an EXACT previous dep string,
|
|
104
|
+
// VERBATIM — whether that was a `file:` tarball path (vendored) or a bare registry semver.
|
|
105
|
+
// The inverse of writePin/writeRegistryPin used by the auto-rollback path (task 2149), which
|
|
106
|
+
// snapshots the returned prev-dep before the bump. Returns the dep string it overwrote.
|
|
107
|
+
function restorePin(instanceDir, depString, fsImpl = fs) {
|
|
108
|
+
const pkgPath = path.join(instanceDir, 'package.json');
|
|
109
|
+
const pkg = readJson(pkgPath, fsImpl);
|
|
110
|
+
if (!pkg) throw new Error(`no readable package.json at ${pkgPath}`);
|
|
111
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
112
|
+
const prev = pkg.dependencies[CORE_PKG];
|
|
113
|
+
pkg.dependencies[CORE_PKG] = depString;
|
|
114
|
+
fsImpl.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
115
|
+
return prev;
|
|
116
|
+
}
|
|
117
|
+
|
|
76
118
|
// ---- steps (each injectable for tests) --------------------------------------
|
|
77
119
|
|
|
78
120
|
function gitTreeClean(instanceDir, run = spawnSync) {
|
|
@@ -127,10 +169,11 @@ function runMigrate(instanceDir, run = spawnSync) {
|
|
|
127
169
|
|
|
128
170
|
// Regenerate the per-instance API artifacts (OpenAPI spec + api-reference + typed
|
|
129
171
|
// client) from the NEW core's routes — the claude-materialize sibling (task 2051).
|
|
130
|
-
// WHY: the routes + generators ship WITH core,
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
172
|
+
// WHY: the routes + generators ship WITH core, and the generated spec/docs/client also
|
|
173
|
+
// ship as CORE STARTERS (task 2078 — a fresh hall/docs must render before first regen),
|
|
174
|
+
// but they are per-instance GENERATED, so a bump changes the served API while the
|
|
175
|
+
// instance's committed spec/client lag until regenerated (the classification settled in
|
|
176
|
+
// task 2141; init does the same at standup). We SPAWN the new core's generators (a fresh child
|
|
134
177
|
// captures its output root from the env below) with <FALLBACK_PREFIX>_INSTANCE_ROOT set
|
|
135
178
|
// so resolveDocsRoot() writes INTO the instance repo, not node_modules. docs first — the
|
|
136
179
|
// client reads the spec it produces. Best-effort: a hiccup warns, never fails the bump
|
|
@@ -252,6 +295,64 @@ async function pollHealth(healthUrl, deps = {}) {
|
|
|
252
295
|
}
|
|
253
296
|
}
|
|
254
297
|
|
|
298
|
+
// Auto-rollback (task 2149): a bump failed AFTER the pin was moved — an install or migrate
|
|
299
|
+
// error, or a failed post-restart health check. Revert the code pin to the pre-upgrade
|
|
300
|
+
// snapshot, reinstall the previous core, restart, re-verify health, and record the reversal
|
|
301
|
+
// in the ledger. The DB is deliberately NOT rolled back: core migrations are additive +
|
|
302
|
+
// forward-safe (the core migration rule), so any already-applied migration is left in place —
|
|
303
|
+
// only the code PIN moves back. Best-effort + fail-soft: every sub-step logs but never throws,
|
|
304
|
+
// so a rollback attempt can't itself crash the upgrader on top of the original failure.
|
|
305
|
+
async function rollback({ instanceDir, snapshot, fromVersion, toVersion, opts }, reason, deps = {}) {
|
|
306
|
+
const log = deps.log || console.log;
|
|
307
|
+
const err = deps.err || console.error;
|
|
308
|
+
const run = deps.run || spawnSync;
|
|
309
|
+
const fsImpl = deps.fs || fs;
|
|
310
|
+
const restore = deps.restorePin || restorePin;
|
|
311
|
+
|
|
312
|
+
err(` ↩ rolling back: ${reason} — reverting ${CORE_PKG} ${toVersion} → ${snapshot.prevVersion || snapshot.prevPin}`);
|
|
313
|
+
const result = { rolledBack: true, reason, restoredPin: snapshot.prevPin, restoredVersion: snapshot.prevVersion };
|
|
314
|
+
|
|
315
|
+
// 1. revert the pin to the exact previous dep string.
|
|
316
|
+
try { restore(instanceDir, snapshot.prevPin, fsImpl); result.restoreOk = true; log(` ✓ restored pin → ${snapshot.prevPin}`); }
|
|
317
|
+
catch (e) { result.restoreOk = false; err(` ! could not restore pin (${e.message}) — package.json may need manual repair`); }
|
|
318
|
+
|
|
319
|
+
// 2. reinstall the previous core.
|
|
320
|
+
const inst = npmInstall(instanceDir, run);
|
|
321
|
+
result.reinstallOk = !!inst.ok;
|
|
322
|
+
if (inst.ok) log(' ✓ reinstalled previous core');
|
|
323
|
+
else err(` ! reinstall of previous core failed (${inst.reason || 'exit ' + inst.code}) — the instance may need manual repair`);
|
|
324
|
+
|
|
325
|
+
// 3. restart back onto the previous core.
|
|
326
|
+
const rs = restartService({ service: opts.service, restartCmd: opts.restartCmd }, run);
|
|
327
|
+
if (rs.skipped) log(' • restart: no --service/--restart-cmd — restart the instance service manually to load the reverted core.');
|
|
328
|
+
else if (!rs.ok) err(` ! restart after rollback failed (exit ${rs.code}) — restart the service manually.`);
|
|
329
|
+
else log(` ✓ restarted ${opts.service || 'service'} on the previous core`);
|
|
330
|
+
result.restartOk = rs.ok;
|
|
331
|
+
|
|
332
|
+
// 4. re-verify: installed == previous, optional health recovery.
|
|
333
|
+
const installedNow = readInstalledCoreVersion(instanceDir, fsImpl);
|
|
334
|
+
result.installedAfter = installedNow;
|
|
335
|
+
log(` ${installedNow === snapshot.prevVersion ? '✓' : '!'} installed core after rollback: ${installedNow || '(unknown)'}`);
|
|
336
|
+
if (opts.healthUrl) {
|
|
337
|
+
const h = await pollHealth(opts.healthUrl, deps);
|
|
338
|
+
result.healthOk = h.ok;
|
|
339
|
+
if (h.ok) log(` ✓ health recovered ${opts.healthUrl} → ${h.status}${h.attempts > 1 ? ` (after ${h.attempts} tries)` : ''}`);
|
|
340
|
+
else err(` ! health STILL failing after rollback (${h.error || 'status ' + h.status}) — manual intervention needed`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 5. record the reversal in the ledger via the always-present `note` column (no schema
|
|
344
|
+
// dependency — so it records even when a FAILED migrate means a newer column wouldn't exist).
|
|
345
|
+
const note = `rolled_back: ${reason} (attempted ${fromVersion || '(none)'} → ${toVersion}, restored ${snapshot.prevVersion || 'previous'})`;
|
|
346
|
+
const ledger = await recordLedger({ fromVersion, toVersion, sourceCommit: opts.sourceCommit, note }, deps);
|
|
347
|
+
if (ledger.ok) log(' ✓ recorded rollback in core_upgrades ledger');
|
|
348
|
+
else if (ledger.skipped) log(` • rollback ledger skipped (${ledger.reason})`);
|
|
349
|
+
else err(` ! rollback ledger insert failed (${ledger.reason}) — record it manually`);
|
|
350
|
+
result.ledger = ledger.ok;
|
|
351
|
+
|
|
352
|
+
err(` ↩ rollback complete — instance restored to core ${snapshot.prevVersion || snapshot.prevPin}.`);
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
|
|
255
356
|
// ---- orchestration ----------------------------------------------------------
|
|
256
357
|
|
|
257
358
|
async function runUpgrade(opts, deps = {}) {
|
|
@@ -264,9 +365,16 @@ async function runUpgrade(opts, deps = {}) {
|
|
|
264
365
|
const instanceDir = path.resolve(opts.instance || process.cwd());
|
|
265
366
|
const targetVersion = opts.to;
|
|
266
367
|
const dryRun = !!opts.dryRun;
|
|
368
|
+
// Auto-rollback is ON by default (task 2149); `--no-rollback-on-failure` opts out (e.g. an
|
|
369
|
+
// interactive run that wants to inspect a broken state). Programmatic callers pass false.
|
|
370
|
+
const rollbackOnFailure = opts.rollbackOnFailure !== false;
|
|
371
|
+
// Pre-upgrade snapshot { prevPin, prevVersion } — captured the instant the pin is moved, so a
|
|
372
|
+
// failure AFTER that point (install / migrate / health) can revert. null until then = nothing to undo.
|
|
373
|
+
let snapshot = null;
|
|
267
374
|
if (!targetVersion) return { ok: false, error: 'missing --to <version> (the target core version)' };
|
|
268
375
|
|
|
269
376
|
const fromVersion = readInstalledCoreVersion(instanceDir, fsImpl) || readPinnedCoreVersion(instanceDir, fsImpl);
|
|
377
|
+
const doRollback = (reason) => rollback({ instanceDir, snapshot, fromVersion, toVersion: targetVersion, opts }, reason, deps);
|
|
270
378
|
log(`bongos upgrade — ${CORE_PKG}: ${fromVersion || '(none)'} → ${targetVersion}`);
|
|
271
379
|
log(` instance: ${instanceDir}${dryRun ? ' [DRY RUN — no changes]' : ''}`);
|
|
272
380
|
if (fromVersion === targetVersion && !opts.force) {
|
|
@@ -282,30 +390,56 @@ async function runUpgrade(opts, deps = {}) {
|
|
|
282
390
|
log(` ✓ pre-flight: ${mods.note}`);
|
|
283
391
|
if (!mods.ok) return { ok: false, error: `module pre-check failed: ${mods.blocking.map((b) => b.key).join(', ')} incompatible with core ${targetVersion}` };
|
|
284
392
|
|
|
285
|
-
// 2. bump the pin
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
393
|
+
// 2. bump the pin. Two mutually exclusive core sources (ADR 0134, task 2090):
|
|
394
|
+
// REGISTRY (--registry): pin @bongos/core@<version> + ensure the env-fed .npmrc, so
|
|
395
|
+
// the `npm install` below pulls the PRIVATE package from the registry (needs NPM_TOKEN).
|
|
396
|
+
// VENDORED (default, --from <tgz>): copy the .tgz into vendor/ + rewrite the file: dep.
|
|
397
|
+
if (opts.registry) {
|
|
398
|
+
if (opts.from) return { ok: false, error: '--registry and --from are mutually exclusive (registry pull vs vendored tarball) — pick one core source' };
|
|
399
|
+
if (!hasNpmToken()) log(' ! no NPM_TOKEN in env — `npm install` will 404 on the PRIVATE @bongos/core until a read-only automation token is provided (ADR 0134).');
|
|
400
|
+
const prevPin = dryRun ? null : writeRegistryPin(instanceDir, targetVersion, fsImpl);
|
|
401
|
+
if (!dryRun) { writeNpmrc(instanceDir, { fsImpl, overwrite: false, log }); snapshot = { prevPin, prevVersion: fromVersion }; }
|
|
402
|
+
log(` ${dryRun ? '[dry-run] would pin' : '✓ pinned'} ${CORE_PKG} → ${targetVersion} (registry)${dryRun ? ' + write an env-fed .npmrc' : ''}${prevPin ? ` (was ${prevPin})` : ''}`);
|
|
403
|
+
} else {
|
|
404
|
+
let vendorRel = opts.pinPath || (opts.from ? null : path.join('vendor', `${ARTIFACT}-${targetVersion}.tgz`));
|
|
405
|
+
if (opts.from) {
|
|
406
|
+
if (!fsImpl.existsSync(opts.from)) return { ok: false, error: `--from tarball not found: ${opts.from}` };
|
|
407
|
+
if (dryRun) { log(` [dry-run] would vendor ${opts.from} → vendor/${ARTIFACT}-${targetVersion}.tgz`); vendorRel = path.join('vendor', `${ARTIFACT}-${targetVersion}.tgz`); }
|
|
408
|
+
else vendorRel = vendorTarball({ instanceDir, fromTgz: path.resolve(opts.from), targetVersion }, fsImpl);
|
|
409
|
+
}
|
|
410
|
+
if (!fsImpl.existsSync(path.join(instanceDir, vendorRel)) && !dryRun) {
|
|
411
|
+
return { ok: false, error: `no core tarball at ${vendorRel} — pass --from <path-to-.tgz> to vendor it (or --registry to pull from the npm registry)` };
|
|
412
|
+
}
|
|
413
|
+
const prevPin = dryRun ? null : writePin(instanceDir, vendorRel, fsImpl);
|
|
414
|
+
if (!dryRun) snapshot = { prevPin, prevVersion: fromVersion };
|
|
415
|
+
log(` ${dryRun ? '[dry-run] would pin' : '✓ pinned'} ${CORE_PKG} → file:${vendorRel}${prevPin ? ` (was ${prevPin})` : ''}`);
|
|
294
416
|
}
|
|
295
|
-
const prevPin = dryRun ? null : writePin(instanceDir, vendorRel, fsImpl);
|
|
296
|
-
log(` ${dryRun ? '[dry-run] would pin' : '✓ pinned'} ${CORE_PKG} → file:${vendorRel}${prevPin ? ` (was ${prevPin})` : ''}`);
|
|
297
417
|
|
|
298
418
|
if (dryRun) { log(' [dry-run] would: npm install → npm run migrate → materialize .claude → regen API docs+client → restart → verify'); return { ok: true, dryRun: true, fromVersion, toVersion: targetVersion }; }
|
|
299
419
|
|
|
300
420
|
// 3. npm install.
|
|
301
421
|
const inst = npmInstall(instanceDir, run);
|
|
302
|
-
if (!inst.ok)
|
|
422
|
+
if (!inst.ok) {
|
|
423
|
+
const error = `npm install failed (${inst.reason || 'exit ' + inst.code})`;
|
|
424
|
+
if (rollbackOnFailure && snapshot) {
|
|
425
|
+
const rb = await doRollback('npm install failed');
|
|
426
|
+
return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb };
|
|
427
|
+
}
|
|
428
|
+
return { ok: false, error, fromVersion };
|
|
429
|
+
}
|
|
303
430
|
log(' ✓ npm install');
|
|
304
431
|
|
|
305
432
|
// 4. migrate (additive core_* migrations).
|
|
306
433
|
if (!opts.skipMigrate) {
|
|
307
434
|
const mig = runMigrate(instanceDir, run);
|
|
308
|
-
if (!mig.ok)
|
|
435
|
+
if (!mig.ok) {
|
|
436
|
+
const error = `migrate failed (${mig.reason || 'exit ' + mig.code})`;
|
|
437
|
+
if (rollbackOnFailure && snapshot) {
|
|
438
|
+
const rb = await doRollback('migrate failed');
|
|
439
|
+
return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb };
|
|
440
|
+
}
|
|
441
|
+
return { ok: false, error, fromVersion };
|
|
442
|
+
}
|
|
309
443
|
log(' ✓ migrate');
|
|
310
444
|
} else log(' • migrate skipped (--skip-migrate)');
|
|
311
445
|
|
|
@@ -345,6 +479,14 @@ async function runUpgrade(opts, deps = {}) {
|
|
|
345
479
|
else err(` ! health ${opts.healthUrl} not ready after ${h.attempts} tr${h.attempts === 1 ? 'y' : 'ies'}: ${h.error || 'status ' + h.status}`);
|
|
346
480
|
}
|
|
347
481
|
|
|
482
|
+
// A failed post-restart health check means the new core is serving broken — auto-revert (task
|
|
483
|
+
// 2149) instead of recording an "upgraded" row + leaving the instance down. The rollback writes
|
|
484
|
+
// its OWN rolled_back ledger row, so skip the normal insert below.
|
|
485
|
+
if (healthOk === false && rollbackOnFailure && snapshot) {
|
|
486
|
+
const rb = await doRollback('health check failed after restart');
|
|
487
|
+
return { ok: false, error: `health check failed after restart — rolled back to ${snapshot.prevVersion || 'the previous core'}`, fromVersion, toVersion: targetVersion, versionOk, healthOk: false, rolledBack: true, rollback: rb };
|
|
488
|
+
}
|
|
489
|
+
|
|
348
490
|
const ledger = await recordLedger({ fromVersion, toVersion: targetVersion, sourceCommit: opts.sourceCommit, note: opts.note }, deps);
|
|
349
491
|
if (ledger.ok) log(' ✓ recorded in core_upgrades ledger');
|
|
350
492
|
else if (ledger.skipped) log(` • ledger skipped (${ledger.reason})`);
|
|
@@ -366,11 +508,13 @@ function parseArgs(argv) {
|
|
|
366
508
|
sourceCommit: arg('--source-commit', argv),
|
|
367
509
|
note: arg('--note', argv),
|
|
368
510
|
pinPath: arg('--pin-path', argv),
|
|
511
|
+
registry: hasFlag('--registry', argv), // ADR 0134: pull @bongos/core@<--to> from the npm registry instead of vendoring
|
|
369
512
|
dryRun: hasFlag('--dry-run', argv),
|
|
370
513
|
force: hasFlag('--force', argv),
|
|
371
514
|
skipMigrate: hasFlag('--skip-migrate', argv),
|
|
372
515
|
skipMaterialize: hasFlag('--skip-materialize', argv),
|
|
373
516
|
skipRegenDocs: hasFlag('--skip-regen-docs', argv),
|
|
517
|
+
rollbackOnFailure: !hasFlag('--no-rollback-on-failure', argv), // ADR 0100: auto-revert ON by default (task 2149)
|
|
374
518
|
};
|
|
375
519
|
}
|
|
376
520
|
|
|
@@ -385,6 +529,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
385
529
|
'',
|
|
386
530
|
' --to <version> target core version — presence switches from pre-check to a real bump',
|
|
387
531
|
' --from <tgz> source tarball to vendor into vendor/ (else it must already be there)',
|
|
532
|
+
' --registry pull @bongos/core@<--to> from the npm registry (env-fed .npmrc + NPM_TOKEN) instead of vendoring a tarball (ADR 0134); mutually exclusive with --from',
|
|
388
533
|
' --instance <dir> consumer instance root (default: cwd)',
|
|
389
534
|
' --service <unit> systemd unit to restart after applying',
|
|
390
535
|
' --restart-cmd <cmd> explicit restart command (overrides --service)',
|
|
@@ -392,10 +537,12 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
392
537
|
' --skip-migrate do not run `npm run migrate`',
|
|
393
538
|
' --skip-materialize do not refresh .claude/',
|
|
394
539
|
' --skip-regen-docs do not regenerate the OpenAPI spec + typed client from the new core',
|
|
540
|
+
' --no-rollback-on-failure do NOT auto-revert on a failed install/migrate/health check (default: auto-rollback ON)',
|
|
395
541
|
' --dry-run show the plan; change nothing',
|
|
396
542
|
' --force skip the clean-tree + same-version guards',
|
|
397
543
|
'',
|
|
398
544
|
'Full bump: pre-flight → bump pin → npm install → migrate → materialize .claude → regen API docs+client → restart → verify.',
|
|
545
|
+
'On a failure after the pin moves (install/migrate/health), auto-rollback reverts the pin, reinstalls the previous core, restarts, and records a rolled_back ledger row (the DB is not rolled back — core migrations are additive).',
|
|
399
546
|
].join('\n') + '\n');
|
|
400
547
|
return 0;
|
|
401
548
|
}
|
|
@@ -410,8 +557,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
410
557
|
}
|
|
411
558
|
|
|
412
559
|
module.exports = {
|
|
413
|
-
runUpgrade, parseArgs, main,
|
|
414
|
-
versionFromTgzPath, readInstalledCoreVersion, readPinnedCoreVersion, writePin,
|
|
560
|
+
runUpgrade, parseArgs, main, rollback,
|
|
561
|
+
versionFromTgzPath, versionFromDep, readInstalledCoreVersion, readPinnedCoreVersion, writePin, writeRegistryPin, restorePin,
|
|
415
562
|
vendorTarball, gitTreeClean, preflightModules, npmInstall, runMigrate, regenerateApiArtifacts, restartService, recordLedger, pollHealth,
|
|
416
563
|
CORE_PKG, ARTIFACT,
|
|
417
564
|
};
|
package/src/bongos/CLAUDE.md
CHANGED
|
@@ -1,64 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This file is a placeholder shipped inside the **@bongos/core** package. The core's own
|
|
4
|
+
internal agent notes are intentionally omitted from the published artifact so they cannot
|
|
5
|
+
load into your instance's context.
|
|
4
6
|
|
|
5
|
-
**
|
|
7
|
+
**It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
|
|
8
|
+
directory, and the live Bongos database are the source of truth for how work happens here
|
|
9
|
+
and who may do what.
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **Lifecycle graduated out** → [`modules/lifecycle/`](../../modules/lifecycle/CLAUDE.md) (the build state machine — tasks · claims · versions · done-when · goals · dependencies · the claim→ship→grade→credit lifecycle · github-push + merge-lock + conflict-resolve + publish-reconciler + ship-card/preflight + version-close; the LAST + most-coupled domain, BV1.R86). Core reaches it only through the `lifecycle` kernel **port** the module registers — `routes/{public,me,builders}.js` + `cascade-dispatch.js` resolve `seams.resolveOptional('lifecycle')` for the version/ship feed, per-builder claims/grades, the Archon roster, and the task-kind classifier; the moved routes (`routes/{tasks,claims,versions,done-when,goals,dependencies,gate-approvals,analytics}.js`) mount via the loader. It **retired the last three doorway leaks** (`createTask`/`VOTABLE_TASK_STATUSES`/`tallyPeerVotes` → behind the port). As a transaction-participant it RESOLVES the `reward`+`onboarding` ports inside its claim/ship/confirm txns ([ADR 0093](../../docs/adr/<redacted>.md) §1,§3).
|
|
10
|
-
- **Grading graduated out** → [`modules/grading/`](../../modules/grading/CLAUDE.md) (the quality gate: v4 adversarial worker panel Narc/Quality/Hacker/Efficiency + deterministic pre-passes + the rubric; BV1.R73). Core reaches it only through the `grade` kernel **port** — the lifecycle module's `routes/tasks.js` + core `routes/public.js` resolve `seams.resolveOptional('grade')`, never a direct require ([ADR 0091](../../docs/adr/<redacted>.md) §4, [ADR 0024](../../docs/adr/<redacted>.md)). `cascade-dispatch.js` (dormant System 3) stayed core.
|
|
11
|
-
- **Economy graduated out** → [`modules/economy/`](../../modules/economy/CLAUDE.md) (credit allocation + the cost-plus/kind-multiplier model, the cost ledger + dashboard rollups, streaks, achievements, the leaderboard; BV1.R77). Core reaches it only through the `reward` kernel **port** — a **transaction-participant** port: the lifecycle (`applyGrade`/`confirmTask`/`decideOverrideRequest`/`claimTask`/`upsertSessionRecordWithReward`) keeps its state-machine SQL and the `credit_log`/`cost_log` *guards*, but resolves `seams.resolveOptional('reward')` and passes its own tx `client` for every credit/achievement WRITE; the read routes (`me.js`/`public.js`/`builders.js`/`sessions.js`) + `publish-reconciler.js` resolve the same port ([ADR 0093](../../docs/adr/<redacted>.md) §1-2). `llm-pricing.js` stays core shared LLM-cost infra (doorway leak `api.llmPricing`, like `llm-cache.js`).
|
|
12
|
-
- **Ideas graduated out** → [`modules/ideas/`](../../modules/ideas/CLAUDE.md) (the `idea_inbox` capture/triage/criterion-proposal flow + the `blockers` queue; BV1.R78). The discord `#ideas` inbound reaches it only through the `ideas.capture` kernel **port** the module registers — which **retired** the `captureIdea` doorway leak ([ADR 0093](../../docs/adr/<redacted>.md) §5). `routes/public.js` keeps its **own** inline `idea_inbox`/`blockers` SELECTs (core querying a shared table with its own SQL is fine — no import, no twin).
|
|
13
|
-
- **Security FEATURE graduated out** → [`modules/security/`](../../modules/security/CLAUDE.md) (red-team / vulnerability **reports** + the **bounty** payout table, rank-gated Metic+; `security.js` + `routes/reports.js`; BV1.R79). Core reaches the bounty schedule only through the `security` kernel **port** the module registers — `routes/public.js`'s `/public/bounty-table` resolves `seams.resolveOptional('security').bountyTableDetailed()`, never a direct require ([ADR 0093](../../docs/adr/<redacted>.md) §1; [02-module-map §5](../../docs/design/modular-architecture/02-module-map.md)). **<redacted> stays kernel:** `permission-path-check.js`/`route-rank-check.js` (authz) AND `routes/security.js` (the `/security/adr` threat-model reader + the Archon-only `/security/docs` exploit-detail store, backed by core `db.js` `security_docs` queries) all stayed in core.
|
|
14
|
-
- **Builder-settings graduated out** → [`modules/builder-settings/`](../../modules/builder-settings/CLAUDE.md) (the per-builder PREFS domain — wandering · skill · sound · render — + builder-needs + their `db.js` slice; the five `/me/*-prefs` routes; BV1.R80). The `GET /me` aggregator (`routes/me.js`) + `cascade-dispatch.js` reach it only through the `builder-settings` kernel **port** the module registers (`seams.resolveOptional('builder-settings')` for the wandering/render/needs profile slices + the skill ceiling), never a direct require ([ADR 0093](../../docs/adr/<redacted>.md) §1). The five `skill_model_prefs`/`sound_prefs`/`behavior_prefs`/`event_sound_prefs` getter/setter pairs left `db.js`; the `builders` table + the Gemini-key columns (art-pipeline credential infra) stay core.
|
|
15
|
-
- `auth.js` — GitHub Device Flow (CLI) + web OAuth → `builder_sessions`. Exports the request guards: `requireBuilder`, `requireRank`, `allowBoxScope` (box tokens are deny-by-default, [ADR 0053](../../docs/adr/0053-scoped-dev-box-session.md)), plus Discord OAuth helpers.
|
|
16
|
-
- `github-push.js` — server-mediated branch publish ([ADR 0055](../../docs/adr/<redacted>.md)): box uploads a git bundle, server pushes + opens PR + auto-merges via a short-lived **GitHub App** installation token (no standing PAT). Also `mergeIfGreen`, gate commit-status helpers.
|
|
17
|
-
- `routes.js` + `routes/` — **thin mounter**: each feature is a sub-router in `routes/`; adding a feature = one new file + one `.use()` line. Audit + rate-limit middleware mount first. See `routes/`'s own nested CLAUDE.md.
|
|
18
|
-
- `permission-path-check.js` / `route-rank-check.js` — the pure protected-path + route-rank matchers (SEC [#863](https://example.com/builders#/task/863) / [ADR 0043](../../docs/adr/<redacted>.md)), reused by the grading module (via the doorway — `api.routeRankCheck`/`api.permissionPathCheck`), pre-push hook, and `--fit` runner.
|
|
19
|
-
- **Onboarding graduated out** → [`modules/onboarding/`](../../modules/onboarding/CLAUDE.md) (admission · the server-ticked newcomer onboarding state machine · the friction analyzer · the evergreen newcomer-chore restock; `onboarding-state.js` + `onboarding.js` + `newcomer-restock.js` + `routes/access-requests.js` + `approval-broadcast.js` + its `db.js` slice; BV1.R81). The novel shape: it is the FIRST core→module port the LIFECYCLE drives — core reaches the state machine + restock only through the `onboarding` kernel **port** the module registers (`seams.resolveOptional('onboarding')` in `db.js` claim/ship, `auth.js`, `routes/me.js`/`builders.js`/`tasks.js`/`claims.js`), never a direct require ([ADR 0093](../../docs/adr/<redacted>.md) §1). The admission GATE READS (`hasInvitedAccessRequest`/`getBuilderByGithubId`) stay core `db.js` (kernel identity/admission).
|
|
20
|
-
- **Sessions/BFG graduated out** → [`modules/sessions/`](../../modules/sessions/CLAUDE.md) (the `session_records` corpus + its `/sessions` data plane, the BFG cross-builder scorer, the session-pulse/stale-claim helpers; `bfg.js` + `pulse.js` + `routes/sessions.js` + its `db.js` slice; BV1.R82). Like `memory`, it registers **NO port** — core never calls back in: the ship-time session-digest write is an **HTTP POST** to the module's own `/sessions` route. Its one outward edge is the economy `reward` port it *resolves* (`seams.resolveOptional('reward')` in `db.js` `upsertSessionRecordWithReward` + `routes/sessions.js`) for the cost-plus token reward + `sumCreditsForTasks` ([ADR 0093](../../docs/adr/<redacted>.md) §1). **Stays core:** `session_logs` (the per-claim ship LOG, `sealSessionLog` — lifecycle) and `builder_sessions` (auth tokens) are NOT this domain.
|
|
21
|
-
- **Autonomy/MAS graduated out** → [`modules/autonomy/`](../../modules/autonomy/CLAUDE.md) (the Multi-Agent System — the autonomy gate, the conductor/architect/run-routine CLIs, and the in-process routine timers; `routes/autonomy.js` + `pollers/routine-timers.js` + the `autonomy_runs` `db.js` slice; BV1.R83). Like `sessions`/`memory`, it registers **NO port** — core never calls back in. The cross-cutting bit: the **routine timer** (was `routineTimers.start()` in `server.js`) is now a loader-started **poller** (`contributes.pollers`), and the **precheck route** (was mounted in `routes.js`) a module route; both severed their `src/`/`server.js` import. The timer's one outward edge is the lifecycle **`tallyPeerVotes`** capability it resolves through the **doorway** (`api.tallyPeerVotes` — the SAME tx `POST /tasks/peer-votes/tally` runs; a tracked lifecycle leak until R86), never a core require ([ADR 0093](../../docs/adr/<redacted>.md) §1). **Stays core:** `tallyPeerVotes` itself (lifecycle — writes `karma_log`/`peer_votes`/`tasks`); the MAS-domain CLIs travel with the domain but stay under `scripts/gds/`.
|
|
22
|
-
- `boxes.js` · `box-access.js` · `do-api.js` · `cf-tunnel.js` — per-builder cloud dev-box lifecycle, rank-gated source clawback, DigitalOcean client, and the Cloudflare named-tunnel control plane ([ADR 0031](../../docs/adr/<redacted>.md)).
|
|
23
|
-
- **Discord graduated out** → [`modules/discord/`](../../modules/discord/CLAUDE.md) (bot, broadcast, role-sync, channels, bug-attachments, link/role; BV1.R47). Core reaches it only through the doorway seams — ports `discord.isLinked`/`discord.guildJoinSpec`, events `task.shipped`/`builder.role-sync-needed` — never a direct require. Discord is a mirror only, never authority ([ADR 0033](../../docs/adr/<redacted>.md)).
|
|
24
|
-
- `llm-pricing.js` · `task-classifier.js` · `search-chunker.js` · `search.js` · `publish-reconciler.js` · `conflict-resolve.js` — token→USD pricing (cost-plus reward), `tasks.kind` heuristic, retrieval-layer chunker + search ([ADR 0060](../../docs/adr/0060-gds-retrieval-layer.md)), the confirmed→shipped auto-heal sweep, and (`conflict-resolve.js`, [ADR 0082](../../docs/adr/<redacted>.md)) the SERVER-side generated-file/append-index merge-conflict resolver the reconciler calls on `merge_conflict` (temp-clone merge with the `otb-regen` driver + `union`, then push; never bypasses a check).
|
|
11
|
+
You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
|
|
12
|
+
Nothing in here needs to be read to use the platform — run `bongos help` for commands.
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- `db.js` is huge — **skim by section/grep, never read whole** (see efficiency note above). (`grader.js` graduated to `modules/grading/` — BV1.R73.)
|
|
29
|
-
- Protected-path + route-rank logic is defined **once** in `permission-path-check.js` / `route-rank-check.js`; reuse the matcher, don't re-implement.
|
|
30
|
-
- `routes.js` is deliberately conflict-free — feature logic goes in a `routes/` sub-router, not here.
|
|
31
|
-
- `preview-server.js` is **not** here — it lives at `../preview-server.js` (game-only sandbox).
|
|
32
|
-
|
|
33
|
-
<!-- BEGIN GENERATED REPO-MAP (scripts/gds/gen-repo-map.js — do not hand-edit) -->
|
|
34
|
-
**Symbol skeleton (generated from the code — do not hand-edit; refresh: `node scripts/gds/gen-repo-map.js`).** Exported signatures, most-referenced first. The interpretive notes above are hand-written; this factual list is regenerated on deploy so it can't drift ([ADR 0063](../../docs/adr/<redacted>.md)).
|
|
35
|
-
|
|
36
|
-
- `api-errors.js` — `envelope(codeOrError, message, details)`, `classify(code)`, `class ApiError`, `CATEGORIES`, `OVERRIDES`, `isApiError(x)`, `DEFAULT_CATEGORY`, `statusForCode(code, override)`, `categoryForCode(code)`, `isRetriable(code)`
|
|
37
|
-
- `api-path-404.js` — `apiNotFoundBody(p)`, `isApiShapedPath(p)`, `apiPathHint(p)`, `GDS_ROOTS`
|
|
38
|
-
- `api-prefix.js` — `API_PREFIX`, `API_VERSION`, `LEGACY_API_PREFIXES`, `VERSIONED_API_PREFIX`, `ALL_API_PREFIXES`
|
|
39
|
-
- `app-pair.js` — `pairStore`, `createPairStore(opts = {})`, `normalizeCode(raw)`
|
|
40
|
-
- `auth.js` — `requireBuilder(req, res, next)`, `requireRank(...allowed)`, `allowBoxScope(req, _res, next)`, `authConfigured()`, `requireBfgPrincipal(req, res, next)`, `loadDiscordBotToken()`, `requireArchonPage(req, res, next)`, `isValidDiscordUserId(id)`, `requireBuilderPage(req, res, next)`, `completeWebFlow({ code, redirectUri, userAgent })` _(+20 more)_
|
|
41
|
-
- `cascade-dispatch.js` — `dispatch({ prompt, taskKind, title, description, discipline, ceiling = 'opus', expectSchema = null,…)`, `LADDER`, `startRungForTask({ taskKind, title, description } = {})`, `startRungForKind(taskKind)`, `ceilingForBuilder(builderPrefs, skillName)`, `MAX_ESCALATIONS`, `rungIndex(model)`, `shouldEscalate(result, expectSchema)`
|
|
42
|
-
- `db-kernel.js` — `getBuilderById(id)`, `insertAuditLog({)`, `withTx(fn, { pool: p = pool } = {})`, `RANKS`, `LIVE_RANK_LADDER`, `createSession({ builderId, token, source, userAgent, expiresAt })`, `lookupSession(token)`, `upsertBuilder({ githubId, githubLogin, displayName, avatarUrl })`, `getBuilderByGithubId(githubId)`, `hasInvitedAccessRequest(login)` _(+3 more)_
|
|
43
|
-
- `db.js` — `setBuilderRank({ builderId, newRank, actorBuilderId })`, `getDiscordLink(builderId)`, `hasCliSession(builderId)`, `recordApplause({ targetTaskId, discordUserId })`, `singleRungPromotionError(oldRank, newRank)`, `deactivateBuilder({ builderId, reason, actorBuilderId })`, `<redacted>(builderId)`, `listAuditLog({ limit = 100, offset = 0, builderId, method, surface } = {})`, `revokeAllSessionsForBuilder(builderId)`, `unlinkDiscord(builderId)` _(+11 more)_
|
|
44
|
-
- `goal-scope-check.js` — `check(goalScopeModules, touches, builderRank)`, `isCovered(touch, goalGlobs)`, `protectedPathBlock(touches, builderRank)`, `scopeSubset(goalScopeModules, touches)`
|
|
45
|
-
- `hierarchy-config.js` — `tier`, `hierarchy()`, `tiers`, `INSTANCE_PATH`, `DEFAULT_HIERARCHY`, `ROLE_KEYS`, `_resetForTests()`, `STANDARD_KEYS`, `loadHierarchy({ instancePath, env = process.env } = {})`, `membershipTier` _(+9 more)_
|
|
46
|
-
- `live-channel.js` — `subscribe(fn)`, `CHANNEL`, `_state()`
|
|
47
|
-
- `llm-cache.js` — `store(spec, { resultStdout, resultCostUsd = 0 } = {})`, `normalize(taskType, prompt)`, `makeCacheSpec({ prompt, model, task_type, scope_sha, owner_builder_id = null, ttl_ms = null } = {})`, `NORMALIZERS`, `poolReachable()`, `withCache(spec, producer)`, `computeCacheKey({ prompt, model, task_type, scope_sha } = {})`, `recordSaving(spec, { resultCostUsd = 0, hitCount = 0 } = {})`, `scopeShaFor(paths, { runner, cwd } = {})`, `stripVolatile(text)` _(+2 more)_
|
|
48
|
-
- `llm-pricing.js` — `priceUsage(model, usage)`, `PRICES`, `priceModelUsage(modelUsage)`, `componentsFromModelUsage(modelUsage)`, `familyFor(model)`, `canonicalizeModelId(model)`
|
|
49
|
-
- `metrics.js` — `render(p = pool)`, `HIST_BUCKETS`, `authorizeScrape(req)`, `httpMiddleware()`, `incCounter(name, help, labels = {}, by = 1)`, `collectDbGauges(p = pool)`, `metricsHandler()`, `normalizeRoute(p)`, `observeHistogram(name, help, labels, value)`, `__resetForTests()`
|
|
50
|
-
- `module-scope-map.js` — `MODULE_SCOPE_MAP`, `moduleKeys()`, `scopeIncludesProtected(keys)`, `isModuleKey(key)`, `isProtected(globs)`, `MODULE_GLOBS`, `globsForModules(keys)`, `scopeForModule(key)`, `isProtectedModule(key)`, `protectedModules()`
|
|
51
|
-
- `optimizer.js` — `VALID_MODES`, `DEFAULT_MAX_PARALLEL`, `optimizeSession({ builderId, mode, timeBudgetMin, maxParallel })`
|
|
52
|
-
- `path-match.js` — `touchesOverlap(a, b)`, `matchOne(file, touches)`
|
|
53
|
-
- `permission-path-check.js` — `PROTECTED_GLOBS`, `SPECIALTY`, `matchProtected(files)`, `checkPermissionPaths({ changedFiles, builderRank } = {})`, `globToRegExp(glob)`, `RANK_TIER`, `tierOf(rank)`, `toWorkerResult(result)`, `MIN_TIER`, `isBelowFloor(rank)`
|
|
54
|
-
- `pool.js` — `instanceDbName()`
|
|
55
|
-
- `repo-info.js` — `loadRepoInfo()`, `parseRemote(remote)`, `_resetCacheForTests()`
|
|
56
|
-
- `route-rank-check.js` — `EXPECTED_RANKS`, `SPECIALTY`, `MODULES_DIR`, `MODULE_EXPECTED_RANKS`, `checkRouteRanks({ routesDir = ROUTES_DIR, modulesDir } = {})`, `toWorkerResult(result)`, `KNOWN_RANKS`, `WRITE_METHODS`, `classifyRank(lines, i)`, `classifyText(text)` _(+2 more)_
|
|
57
|
-
- `routes.js` — `buildGdsRouter()`
|
|
58
|
-
- `secret-box.js` — `VERSION`, `isConfigured()`, `decrypt(blob)`, `encrypt(plaintext)`, `provisionMasterKey(opts = {})`, `masterKeyFile()`
|
|
59
|
-
- `sensitive-surfaces.js` — `isSensitive(file)`, `SENSITIVE_SURFACES`
|
|
60
|
-
- `serve-internal.js` — `isBuildersHost(req)`, `mountInternalSurfaces(app)`, `isStatusHost(req)`, `injectBrandingGlobal(html, brand = clientBranding()`, `applyBrandTokens(html, brand = clientBranding()`, `injectBrandingInto(stamped, brand = clientBranding()`, `injectModuleHallWidgets(html, scripts = hallWidgetScripts({ isEnabled: isModuleEnabled })`, `themeMarkup(theme)`
|
|
61
|
-
- `uptime-poller.js` — `start()`
|
|
62
|
-
<!-- END GENERATED REPO-MAP -->
|
|
63
|
-
|
|
64
|
-
**See also.** [ADR 0010](../../docs/adr/0010-pms-architecture.md) (GDS architecture) · [ADR 0016](../../docs/adr/<redacted>.md) (trust boundary) · [docs/architecture.md](../../docs/architecture.md) · `routes/CLAUDE.md` (HTTP surface).
|
|
14
|
+
_To work ON the platform core itself, use the standalone @bongos/core source repository,
|
|
15
|
+
not this vendored copy._
|
package/src/bongos/auth.js
CHANGED
|
@@ -224,6 +224,30 @@ async function startDeviceFlow() {
|
|
|
224
224
|
throw new Error(`device/code failed (${res.status}): ${text}`);
|
|
225
225
|
}
|
|
226
226
|
const data = await res.json();
|
|
227
|
+
// GitHub returns HTTP 200 with an { error, error_description } body for OAuth-App
|
|
228
|
+
// misconfig — most often device_flow_disabled (Device Flow not enabled on the App)
|
|
229
|
+
// or incorrect_client_credentials. res.ok is TRUE there, so without this the missing
|
|
230
|
+
// device_code silently produced a broken flow / an opaque 500 (the 2026-07-08 demo
|
|
231
|
+
// login failure). Mirror the token-exchange guard (data.error, above); task 2126.
|
|
232
|
+
if (data.error) {
|
|
233
|
+
// device_flow_disabled is self-inflicted + common: GitHub defaults Device Flow OFF on new
|
|
234
|
+
// Apps and the one-click manifest can't enable it (task 2129). Make the CLI error self-
|
|
235
|
+
// service by naming the right settings surface. We hold only client_id here (not the app
|
|
236
|
+
// slug — that lives on the control plane), so we can't build /settings/apps/<slug>; but the
|
|
237
|
+
// id PREFIX distinguishes a GitHub App (Iv*) from an OAuth App (Ov*), which picks the right
|
|
238
|
+
// listing to open.
|
|
239
|
+
let hint = '';
|
|
240
|
+
if (data.error === 'device_flow_disabled') {
|
|
241
|
+
const where = /^Iv/i.test(creds.client_id || '')
|
|
242
|
+
? 'https://github.com/settings/apps (org-owned apps: https://github.com/organizations/<org>/settings/apps)'
|
|
243
|
+
: 'https://github.com/settings/developers';
|
|
244
|
+
hint = ` — terminal sign-in needs Device Flow enabled on the app: open ${where}, open your app, check "Enable Device Flow", and save. Web sign-in is unaffected.`;
|
|
245
|
+
}
|
|
246
|
+
throw new Error(`GitHub rejected device-flow start: ${data.error}${data.error_description ? ` — ${data.error_description}` : ''}${hint}`);
|
|
247
|
+
}
|
|
248
|
+
if (!data.device_code) {
|
|
249
|
+
throw new Error('GitHub device/code returned no device_code (unexpected response shape).');
|
|
250
|
+
}
|
|
227
251
|
// data: { device_code, user_code, verification_uri, expires_in, interval }
|
|
228
252
|
deviceFlows.set(data.device_code, {
|
|
229
253
|
interval: data.interval || 5,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/bongos/module-submissions.js — the ADR 0107 §1 upstreaming review queue
|
|
2
|
+
// (task 1770, migration core_191). A rank-gated (metic+archon) queue a builder
|
|
3
|
+
// files a packaged module into (manifest + attribution + file snapshot + DCO
|
|
4
|
+
// sign-off) before it can be reviewed/accepted upstream. See
|
|
5
|
+
// docs/adr/0135-module-upstream-submission-interim-queue.md for why this queue
|
|
6
|
+
// lives in THIS instance's own DB rather than a live cross-repo call.
|
|
7
|
+
//
|
|
8
|
+
// Pure DB access — the packaging (pre-check, file read, sign-off) is
|
|
9
|
+
// scripts/gds/module.js's job (build tooling, fs-only); the route wires the
|
|
10
|
+
// two together.
|
|
11
|
+
|
|
12
|
+
const { pool } = require('./pool');
|
|
13
|
+
|
|
14
|
+
async function createModuleSubmission({
|
|
15
|
+
moduleKey, submittedBy, manifest, files, signOff, precheckPassedAt, sourceRepo,
|
|
16
|
+
}) {
|
|
17
|
+
const { rows } = await pool.query(
|
|
18
|
+
`INSERT INTO module_submissions
|
|
19
|
+
(module_key, submitted_by, manifest, files, sign_off, precheck_passed_at, source_repo)
|
|
20
|
+
VALUES ($1, $2, $3::jsonb, $4::jsonb, $5::jsonb, $6, $7)
|
|
21
|
+
RETURNING id, module_key, submitted_by, manifest, sign_off, precheck_passed_at,
|
|
22
|
+
source_repo, status, reviewed_by, reviewed_at, review_notes, submitted_at`,
|
|
23
|
+
[
|
|
24
|
+
moduleKey,
|
|
25
|
+
submittedBy,
|
|
26
|
+
JSON.stringify(manifest),
|
|
27
|
+
JSON.stringify(files),
|
|
28
|
+
JSON.stringify(signOff),
|
|
29
|
+
precheckPassedAt,
|
|
30
|
+
sourceRepo || null,
|
|
31
|
+
]
|
|
32
|
+
);
|
|
33
|
+
return rows[0];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Metadata-only list (no files jsonb — the file snapshot can run to hundreds of
|
|
37
|
+
// KB per submission, and no list consumer needs it; a future GET .../:id can add
|
|
38
|
+
// a with-files fetch if task 1771's review UI needs the code inline).
|
|
39
|
+
async function listModuleSubmissions({ status, moduleKey } = {}) {
|
|
40
|
+
const clauses = [];
|
|
41
|
+
const params = [];
|
|
42
|
+
if (status) { params.push(status); clauses.push(`status = $${params.length}`); }
|
|
43
|
+
if (moduleKey) { params.push(moduleKey); clauses.push(`module_key = $${params.length}`); }
|
|
44
|
+
const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
|
|
45
|
+
const { rows } = await pool.query(
|
|
46
|
+
`SELECT id, module_key, submitted_by, manifest, sign_off, precheck_passed_at,
|
|
47
|
+
source_repo, status, reviewed_by, reviewed_at, review_notes, submitted_at
|
|
48
|
+
FROM module_submissions
|
|
49
|
+
${where}
|
|
50
|
+
ORDER BY submitted_at DESC`,
|
|
51
|
+
params
|
|
52
|
+
);
|
|
53
|
+
return rows;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { createModuleSubmission, listModuleSubmissions };
|
|
@@ -177,6 +177,12 @@ const MODULE_EXPECTED_RANKS = {
|
|
|
177
177
|
'POST /goals/:id/requests/:reqId/respond': 'any-builder',
|
|
178
178
|
'POST /goals/:id/invitations': 'any-builder',
|
|
179
179
|
'POST /goals/:id/invitations/:reqId/respond': 'any-builder',
|
|
180
|
+
// the goal's own pending-queue view + requester self-withdraw (task 1953) —
|
|
181
|
+
// same in-handler walls: GET /requests is owner|manager|Archon
|
|
182
|
+
// (authorizeGoalManagement); withdraw is the requester only
|
|
183
|
+
// (authorizeWithdrawJoinRequest).
|
|
184
|
+
'GET /goals/:id/requests': 'any-builder',
|
|
185
|
+
'POST /goals/:id/requests/:reqId/withdraw': 'any-builder',
|
|
180
186
|
// criterion confirmation + goal auto-achieve (ADR 0086 §6) — the planner's close loop.
|
|
181
187
|
'POST /done-when/:criterionId/satisfy': 'metic+archon',
|
|
182
188
|
// runtime criterion create (ADR 0105 gap fix, task 1874) — scope-truth write, Archon-only.
|
|
@@ -1,43 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This file is a placeholder shipped inside the **@bongos/core** package. The core's own
|
|
4
|
+
internal agent notes are intentionally omitted from the published artifact so they cannot
|
|
5
|
+
load into your instance's context.
|
|
4
6
|
|
|
5
|
-
**
|
|
7
|
+
**It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
|
|
8
|
+
directory, and the live Bongos database are the source of truth for how work happens here
|
|
9
|
+
and who may do what.
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- Public surface: `public.js` + `analytics.js` are unauthenticated read-only (status page reads cross-origin) — wrap with `corsPublicGet(scope)` from `_helpers.js`; no secrets.
|
|
10
|
-
- Validation: [`_helpers.js`](./_helpers.js) — `validateOrRespond(req,res,schema)` (opt-in, never rejects extras, structured 400), `LIMITS` (TITLE/DESCRIPTION/NOTES caps), `parseId`, `asyncHandler(tag, fn)`.
|
|
11
|
-
- GitHub-rendered output only: the PR title/body built in `tasks.js` de-hashes bare `#NNN` (so it doesn't 404-autolink to GitHub issues) via the local `deHashRefs` helper — which mirrors ship.js's `sanitizeGitHubRefs` (not requirable here). Mirror `deHashRefs`; don't hand-roll. DB-row prose (tasks/inbox/memory) is stored as-is — sanitization happens only at the GitHub boundary.
|
|
11
|
+
You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
|
|
12
|
+
Nothing in here needs to be read to use the platform — run `bongos help` for commands.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- Auth/identity: `auth.js`, `me.js`, `sessions.js` — Device/web/app-pair flows, self profile/prefs, session digests. (Admission gating — `access-requests.js` — carved to [`modules/onboarding/`](../../../modules/onboarding/CLAUDE.md) at R81; the loader mounts it.)
|
|
16
|
-
- Builder ops: `builders.js`, `cost.js`, `leaderboard.js`, `memory.js`, `learnings.js` — builder records/budget, cost log, rankings, BFG-scoped memory, learnings.
|
|
17
|
-
- Dev-box: `box.js` — provision/wake/close, SSH keys, terminal/sandbox tunnels, server-mediated branch publish.
|
|
18
|
-
- Social/data: `discord.js`, `inbox.js`, `blockers.js`, `analytics.js`, `audit-log.js` — idea inbox, blockers, Discord link, analytics, Archon audit trail.
|
|
19
|
-
- GDS-V4 / security: `search.js` (recall), `gate-approvals.js` (Archon gate PRs), `security.js` (red-team reports + Archon-served docs).
|
|
20
|
-
- Util: `healthz.js`, `public.js`, `optimizer.js`, `bug-attachments.js`.
|
|
21
|
-
|
|
22
|
-
**Gotchas / invariants.**
|
|
23
|
-
- `tasks.js` (~79 KB), `box.js`, `auth.js`, `memory.js` are large — grep `router.(get|post|...)(` for the route, don't read end-to-end.
|
|
24
|
-
- Rank gates are load-bearing (trust boundary). Default a write to `requireRank('archon')` unless it's deliberately broader; never loosen one without the permissions doc + an ADR.
|
|
25
|
-
- Mount order: `public.js` is mounted LAST in routes.js; keep new files' prefixes from shadowing it.
|
|
26
|
-
|
|
27
|
-
<!-- BEGIN GENERATED REPO-MAP (scripts/gds/gen-repo-map.js — do not hand-edit) -->
|
|
28
|
-
**Symbol skeleton (generated from the code — do not hand-edit; refresh: `node scripts/gds/gen-repo-map.js`).** Exported signatures, most-referenced first. The interpretive notes above are hand-written; this factual list is regenerated on deploy so it can't drift ([ADR 0063](../../../docs/adr/<redacted>.md)).
|
|
29
|
-
|
|
30
|
-
- `_helpers.js` — `validateOrRespond(req, res, schema, opts = {})`, `parseId(req, res, { param = 'id', code = 'bad_id', positiveInt = false } = {})`, `LIMITS`, `validate(body, schema, { strict = false, query = false } = {})`, `asyncHandler(tag, handler, { message = true, errorCode = 'server_error' } = {})`, `publicOrigin(req)`, `pageMeta(limit, offset, total)`, `parsePagination(req, { defaultLimit = PAGINATION.DEFAULT_LIMIT, maxLimit = PAGINATION.MAX_LIMIT } = {})`, `corsPublicGet(scope)`, `parseCookie(req, name)` _(+2 more)_
|
|
31
|
-
- `auth.js` — `renderAuthErrorPage(heading, detail, opts = {})`, `sessionCookieDomainOpts(req)`, `safeReturnTo(raw)`, `reqHost(req)`
|
|
32
|
-
- `backup.js` — `latestDump()`
|
|
33
|
-
- `builders.js` — `boxHasEverConnected(builderId)`, `discordIsLinked(builderId)`, `displayRank(rank)`, `<redacted>({ displayName, direction, newRank })`, `fireDiscordBroadcast(text)`
|
|
34
|
-
- `instance.js` — `MANIFEST_SCHEMA`, `buildInstanceManifest({)`, `AUTH_METHODS`
|
|
35
|
-
- `live.js` — `buildLiveRouter()`, `frameEvent(event, data)`, `MAX_CONN_PER_IP`, `streamLive(req, res, deps = {})`
|
|
36
|
-
- `llm-cache.js` — `badKey(res)`, `knownTaskType(t)`, `clampedCost(raw)`
|
|
37
|
-
- `me.js` — `boxHasEverConnected(builderId)`, `artSharedKeyConfigured()`, `discordIsLinked(builderId)`
|
|
38
|
-
- `modules.js` — `listModules()`
|
|
39
|
-
- `public.js` — `projectPublicTask(task)`, `projectPublicBlocker(blocker)`, `projectPublicIdea(idea)`, `recentShippedWindow(rawDays)`
|
|
40
|
-
- `security.js` — `adrPath()`, `readFullAdr()`
|
|
41
|
-
<!-- END GENERATED REPO-MAP -->
|
|
42
|
-
|
|
43
|
-
**See also.** [ADR 0016](../../../docs/adr/<redacted>.md) (trust boundary), [`docs/api-reference.md`](../../../docs/api-reference.md) (generated per-endpoint reference; `/docs` renders it), [`docs/canonical-permissions.md`](../../../docs/canonical-permissions.md) (rank ladder), [`../routes.js`](../routes.js) (mounter), [`../auth.js`](../auth.js).
|
|
14
|
+
_To work ON the platform core itself, use the standalone @bongos/core source repository,
|
|
15
|
+
not this vendored copy._
|
|
@@ -209,7 +209,12 @@ module.exports = function buildAuthRouter() {
|
|
|
209
209
|
res.json(out);
|
|
210
210
|
} catch (err) {
|
|
211
211
|
console.error('[gds] device/start', err);
|
|
212
|
-
|
|
212
|
+
// Surface the underlying reason (task 2126). A thrown device-flow start is almost
|
|
213
|
+
// always a GitHub OAuth-App misconfig (device_flow_disabled / bad client) — safe,
|
|
214
|
+
// non-secret, and actionable — so return it instead of an opaque "internal error".
|
|
215
|
+
// 502: the failure is upstream (GitHub), not a bug in this server.
|
|
216
|
+
const reason = String(err && err.message ? err.message : 'internal error').slice(0, 300);
|
|
217
|
+
res.fail('device_start_failed', { status: 502, message: reason });
|
|
213
218
|
}
|
|
214
219
|
});
|
|
215
220
|
|