@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
|
@@ -376,6 +376,32 @@ function authorizeJoinRequestResponse({ goal, actorId, actorRank, actorMembershi
|
|
|
376
376
|
return { ok: true };
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
// authorizeWithdrawJoinRequest — the gate for POST /goals/:id/requests/:reqId/withdraw
|
|
380
|
+
// (task 1953): ONLY the requester may withdraw their OWN pending join-request — a
|
|
381
|
+
// symmetric mirror of authorizeInvitationResponse (the invitee resolves their own
|
|
382
|
+
// invite; here the requester resolves their own request, self-served instead of
|
|
383
|
+
// waiting on an owner/manager deny). Ordering: request_not_found → wrong_goal →
|
|
384
|
+
// not_a_join_request → not_pending → not_your_request. PURE + exported.
|
|
385
|
+
function authorizeWithdrawJoinRequest({ request, goalId, actorId }) {
|
|
386
|
+
if (!request) return { ok: false, status: 404, body: { error: 'request_not_found' } };
|
|
387
|
+
if (String(request.goal_id) !== String(goalId)) {
|
|
388
|
+
return { ok: false, status: 404, body: { error: 'request_not_found', message: 'No such request on this goal.' } };
|
|
389
|
+
}
|
|
390
|
+
if (request.kind !== 'request') {
|
|
391
|
+
return { ok: false, status: 400, body: { error: 'not_a_join_request', message: 'That row is an invitation, not a join-request.' } };
|
|
392
|
+
}
|
|
393
|
+
if (request.status !== 'pending') {
|
|
394
|
+
return { ok: false, status: 409, body: { error: 'not_pending', status: request.status } };
|
|
395
|
+
}
|
|
396
|
+
if (String(request.builder_id) !== String(actorId)) {
|
|
397
|
+
return { ok: false, status: 403, body: {
|
|
398
|
+
error: 'not_your_request',
|
|
399
|
+
message: 'Only the requester may withdraw their own join-request.',
|
|
400
|
+
} };
|
|
401
|
+
}
|
|
402
|
+
return { ok: true };
|
|
403
|
+
}
|
|
404
|
+
|
|
379
405
|
module.exports = function buildGoalsRouter() {
|
|
380
406
|
const router = express.Router();
|
|
381
407
|
|
|
@@ -798,6 +824,57 @@ module.exports = function buildGoalsRouter() {
|
|
|
798
824
|
}
|
|
799
825
|
});
|
|
800
826
|
|
|
827
|
+
// GET /goals/:id/requests — the goal's own pending invite/join-request queue
|
|
828
|
+
// (task 1953): every pending row on THIS goal, both kinds — outstanding invites
|
|
829
|
+
// the goal has sent (awaiting the invitee) and join-requests awaiting approval.
|
|
830
|
+
// db.listPendingRequestsForGoal was built for this route in task 1952's landing
|
|
831
|
+
// but never wired to HTTP; this is the goals-page manager view alongside the
|
|
832
|
+
// cross-goal GET /goals/inbox.
|
|
833
|
+
// rank: any-builder (requireBuilder) + the in-handler owner|manager|Archon gate
|
|
834
|
+
// (authorizeGoalManagement) — same wall as visibility/invite/request-respond.
|
|
835
|
+
router.get('/goals/:id/requests', auth.requireBuilder, async (req, res) => {
|
|
836
|
+
const id = parseId(req, res, { code: 'bad_goal_id' });
|
|
837
|
+
if (id === null) return;
|
|
838
|
+
try {
|
|
839
|
+
const goal = await db.getGoal(id);
|
|
840
|
+
const actorMembership = goal
|
|
841
|
+
? await db.getGoalMember({ goalId: id, builderId: req.builder.id })
|
|
842
|
+
: null;
|
|
843
|
+
const decision = authorizeGoalManagement({
|
|
844
|
+
goal, actorId: req.builder.id, actorRank: req.builder.rank, actorMembership,
|
|
845
|
+
});
|
|
846
|
+
if (!decision.ok) return res.status(decision.status).json(decision.body);
|
|
847
|
+
const requests = await db.listPendingRequestsForGoal(id);
|
|
848
|
+
return res.json({ requests });
|
|
849
|
+
} catch (err) {
|
|
850
|
+
console.error('[gds] GET /goals/:id/requests', err);
|
|
851
|
+
return res.fail('fetch_failed', { status: 500, message: 'internal error' });
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// POST /goals/:id/requests/:reqId/withdraw — the requester cancels their OWN
|
|
856
|
+
// pending join-request (task 1953): the symmetric self-served mirror of a
|
|
857
|
+
// manager's deny. Sets status='cancelled' (kept as the audit trail, like a
|
|
858
|
+
// deny's 'rejected'); never touches an invitation row.
|
|
859
|
+
// rank: any-builder (requireBuilder) + the in-handler authorizeWithdrawJoinRequest.
|
|
860
|
+
router.post('/goals/:id/requests/:reqId/withdraw', auth.requireBuilder, async (req, res) => {
|
|
861
|
+
const id = parseId(req, res, { code: 'bad_goal_id' });
|
|
862
|
+
if (id === null) return;
|
|
863
|
+
const reqId = parseId(req, res, { param: 'reqId', code: 'bad_request_id', positiveInt: true });
|
|
864
|
+
if (reqId === null) return;
|
|
865
|
+
try {
|
|
866
|
+
const request = await db.getMembershipRequest(reqId);
|
|
867
|
+
const decision = authorizeWithdrawJoinRequest({ request, goalId: id, actorId: req.builder.id });
|
|
868
|
+
if (!decision.ok) return res.status(decision.status).json(decision.body);
|
|
869
|
+
const resolved = await db.resolveMembershipRequest({ id: reqId, status: 'cancelled', resolvedBy: req.builder.id });
|
|
870
|
+
if (!resolved) return res.fail('not_pending', { status: 409, message: 'The request changed underneath this withdrawal — re-read and retry.' });
|
|
871
|
+
return res.json({ ok: true, request: resolved });
|
|
872
|
+
} catch (err) {
|
|
873
|
+
console.error('[gds] POST /goals/:id/requests/:reqId/withdraw', err);
|
|
874
|
+
return res.fail('withdraw_failed', { status: 500, message: 'internal error' });
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
801
878
|
// POST /goals/:id/members — the goal's own authority (owner | manager | Archon)
|
|
802
879
|
// invites a builder to the goal (task 1762: widened from Archon-only so a
|
|
803
880
|
// manager doesn't need an Archon in the loop for a normal goal). This stays the
|
|
@@ -1417,3 +1494,6 @@ module.exports.protectedScopeAdmissionBlock = protectedScopeAdmissionBlock;
|
|
|
1417
1494
|
module.exports.authorizeCreateJoinRequest = authorizeCreateJoinRequest;
|
|
1418
1495
|
module.exports.authorizeInvitationResponse = authorizeInvitationResponse;
|
|
1419
1496
|
module.exports.authorizeJoinRequestResponse = authorizeJoinRequestResponse;
|
|
1497
|
+
// task 1953: the withdraw gate — exported for tests/goal_membership_requests.mjs,
|
|
1498
|
+
// same precedent as its siblings above.
|
|
1499
|
+
module.exports.authorizeWithdrawJoinRequest = authorizeWithdrawJoinRequest;
|
package/modules/memory/CLAUDE.md
CHANGED
|
@@ -1,40 +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
|
-
|
|
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.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Files
|
|
12
|
-
|
|
13
|
-
| File | What |
|
|
14
|
-
|---|---|
|
|
15
|
-
| `memory.js` | per-builder memory store (quotas, BFG namespace, store/fetch/list). Manages its own transactions via the doorway `pool`. |
|
|
16
|
-
| `search.js` | the recall query engine — FTS + trigram, RRF fusion, rank-scoped (ADR 0060). |
|
|
17
|
-
| `search-chunker.js` | pure markdown→chunk splitter (no DB). Also used by the `search-ingest-*` CLIs (memory-owned scripts). |
|
|
18
|
-
| `learnings.js` | durable paid-once knowledge capture (the `learnings` table). |
|
|
19
|
-
| `routes/memory.js` | `/memory/*` — sync/read/forensic. Carries its own larger `express.json` for `/memory/sync` (1 MB batches); core `routes.js` still skips the global parser for that path. |
|
|
20
|
-
| `routes/search.js` | `/search` (recall) + `/search/mark` (the ADR 0095 C3 usefulness vote — `POST {chunk_id, useful?}`, session-scoped). |
|
|
21
|
-
| `routes/learnings.js` | `/learnings` (POST capture + GET). |
|
|
22
|
-
|
|
23
|
-
All four lib files reach Postgres through the doorway `pool`; the routes reach auth gates + the kernel db helpers (`getBuilderById`, `insertAuditLog`) + the shared request validators (`validateOrRespond`/`LIMITS`) off the doorway `api`, and the lib files via intra-module relative requires.
|
|
24
|
-
|
|
25
|
-
## Tables (owned, not moved)
|
|
26
|
-
|
|
27
|
-
`builder_memory`, `memory_search_chunks`, `learnings` (+ their migrations) were created before the module system and stay in core `migrations/` (the schema-migrations stem-key makes a later relocation a no-op). The module owns the *queries*; any **new** memory migration belongs in `modules/memory/migrations/` namespaced `memory_*` (the additive rule).
|
|
28
|
-
|
|
29
|
-
## Seams
|
|
30
|
-
|
|
31
|
-
None yet — memory neither provides nor consumes a port/event. Core reads memory only through its HTTP routes (the hall's sync/recall), never a direct import.
|
|
32
|
-
|
|
33
|
-
## NOT in this module
|
|
34
|
-
|
|
35
|
-
- **`bfg.js`** — the BFG memory-hygiene evaluator stays core (it scores memory quality across builders); it touches memory through the routes/store contract, not a sibling import.
|
|
36
|
-
- **`migrations/`** — the memory tables' creating migrations stay in core `migrations/` (pre-module-system; see Tables above).
|
|
37
|
-
|
|
38
|
-
## Tests
|
|
39
|
-
|
|
40
|
-
`tests/memory_isolation.mjs`, `tests/bfg*.mjs`, `tests/search_*.mjs`, `tests/search_chunker.mjs`, `tests/diff_derived_signals.mjs`, `tests/recall_*.mjs` import from `modules/memory/…`. The ADR 0095 C3 mark/rank loop's DB-backed cases live in the `tests/search_isolation.mjs` Layer C class (integration gate); the pure ranker-factor + scope logic is exercised by the in-module assertions in `markChunkUsefulness`.
|
|
14
|
+
_To work ON the platform core itself, use the standalone @bongos/core source repository,
|
|
15
|
+
not this vendored copy._
|
|
@@ -1,55 +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
|
-
- **the friction analyzer** ([`onboarding.js`](onboarding.js)) — the *pure* half of "feedback without asking" (V3.R79 / C1): parse a consented onboarding transcript, compute deterministic friction signals, build the subagent prompt, validate its JSON, shape the `idea_inbox` payload. No FS/network/DB (those live in `scripts/gds/onboarding-analyze.js`).
|
|
10
|
-
- **the evergreen newcomer-chore restock** ([`newcomer-restock.js`](newcomer-restock.js) + [`db.js`](db.js)) — a fresh first-builder task is always waiting: claiming a chore instance stamps a replacement synchronously, in the claim request. Template content lives in `scripts/gds/newcomer-floor.templates.json`; the supply read (`newcomerInstanceStats`) is the module's db slice.
|
|
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.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
This module reaches core **only** through the published doorway [`../../src/module-api.js`](../../src/module-api.js) (`coreVersion ^1.9.0`). It must **never** `require()` a core internal directly, and never another module. Core, in turn, must never `require()` a file in here — it reaches the state-machine + restock surface only through the `onboarding` port (below). Both directions are enforced by the BV1.R44/R45 fitness checks.
|
|
15
|
-
|
|
16
|
-
## The `onboarding` kernel PORT
|
|
17
|
-
|
|
18
|
-
The novel shape of this carve: it is the FIRST **core → module** port the LIFECYCLE drives (alongside the economy `reward` port). The onboarding state machine is ticked from inside core's claim/ship transactions, so the boundary can't be "module reaches into core" (the builder-settings pattern) — it's "core reaches the module's state machine through a kernel port."
|
|
19
|
-
|
|
20
|
-
- **Provides — port `onboarding`** → the surface in [`port.js`](port.js): `{ getState, shapeForApi, safeMarkStage, markStage, broadcastGraduation, restockForClaim, restockAll, SOURCE_TAG }`. Registered in [`routes/onboarding.js`](routes/onboarding.js)'s factory (the loader runs it at boot, and again whenever a test rebuilds the GDS router — `hasProvider`-guarded). Mirrors `modules/economy/routes/reward.js`.
|
|
21
|
-
- **The core consumers resolve it WITHOUT importing this module** (all via `seams.resolveOptional('onboarding')`):
|
|
22
|
-
- `src/bongos/db.js` — `claimTask`/`shipTask` tick `markStage` INSIDE their transaction (passing the txn `client`) and fire `broadcastGraduation` after COMMIT. The transaction-participant direction.
|
|
23
|
-
- `src/bongos/auth.js` — the Device-Flow + web auth callbacks mark `auth_complete` (`safeMarkStage`).
|
|
24
|
-
- `src/bongos/routes/me.js` — `GET /me` reads+shapes the checklist (`getState`/`shapeForApi`); `PATCH /me/disciplines` marks `pick_disciplines`.
|
|
25
|
-
- `src/bongos/routes/builders.js` — `GET /builders/:id/onboarding` reads+shapes the roster-visible checklist.
|
|
26
|
-
- `src/bongos/routes/tasks.js` — `GET /tasks/claimable` marks `first_start`; `POST /tasks/newcomer-restock` is the Archon backstop (`restockAll`).
|
|
27
|
-
- `src/bongos/routes/claims.js` — `POST /claims` restocks the chore shelf the instant one is claimed (`restockForClaim`/`SOURCE_TAG`).
|
|
28
|
-
- All degrade gracefully when the module is off (a vanilla instance): the state-machine ticks become best-effort no-ops and the restock is skipped (the chore shelf simply isn't maintained), exactly as the surrounding try/catch already tolerated. The doorway gained one additive export (`enabledDisciplines`, CORE_VERSION → 1.9.0) the restock backstop needs; the onboarding capability itself is the port, not a doorway export.
|
|
29
|
-
|
|
30
|
-
## Files
|
|
31
|
-
|
|
32
|
-
| File | What |
|
|
33
|
-
|---|---|
|
|
34
|
-
| `onboarding-state.js` | the server-ticked onboarding state machine (markStage/getState/shapeForApi/safeMarkStage/broadcastGraduation/requireStageCleared + the displayed-checklist shaping). Pure of core `require()` — moved verbatim. |
|
|
35
|
-
| `onboarding.js` | the *pure* friction analyzer (transcript parse → signals → prompt → validate → idea payload). Reads `branding().llmProjectName` through the doorway (de-branded in BV1.R76). |
|
|
36
|
-
| `newcomer-restock.js` | the on-claim chore stamper — template load + per-template floor + monotonic-seq stamping. Reaches `createTask`/`newcomerInstanceStats` via the module `db.js`; `enabledDisciplines` via the doorway. |
|
|
37
|
-
| `db.js` | the onboarding db slice — `newcomerInstanceStats` (the chore-supply read; pool via the doorway) + a `createTask` re-export off the doorway so the stamper's call site stays byte-identical. |
|
|
38
|
-
| `approval-broadcast.js` | the `#member-approvals` outbound (the admission route's sole consumer; travels with it). Posts via `scripts/discord/lib` (a scripts/ require — outside the boundary). |
|
|
39
|
-
| `port.js` | the `onboarding` port provider surface — DELEGATES (does not spread) to onboarding-state / newcomer-restock so test stubs bite at call time. |
|
|
40
|
-
| `routes/onboarding.js` | the module route factory: registers the `onboarding` port + mounts the admission router. |
|
|
41
|
-
| `routes/access-requests.js` | the admission cold-start endpoints (the public POST + the two Archon-only resolves). Auth/pool/validators via the doorway; approval-broadcast intra-module. |
|
|
42
|
-
|
|
43
|
-
## NOT in this module (deliberately left in core)
|
|
44
|
-
|
|
45
|
-
- **The admission GATE READS** — `db.hasInvitedAccessRequest` / `getBuilderByGithubId` stay core `db.js`: they are KERNEL identity/admission reads the trust boundary (`auth.js`) runs at first sign-in, not the onboarding state/restock domain (mirrors the Gemini-key staying core in the builder-settings carve).
|
|
46
|
-
- **The `GET /me` + `GET /builders/:id/onboarding` AGGREGATORS** — `routes/me.js` / `routes/builders.js` stay core; they resolve this port for their onboarding slices (the facade precedent, 02-module-map §2).
|
|
47
|
-
- **The onboarding-domain CLIs** — `scripts/gds/setup.js`, `onboarding-config.js`, `onboarding-analyze.js`, `newcomer-floor.js` travel with the domain but stay under `scripts/` and require the module directly (scripts are outside the kernel boundary — the grading/economy precedent).
|
|
48
|
-
|
|
49
|
-
## Tables
|
|
50
|
-
|
|
51
|
-
None owned. `builder_onboarding_state` (045) + `access_requests` live on the CORE schema and stay in core `migrations/`. This module owns the *onboarding-state queries* (inside `onboarding-state.js`) + the chore-supply read; any **new** onboarding migration belongs in `modules/onboarding/migrations/` namespaced `onboarding_*` (the additive rule).
|
|
52
|
-
|
|
53
|
-
## Tests
|
|
54
|
-
|
|
55
|
-
`tests/onboarding_state.mjs`, `tests/onboarding_gate.mjs`, `tests/onboarding_route_signals.mjs`, and `tests/newcomer_restock.mjs` import from `modules/onboarding/…` (the route-signal test registers the `onboarding` port). `tests/discord_approvals.mjs` imports the moved `approval-broadcast.js`. The doorway/boundary checks discover `onboarding` as a module. See [ADR 0093](../../docs/adr/<redacted>.md).
|
|
14
|
+
_To work ON the platform core itself, use the standalone @bongos/core source repository,
|
|
15
|
+
not this vendored copy._
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
|
+
|
|
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.
|
|
6
|
+
|
|
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.
|
|
10
|
+
|
|
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.
|
|
13
|
+
|
|
14
|
+
_To work ON the platform core itself, use the standalone @bongos/core source repository,
|
|
15
|
+
not this vendored copy._
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
-- provisioning_001_tables.sql — the `provisioning` module's DB slice (ADR 0111 §1,
|
|
2
|
+
-- task #1942 / BONGOS-V1 goal 32). Instance hosting + domains: the web-tier REQUEST
|
|
3
|
+
-- + STATE surface for standing up + operating a Cloud Bongos instance's own infra.
|
|
4
|
+
--
|
|
5
|
+
-- These three tables mirror the dev-box lifecycle ledger (migrations 067/070), one
|
|
6
|
+
-- layer up — a whole INSTANCE instead of a builder's box:
|
|
7
|
+
-- • provisioning_instances — one row per instance being provisioned/operated.
|
|
8
|
+
-- • provisioning_intents — the trust-boundary-safe request queue (the box_intents
|
|
9
|
+
-- analogue): the web tier only ENQUEUES; the control-plane
|
|
10
|
+
-- runner (scripts/gds/provision.js, task P3) drains it. The
|
|
11
|
+
-- web process holds NO cloud tokens (ADR 0111 §2 / ADR 0016).
|
|
12
|
+
-- • provisioning_events — an append-only audit ledger of provisioning actions
|
|
13
|
+
-- (direct control-plane infra mutations are project-state
|
|
14
|
+
-- changes too — CLAUDE.md hard rule — so they get a row).
|
|
15
|
+
--
|
|
16
|
+
-- Module migrations are ADDITIVE and NAMESPACED: every object lives under the
|
|
17
|
+
-- provisioning_ prefix, there is no down-migration, and the file itself is
|
|
18
|
+
-- provisioning_-prefixed (ADR 0108 §5) so its stem can never collide with a core /
|
|
19
|
+
-- instance / other-module migration across repos. `scripts/migrate.sh` discovers +
|
|
20
|
+
-- applies this ONLY when the provisioning module is enabled in config/modules.json.
|
|
21
|
+
--
|
|
22
|
+
-- Idempotent: safe to re-run.
|
|
23
|
+
|
|
24
|
+
-- ---------------------------------------------------------------------------
|
|
25
|
+
-- provisioning_instances — one row per instance an owner is provisioning/operating.
|
|
26
|
+
-- ---------------------------------------------------------------------------
|
|
27
|
+
CREATE TABLE IF NOT EXISTS provisioning_instances (
|
|
28
|
+
id bigserial PRIMARY KEY,
|
|
29
|
+
|
|
30
|
+
-- The owner (a builder in THIS GDS) who requested + operates this instance.
|
|
31
|
+
owner_builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE CASCADE,
|
|
32
|
+
|
|
33
|
+
-- A DNS/db-safe slug naming the instance (e.g. 'mercury'). The stable key an
|
|
34
|
+
-- owner + the port reference it by; also the base for its DB name + hostname.
|
|
35
|
+
slug text NOT NULL UNIQUE,
|
|
36
|
+
|
|
37
|
+
-- The target repo/ref this instance runs (e.g. 'owner/repo'). Advisory metadata
|
|
38
|
+
-- the runner reads; NULL until supplied.
|
|
39
|
+
target_ref text,
|
|
40
|
+
|
|
41
|
+
-- Hosting shape (ADR 0111 §3/§6). Cheapest-first DNA:
|
|
42
|
+
-- co-tenant — a process + DB on an EXISTING box (the cloudbongos.com model, $0 extra)
|
|
43
|
+
-- dedicated — its own DigitalOcean droplet (only when justified)
|
|
44
|
+
hosting_shape text NOT NULL DEFAULT 'co-tenant'
|
|
45
|
+
CHECK (hosting_shape IN ('co-tenant', 'dedicated')),
|
|
46
|
+
|
|
47
|
+
-- Bring-your-own-domain first (ADR 0111 §4): the apex/host the owner already owns
|
|
48
|
+
-- (or a delegated zone). NULL until supplied. Registrar PURCHASE is deferred.
|
|
49
|
+
domain text,
|
|
50
|
+
|
|
51
|
+
-- The instance's own database name (createdb <db_name>; GDS-only migrate).
|
|
52
|
+
db_name text,
|
|
53
|
+
|
|
54
|
+
-- Allocated port for a co-tenant process (NULL for a dedicated droplet / unallocated).
|
|
55
|
+
port int,
|
|
56
|
+
|
|
57
|
+
-- The droplet size tier for a dedicated shape (a SIZE_CATALOG slug); NULL co-tenant.
|
|
58
|
+
tier text,
|
|
59
|
+
|
|
60
|
+
-- Lifecycle state machine (mirrored in modules/provisioning/provisioning.js
|
|
61
|
+
-- INSTANCE_STATES):
|
|
62
|
+
-- requested — the owner asked; a 'provision' intent is (or will be) enqueued
|
|
63
|
+
-- provisioning — the control-plane runner is standing it up
|
|
64
|
+
-- active — running + serving
|
|
65
|
+
-- tearing_down — a 'teardown' intent is being executed
|
|
66
|
+
-- torn_down — fully de-provisioned
|
|
67
|
+
-- error — a provisioning op failed; needs operator attention
|
|
68
|
+
status text NOT NULL DEFAULT 'requested'
|
|
69
|
+
CHECK (status IN ('requested', 'provisioning', 'active',
|
|
70
|
+
'tearing_down', 'torn_down', 'error')),
|
|
71
|
+
|
|
72
|
+
-- Monthly cost estimate (USD) for visibility (ADR 0111 §6). Co-tenant = 0;
|
|
73
|
+
-- dedicated = the tier's SIZE_CATALOG monthly rate. NOT a bill — just visibility.
|
|
74
|
+
cost_estimate_usd numeric(10,4) NOT NULL DEFAULT 0,
|
|
75
|
+
|
|
76
|
+
error_note text,
|
|
77
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
78
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
79
|
+
provisioned_at timestamptz,
|
|
80
|
+
torn_down_at timestamptz
|
|
81
|
+
);
|
|
82
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_instances_owner
|
|
83
|
+
ON provisioning_instances (owner_builder_id);
|
|
84
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_instances_status
|
|
85
|
+
ON provisioning_instances (status);
|
|
86
|
+
|
|
87
|
+
-- ---------------------------------------------------------------------------
|
|
88
|
+
-- provisioning_intents — the provision / teardown request queue (the
|
|
89
|
+
-- trust-boundary-safe bridge between the web process and the control plane).
|
|
90
|
+
-- The web side only ever RECORDS a request here; scripts/gds/provision.js (task
|
|
91
|
+
-- P3, the sole holder of the DO/Cloudflare tokens) drains it. The web process
|
|
92
|
+
-- stays powerless over infra — it can ask, never act (ADR 0111 §2 / ADR 0016).
|
|
93
|
+
-- ---------------------------------------------------------------------------
|
|
94
|
+
CREATE TABLE IF NOT EXISTS provisioning_intents (
|
|
95
|
+
id bigserial PRIMARY KEY,
|
|
96
|
+
instance_id bigint NOT NULL REFERENCES provisioning_instances(id) ON DELETE CASCADE,
|
|
97
|
+
|
|
98
|
+
-- Only the two SAFE, owner/Archon-initiated ops are queueable from the web:
|
|
99
|
+
-- 'provision' (stand up) and 'teardown' (an explicit tear-down the owner/Archon
|
|
100
|
+
-- asked for). No implicit destructive op is ever queued.
|
|
101
|
+
action text NOT NULL CHECK (action IN ('provision', 'teardown')),
|
|
102
|
+
|
|
103
|
+
-- pending → waiting for the runner to pick it up
|
|
104
|
+
-- running → the runner claimed it and is doing the control-plane work
|
|
105
|
+
-- done → the op succeeded
|
|
106
|
+
-- error → the op failed (last_error carries why); the row is no longer "open"
|
|
107
|
+
-- so the partial unique index below lets a fresh intent (retry) through.
|
|
108
|
+
state text NOT NULL DEFAULT 'pending'
|
|
109
|
+
CHECK (state IN ('pending', 'running', 'done', 'error')),
|
|
110
|
+
|
|
111
|
+
-- Who asked. 'api:self' for an owner's own request; 'api:archon:<id>' for an
|
|
112
|
+
-- Archon force action; a runner/control-plane tag otherwise. Audit only.
|
|
113
|
+
requested_by text,
|
|
114
|
+
|
|
115
|
+
-- Retry accounting. The runner bumps attempts each claim; a small cap (enforced
|
|
116
|
+
-- in the runner, not here) stops a poison intent from looping forever.
|
|
117
|
+
attempts int NOT NULL DEFAULT 0,
|
|
118
|
+
last_error text,
|
|
119
|
+
|
|
120
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
121
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
122
|
+
resolved_at timestamptz
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
-- At most ONE open (pending|running) intent per instance — a repeated request
|
|
126
|
+
-- collides here and the route returns the already-open intent instead of piling
|
|
127
|
+
-- up a redundant queue. Resolved (done|error) rows are exempt, so a retry after a
|
|
128
|
+
-- failure is always allowed (mirrors ux_box_intents_open).
|
|
129
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_provisioning_intents_open
|
|
130
|
+
ON provisioning_intents (instance_id)
|
|
131
|
+
WHERE state IN ('pending', 'running');
|
|
132
|
+
|
|
133
|
+
-- The runner's hot path: "oldest pending intent". A partial index keeps it tiny
|
|
134
|
+
-- (it only ever indexes the unresolved tail, not the full history).
|
|
135
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_intents_pending
|
|
136
|
+
ON provisioning_intents (created_at)
|
|
137
|
+
WHERE state = 'pending';
|
|
138
|
+
|
|
139
|
+
-- ---------------------------------------------------------------------------
|
|
140
|
+
-- provisioning_events — append-only audit/log of provisioning actions. The HTTP
|
|
141
|
+
-- audit_log captures only API mutations; the real provisioning ops run on the
|
|
142
|
+
-- control-plane runner, so they record here (like box_events for the box lifecycle).
|
|
143
|
+
-- ---------------------------------------------------------------------------
|
|
144
|
+
CREATE TABLE IF NOT EXISTS provisioning_events (
|
|
145
|
+
id bigserial PRIMARY KEY,
|
|
146
|
+
instance_id bigint NOT NULL REFERENCES provisioning_instances(id) ON DELETE CASCADE,
|
|
147
|
+
owner_builder_id bigint REFERENCES builders(id) ON DELETE SET NULL,
|
|
148
|
+
-- request | provision | active | teardown | torn_down | cost | error | ...
|
|
149
|
+
event text NOT NULL,
|
|
150
|
+
detail text,
|
|
151
|
+
cost_usd numeric(10,4),
|
|
152
|
+
-- Who/what triggered it: 'api:self' | 'api:archon:<id>' | 'runner' | ...
|
|
153
|
+
actor text,
|
|
154
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
155
|
+
);
|
|
156
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_events_instance
|
|
157
|
+
ON provisioning_events (instance_id, created_at DESC);
|
|
158
|
+
|
|
159
|
+
INSERT INTO schema_migrations (version) VALUES ('provisioning_001_tables')
|
|
160
|
+
ON CONFLICT (version) DO NOTHING;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- provisioning_002_standalone_shape.sql — widen provisioning_instances.hosting_shape
|
|
2
|
+
-- to allow 'standalone' (ADR 0108 / ADR 0111, BONGOS-V1 goal 32/35, task 1978).
|
|
3
|
+
--
|
|
4
|
+
-- A standalone instance runs from its OWN repo checkout with a pinned @cloudbongos/core
|
|
5
|
+
-- npm dep (the ADR 0108 destination model), distinct from the co-tenant shape (which
|
|
6
|
+
-- shares the control-plane checkout) and dedicated (its own droplet). Widening a CHECK
|
|
7
|
+
-- enum is ADDITIVE — it only permits MORE values; no existing row changes, no data is
|
|
8
|
+
-- lost — so it is safe under the module-owned-migration rule (ADR 0083 §Decision #5).
|
|
9
|
+
ALTER TABLE provisioning_instances DROP CONSTRAINT IF EXISTS provisioning_instances_hosting_shape_check;
|
|
10
|
+
ALTER TABLE provisioning_instances ADD CONSTRAINT provisioning_instances_hosting_shape_check
|
|
11
|
+
CHECK (hosting_shape IN ('co-tenant', 'dedicated', 'standalone'));
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
-- provisioning_003_github_app_manifest.sql — the GitHub App Manifest onboarding flow
|
|
2
|
+
-- (ADR 0133, BONGOS-V1 goal 35 / criterion 165, task 2080).
|
|
3
|
+
--
|
|
4
|
+
-- WHY. The last human friction in greenfield onboarding was: the owner had to hand-
|
|
5
|
+
-- create a GitHub OAuth App, generate a client secret, and hand it to the box
|
|
6
|
+
-- (oauth-secret.js). GitHub has NO API to create an OAuth App — but it DOES have the
|
|
7
|
+
-- GitHub App *Manifest* flow: the owner clicks ONE pre-filled link, approves on GitHub,
|
|
8
|
+
-- and GitHub returns a temporary `code` we exchange (control-plane-side) for the
|
|
9
|
+
-- client_id + client_secret automatically. The secret never touches the browser/chat.
|
|
10
|
+
--
|
|
11
|
+
-- The flow spans TWO processes across the trust boundary (ADR 0111 §2 / ADR 0016):
|
|
12
|
+
-- • the web tier mints a one-time `state` token + stores the row (this table), and
|
|
13
|
+
-- its public callback stores GitHub's `code` when the owner returns;
|
|
14
|
+
-- • the control-plane runner (scripts/gds/provision.js — the sole token-holder)
|
|
15
|
+
-- drains rows in status 'code_received', exchanges the code for creds, and places
|
|
16
|
+
-- them in /etc/<slug>/web.env (the oauth-secret.js path), then verifies /healthz.
|
|
17
|
+
-- Because the two processes don't share memory, the state/code lease is DB-backed (the
|
|
18
|
+
-- merge_lease / builder_sessions expires_at pattern), NOT the in-memory deviceFlows map.
|
|
19
|
+
--
|
|
20
|
+
-- Additive + idempotent + namespaced (ADR 0108 §5). Applied only when the provisioning
|
|
21
|
+
-- module is enabled (config/modules.json). Safe to re-run.
|
|
22
|
+
|
|
23
|
+
-- ---------------------------------------------------------------------------
|
|
24
|
+
-- (1) Widen provisioning_intents.action so the manifest conversion can ride the
|
|
25
|
+
-- existing intent queue if ever needed. Today the runner drains manifests by
|
|
26
|
+
-- scanning this table's status (drainOAuthManifests) — but widening the CHECK
|
|
27
|
+
-- keeps a future intent-driven path open and keeps INTENT_ACTIONS in lockstep.
|
|
28
|
+
-- Widening a CHECK enum is additive (permits MORE values; no row changes).
|
|
29
|
+
-- ---------------------------------------------------------------------------
|
|
30
|
+
ALTER TABLE provisioning_intents DROP CONSTRAINT IF EXISTS provisioning_intents_action_check;
|
|
31
|
+
ALTER TABLE provisioning_intents ADD CONSTRAINT provisioning_intents_action_check
|
|
32
|
+
CHECK (action IN ('provision', 'teardown', 'oauth-manifest'));
|
|
33
|
+
|
|
34
|
+
-- ---------------------------------------------------------------------------
|
|
35
|
+
-- (2) provisioning_oauth_manifests — one row per GitHub-App-Manifest onboarding
|
|
36
|
+
-- attempt for an instance. The `state` is the one-time CSRF token round-tripped
|
|
37
|
+
-- through GitHub; `github_code` is GitHub's temporary conversion code (single-use,
|
|
38
|
+
-- ~1h GitHub-side TTL) the control-plane runner exchanges for the client creds.
|
|
39
|
+
-- ---------------------------------------------------------------------------
|
|
40
|
+
CREATE TABLE IF NOT EXISTS provisioning_oauth_manifests (
|
|
41
|
+
id bigserial PRIMARY KEY,
|
|
42
|
+
instance_id bigint NOT NULL REFERENCES provisioning_instances(id) ON DELETE CASCADE,
|
|
43
|
+
|
|
44
|
+
-- The one-time CSRF `state` token minted by the web tier (crypto.randomBytes hex),
|
|
45
|
+
-- carried to GitHub in the manifest POST and returned on the callback. UNIQUE so a
|
|
46
|
+
-- callback maps to exactly one attempt. Unguessable (256-bit) — it is the callback's
|
|
47
|
+
-- only authorization (GitHub redirects the browser back unauthenticated).
|
|
48
|
+
state text NOT NULL UNIQUE,
|
|
49
|
+
|
|
50
|
+
-- Lifecycle:
|
|
51
|
+
-- pending — link minted; awaiting the owner's click + GitHub's redirect back
|
|
52
|
+
-- code_received — GitHub redirected back with a `code`; awaiting the control-plane runner
|
|
53
|
+
-- converting — the runner claimed the row and is exchanging the code with GitHub
|
|
54
|
+
-- done — creds fetched + placed in web.env; /healthz auth_configured verified
|
|
55
|
+
-- error — conversion or placement failed (last_error carries why)
|
|
56
|
+
-- expired — the `state` TTL passed before the owner completed the round-trip
|
|
57
|
+
status text NOT NULL DEFAULT 'pending'
|
|
58
|
+
CHECK (status IN ('pending', 'code_received', 'converting', 'done', 'error', 'expired')),
|
|
59
|
+
|
|
60
|
+
-- GitHub's temporary manifest-conversion code. Written by the web-tier callback;
|
|
61
|
+
-- consumed by the runner (POST /app-manifests/{code}/conversions). NEVER the secret
|
|
62
|
+
-- itself — only the short-lived code is web-tier-visible; the client_secret is fetched
|
|
63
|
+
-- by the control plane and written straight to web.env, never stored here.
|
|
64
|
+
github_code text,
|
|
65
|
+
|
|
66
|
+
-- The GitHub App id + slug GitHub assigned once converted (audit; the App the owner
|
|
67
|
+
-- created for their instance's sign-in). Filled by the runner on success.
|
|
68
|
+
app_id bigint,
|
|
69
|
+
app_slug text,
|
|
70
|
+
|
|
71
|
+
-- The builder who initiated the attempt (the signed-in owner on the mothership).
|
|
72
|
+
requested_by bigint REFERENCES builders(id) ON DELETE SET NULL,
|
|
73
|
+
|
|
74
|
+
-- Runner retry accounting (a poison conversion never loops forever) + last error.
|
|
75
|
+
attempts int NOT NULL DEFAULT 0,
|
|
76
|
+
last_error text,
|
|
77
|
+
|
|
78
|
+
-- The `state` token's TTL — the owner must complete the GitHub round-trip before this.
|
|
79
|
+
expires_at timestamptz NOT NULL,
|
|
80
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
81
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
82
|
+
resolved_at timestamptz
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
-- The runner's hot path: "oldest manifest awaiting conversion" (status='code_received').
|
|
86
|
+
-- A partial index keeps it tiny (only the unresolved tail).
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_oauth_manifests_ready
|
|
88
|
+
ON provisioning_oauth_manifests (created_at)
|
|
89
|
+
WHERE status = 'code_received';
|
|
90
|
+
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_oauth_manifests_instance
|
|
92
|
+
ON provisioning_oauth_manifests (instance_id, created_at DESC);
|
|
93
|
+
|
|
94
|
+
INSERT INTO schema_migrations (version) VALUES ('provisioning_003_github_app_manifest')
|
|
95
|
+
ON CONFLICT (version) DO NOTHING;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "provisioning",
|
|
3
|
+
"title": "Instance provisioning",
|
|
4
|
+
"description": "Stand up + operate a Cloud Bongos instance's own infrastructure (server, database, domain, DNS, TLS) via the API, so an owner does not manage infra by hand (ADR 0111). Owns the web-tier REQUEST + STATE surface only: it enqueues provisioning intents + reads instance state — it holds NO cloud-provider tokens and calls NO cloud API. A separate control-plane runner (scripts/gds/provision.js, task P3) is the sole token-holder that drains the intents. Distinct from the `hosting` module (ADR 0092), which brokers compute WORKLOADS.",
|
|
5
|
+
"coreVersion": "^1.14.0",
|
|
6
|
+
"default": false,
|
|
7
|
+
"author": "Off the Boats",
|
|
8
|
+
"origin": "Off the Boats (the first Cloud Bongos instance, amazonprimea.com)",
|
|
9
|
+
"license": "AGPL-3.0",
|
|
10
|
+
"maintainer": "Off the Boats maintainers",
|
|
11
|
+
"contributes": {
|
|
12
|
+
"routes": ["provisioning"],
|
|
13
|
+
"migrations": true
|
|
14
|
+
},
|
|
15
|
+
"provides": ["provisioning.instanceState"],
|
|
16
|
+
"consumes": []
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// modules/provisioning/onboard-plan.js — the CANONICAL onboarding step sequence.
|
|
2
|
+
//
|
|
3
|
+
// Goal 35 / criterion 165 names the sequence as the source of truth: "The canonical
|
|
4
|
+
// step sequence is the source of truth; skill/CLI/hall are forms over it." This file
|
|
5
|
+
// is that ONE source. The CLI (`bongos onboard`, scripts/gds/onboard.js), the branded
|
|
6
|
+
// /create landing page, and the hall's new-project wizard all read it — via a direct
|
|
7
|
+
// require (the CLI) or the public GET /provisioning/onboard-plan route (the web) — so
|
|
8
|
+
// the sequence can never drift between the forms (task 2085, anti-drift).
|
|
9
|
+
//
|
|
10
|
+
// PURE — no I/O, no core imports. Safe to require from a script, a route, or a test.
|
|
11
|
+
//
|
|
12
|
+
// Each step: { key, kind: 'auto' | 'human', label }. `kind: 'human'` marks the
|
|
13
|
+
// irreducibly-manual owner actions (create the GitHub repo, wire GitHub sign-in, first
|
|
14
|
+
// sign-in) that every form renders UI-first; `kind: 'auto'` steps the flow performs.
|
|
15
|
+
// The sequence branches at exactly ONE leg — scaffold — into greenfield (`bongos init`,
|
|
16
|
+
// a fresh empty repo) vs brownfield (`bongos init --adopt`, LAYER onto an existing repo;
|
|
17
|
+
// ADR 0121 §Decision 1 / task 2046). Every other leg is identical, so `mode` only
|
|
18
|
+
// relabels the github-repo / init / scaffold steps.
|
|
19
|
+
function onboardPlan(mode = 'greenfield') {
|
|
20
|
+
const adopt = mode === 'adopt';
|
|
21
|
+
return [
|
|
22
|
+
{ key: 'name-address', kind: 'auto', label: 'Decide name + address (interview)' },
|
|
23
|
+
{ key: 'github-repo', kind: 'human', label: adopt ? 'Use your EXISTING GitHub repository' : 'Create the empty GitHub repository' },
|
|
24
|
+
{ key: 'dns-preflight', kind: 'auto', label: 'DNS token preflight — before the address step (task 1980)' },
|
|
25
|
+
{ key: 'init', kind: 'auto', label: adopt ? 'bongos init --adopt — LAYER onto the existing repo (pre-flight + accept)' : 'bongos init — scaffold the instance config' },
|
|
26
|
+
{ key: 'scaffold', kind: 'auto', label: adopt ? 'Layer config + pinned @bongos/core onto the existing repo (no clobber)' : 'Scaffold the standalone repo (own repo + pinned @bongos/core)' },
|
|
27
|
+
{ key: 'provision', kind: 'auto', label: 'Provision core + DB + address + service (standalone, task 1978)' },
|
|
28
|
+
{ key: 'seed-version', kind: 'auto', label: 'Seed the first version (V1) so the instance can take tasks immediately (task 2119)' },
|
|
29
|
+
{ key: 'oauth-app', kind: 'human', label: 'Wire GitHub sign-in — one-click GitHub App (task 2080), or a manual OAuth app' },
|
|
30
|
+
{ key: 'oauth-place', kind: 'auto', label: 'Place the OAuth secret — auto via the Manifest flow, or verified manual (task 1979)' },
|
|
31
|
+
{ key: 'first-sign-in', kind: 'human', label: 'First sign-in (seats the founding Archon)' },
|
|
32
|
+
{ key: 'verify', kind: 'auto', label: 'Verify /healthz auth_configured' },
|
|
33
|
+
{ key: 'brand', kind: 'auto', label: 'Brand the instance look' },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { onboardPlan };
|