@bongos/core 1.20.82 → 1.21.1
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 +154 -64
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/adr/0051-full-session-transcript-corpus.md +3 -1
- package/docs/adr/0361-merges-publish-as-candidates-release-decides-what-others-are-offered.md +3 -3
- package/docs/adr/README.md +1 -1
- package/docs/api/openapi.json +153 -4
- package/docs/api-reference.md +5 -3
- package/docs/architecture.md +3 -0
- package/docs/copy-inventory.md +10 -10
- package/docs/copy-registry.json +12 -12
- package/docs/design/project-startup-direction.md +63 -0
- package/docs/file-map.md +2 -0
- package/docs/module-api-changelog.md +8 -0
- package/docs/page-inventory.json +8 -3
- package/docs/page-readings.json +382 -373
- package/docs/recipes/private-npm-distribution.md +1 -1
- package/migrations/core_269_session_transcripts.sql +35 -0
- package/modules/hall-ui/public/genesis-home.js +17 -3
- package/modules/hall-ui/public/index-genesis-demo.states.json +13 -0
- package/modules/lifecycle/done-when.js +82 -42
- package/modules/lifecycle/workflow-dispatch.js +21 -11
- package/modules/npm-release/release.js +10 -1
- package/modules/onboarding/founding-birth.js +116 -0
- package/modules/onboarding/genesis-home.js +72 -5
- package/modules/onboarding/port.js +9 -2
- package/modules/onboarding/routes/onboarding.js +8 -1
- package/modules/provisioning/birth.js +64 -0
- package/modules/provisioning/dns-resolves.js +54 -0
- package/modules/provisioning/migrations/provisioning_036_dns_resolved.sql +30 -0
- package/modules/provisioning/pollers/liveness-sweep.js +5 -0
- package/modules/provisioning/provisioning.js +7 -6
- package/modules/provisioning/routes/provisioning.js +6 -2
- package/modules/provisioning/tests/liveness.mjs +48 -0
- package/modules/public-landing/public/projects-dns-pending.states.json +85 -0
- package/modules/public-landing/public/projects.html +40 -15
- package/modules/sessions/db.js +58 -0
- package/modules/sessions/routes/sessions.js +97 -0
- package/modules/ui-design/kit/fixtures/founding-genesis-demo.json +176 -0
- package/modules/ui-design/kit/fixtures/me-founding-demo.json +27 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances-demo.json +3 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances-dns-pending.json +49 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances.json +3 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +41 -0
- package/scripts/gds/provision.js +77 -53
- package/scripts/gds/session-digest.js +11 -3
- package/scripts/gds/session-transcript-build.js +155 -0
- package/scripts/gds/session-transcript-upload.js +29 -0
- package/scripts/gds/ship-session-upload.js +6 -1
- package/scripts/gds/upgrade-commit-identity.js +32 -0
- package/scripts/gds/upgrade.js +1 -1
- package/src/bongos/routes.js +3 -0
- package/src/branding.js +9 -0
- package/src/module-api.js +1 -1
- package/tests/auto_satisfy_criteria.mjs +20 -0
- package/tests/founding_birth.mjs +200 -0
- package/tests/founding_mode.mjs +3 -1
- package/tests/genesis_home.mjs +71 -2
- package/tests/goal_closure_invariants.mjs +68 -0
- package/tests/hub_map_orb_states.mjs +5 -1
- package/tests/npm_release_release.mjs +37 -2
- package/tests/projects_hub.mjs +5 -3
- package/tests/projects_hub_dns_ready.mjs +153 -0
- package/tests/projects_hub_pre_uat.mjs +9 -5
- package/tests/provision_dns_first.mjs +171 -0
- package/tests/provision_settings_apply.mjs +6 -1
- package/tests/provisioning_settings_env.mjs +49 -0
- package/tests/session_transcript.mjs +273 -0
- package/tests/ui_design_kit.mjs +1 -1
- package/tests/upgrade_commit_identity.mjs +89 -0
- package/tests/wizard_demo.mjs +29 -3
package/modules/sessions/db.js
CHANGED
|
@@ -658,6 +658,62 @@ async function getSessionById(id) {
|
|
|
658
658
|
return rows[0] || null;
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
+
const TRANSCRIPT_PAGE_DEFAULT = 500; // turns per read; the reader pages with offset=
|
|
662
|
+
const TRANSCRIPT_PAGE_MAX = 2000;
|
|
663
|
+
|
|
664
|
+
// upsertSessionTranscript — store/refresh one session's FULL scrubbed transcript
|
|
665
|
+
// (ADR 0051, task 1001004). Keyed on session_id like session_records, and the same
|
|
666
|
+
// ownership guard applies: a row can only be rewritten by the builder who owns it,
|
|
667
|
+
// so guessing another builder's session_id cannot overwrite their transcript. The
|
|
668
|
+
// content arrives already scrubbed (the uploader's box is the scrub point); this
|
|
669
|
+
// layer stores it and never inspects it. `turnsJson` is the ALREADY-serialized
|
|
670
|
+
// array (the route serializes once to measure it; re-stringifying 12 MB here would
|
|
671
|
+
// block the event loop a second time). The row is only ever created FROM the
|
|
672
|
+
// caller's own session_records row, so a transcript cannot exist for a session the
|
|
673
|
+
// builder did not upload a digest for (and a stranger cannot squat a session_id
|
|
674
|
+
// ahead of its owner). Returns { session_id, turn_count, byte_size, truncated } or
|
|
675
|
+
// null when there is no such owned session.
|
|
676
|
+
async function upsertSessionTranscript({ sessionId, builderId, turnsJson, turnCount = 0, truncated = false, byteSize = 0 }) {
|
|
677
|
+
const { rows } = await pool.query(
|
|
678
|
+
`INSERT INTO session_transcripts (session_id, builder_id, turn_count, byte_size, truncated, turns)
|
|
679
|
+
SELECT sr.session_id, sr.builder_id, $3, $4, $5, $6::jsonb
|
|
680
|
+
FROM session_records sr
|
|
681
|
+
WHERE sr.session_id = $1 AND sr.builder_id = $2
|
|
682
|
+
ON CONFLICT (session_id) DO UPDATE SET
|
|
683
|
+
turn_count = EXCLUDED.turn_count,
|
|
684
|
+
byte_size = EXCLUDED.byte_size,
|
|
685
|
+
truncated = EXCLUDED.truncated,
|
|
686
|
+
turns = EXCLUDED.turns,
|
|
687
|
+
uploaded_at = now()
|
|
688
|
+
WHERE session_transcripts.builder_id = EXCLUDED.builder_id
|
|
689
|
+
RETURNING session_id, turn_count, byte_size, truncated`,
|
|
690
|
+
[sessionId, builderId, Math.max(0, Math.trunc(Number(turnCount) || 0)), Math.max(0, Math.trunc(Number(byteSize) || 0)), !!truncated, turnsJson]
|
|
691
|
+
);
|
|
692
|
+
return rows[0] || null;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// getSessionTranscript — one PAGE of the full transcript for one session_records row (by its
|
|
696
|
+
// numeric id, the same id /sessions/:id uses), with the owner so the route can
|
|
697
|
+
// apply the own-vs-Archon rule. null when the session or its transcript is absent.
|
|
698
|
+
async function getSessionTranscript(id, { offset = 0, limit = TRANSCRIPT_PAGE_DEFAULT } = {}) {
|
|
699
|
+
const off = Math.max(0, Math.trunc(Number(offset) || 0));
|
|
700
|
+
const lim = Math.min(TRANSCRIPT_PAGE_MAX, Math.max(1, Math.trunc(Number(limit) || TRANSCRIPT_PAGE_DEFAULT)));
|
|
701
|
+
// The slice happens in Postgres so a page read never ships the whole MB-scale
|
|
702
|
+
// jsonb to Node just to throw most of it away.
|
|
703
|
+
const { rows } = await pool.query(
|
|
704
|
+
`SELECT sr.id, sr.session_id, sr.builder_id, st.turn_count, st.byte_size,
|
|
705
|
+
st.truncated, st.uploaded_at,
|
|
706
|
+
COALESCE((SELECT jsonb_agg(e.v ORDER BY e.n)
|
|
707
|
+
FROM jsonb_array_elements(st.turns) WITH ORDINALITY AS e(v, n)
|
|
708
|
+
WHERE e.n > $2 AND e.n <= $2 + $3), '[]'::jsonb) AS turns
|
|
709
|
+
FROM session_records sr
|
|
710
|
+
JOIN session_transcripts st ON st.session_id = sr.session_id
|
|
711
|
+
WHERE sr.id = $1`,
|
|
712
|
+
[Number(id), off, lim]
|
|
713
|
+
);
|
|
714
|
+
return rows[0] ? { ...rows[0], offset: off, limit: lim } : null;
|
|
715
|
+
}
|
|
716
|
+
|
|
661
717
|
// listSessionsByBuilder — a builder's OWN sessions (self-serve summary list).
|
|
662
718
|
async function listSessionsByBuilder(builderId, limit = 25) {
|
|
663
719
|
const { rows } = await pool.query(
|
|
@@ -865,6 +921,8 @@ async function sessionTypeCounts() {
|
|
|
865
921
|
module.exports = {
|
|
866
922
|
// write side (6D.1)
|
|
867
923
|
upsertSessionRecord,
|
|
924
|
+
upsertSessionTranscript, // ADR 0051 — full scrubbed transcript (separate table)
|
|
925
|
+
getSessionTranscript,
|
|
868
926
|
upsertSessionRecordWithReward, // route entry point — upsert + token reward (task 1005)
|
|
869
927
|
awardDeferredSessionRewardForTask, // ADR 0120 land-side trigger — task.shipped listener calls it
|
|
870
928
|
reconcileUnbookedSessionRewards, // ADR 0120 state-based safety net — books rewards for out-of-band ships
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
// audited (the BFG principal posture until 6C.1).
|
|
17
17
|
// GET /sessions/:id — one session's full detail for deep-dive. Owner
|
|
18
18
|
// self-serve; cross-builder requires Archon + audit.
|
|
19
|
+
// PUT /sessions/:sid/transcript
|
|
20
|
+
// — upload the FULL scrubbed transcript of your OWN
|
|
21
|
+
// session (ADR 0051). Own 16 MB parser.
|
|
22
|
+
// GET /sessions/:id/transcript
|
|
23
|
+
// — read it. Owner, or Archon + audit written first.
|
|
19
24
|
//
|
|
20
25
|
// Cross-builder reads are GETs, which the global audit middleware does NOT
|
|
21
26
|
// instrument — so each writes its own audit_log row (fail-closed: logged BEFORE
|
|
@@ -54,6 +59,16 @@ const seams = api; // resolveOptional('reward' | 'builder-settings') — kernel-
|
|
|
54
59
|
const { validateOrRespond, parseId } = api;
|
|
55
60
|
|
|
56
61
|
const UPLOAD_BODY_LIMIT = '4mb';
|
|
62
|
+
// ADR 0051 — the full transcript is MB-scale. The client's own ceiling is 12 MB of
|
|
63
|
+
// content (scripts/gds/session-transcript-build.js); 16 MB leaves JSON overhead.
|
|
64
|
+
const TRANSCRIPT_BODY_LIMIT = '16mb';
|
|
65
|
+
const MAX_TRANSCRIPT_TURNS = 20000;
|
|
66
|
+
// SR-17 for the transcript (the sibling POST bounds detail[] to 2 MB; this row is
|
|
67
|
+
// deliberately ~6x that because a full transcript IS the product, but it is still
|
|
68
|
+
// bounded IN the handler, not just by the parser): a per-turn ceiling stops one
|
|
69
|
+
// absurd turn, and the serialized total matches the client's own ceiling.
|
|
70
|
+
const MAX_TRANSCRIPT_TURN_BYTES = 256 * 1024;
|
|
71
|
+
const MAX_TRANSCRIPT_TOTAL_BYTES = 12 * 1024 * 1024;
|
|
57
72
|
const MAX_DETAIL_TURNS = 5000; // a hard ceiling; ship.js bounds well under this
|
|
58
73
|
|
|
59
74
|
// Disk-fill defense (SR-17 / task 997). The 4 MB body limit + maxItems=5000 cap
|
|
@@ -189,6 +204,7 @@ module.exports = function buildSessionsRouter() {
|
|
|
189
204
|
registerSeams();
|
|
190
205
|
const router = express.Router();
|
|
191
206
|
const uploadJson = express.json({ limit: UPLOAD_BODY_LIMIT });
|
|
207
|
+
const transcriptJson = express.json({ limit: TRANSCRIPT_BODY_LIMIT });
|
|
192
208
|
|
|
193
209
|
// rank: any-builder — upload your OWN session digest. Owner is forced to the
|
|
194
210
|
// authenticated caller; the body cannot attribute a record to another builder.
|
|
@@ -315,6 +331,59 @@ module.exports = function buildSessionsRouter() {
|
|
|
315
331
|
}
|
|
316
332
|
});
|
|
317
333
|
|
|
334
|
+
// rank: any-builder — upload the FULL scrubbed transcript of your OWN session
|
|
335
|
+
// (ADR 0051, task 1001004). Separate from POST /sessions on purpose: the digest
|
|
336
|
+
// is KB and carries the reward/evaluator path under a 4 MB parser; the transcript
|
|
337
|
+
// is MB and gets its own larger parser, so a transcript failure can never block a
|
|
338
|
+
// digest. The row is created only FROM the caller's own session_records row (no
|
|
339
|
+
// digest → 404, someone else's session_id → 404, never a confirmation it exists).
|
|
340
|
+
// The content is scrubbed on the uploader's box; the server bounds SHAPE, not
|
|
341
|
+
// secrets — every turn must be a plain object and the whole body is capped.
|
|
342
|
+
router.put('/sessions/:sid/transcript', auth.requireBuilder, transcriptJson, async (req, res) => {
|
|
343
|
+
const sid = String(req.params.sid || '');
|
|
344
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/.test(sid)) return res.fail('bad_session_id', 400);
|
|
345
|
+
if (validateOrRespond(req, res, {
|
|
346
|
+
turns: { required: true, type: 'array', maxItems: MAX_TRANSCRIPT_TURNS },
|
|
347
|
+
truncated: { type: 'boolean' },
|
|
348
|
+
})) return;
|
|
349
|
+
const body = req.body || {};
|
|
350
|
+
const turns = body.turns;
|
|
351
|
+
// Serialize each turn ONCE: the same strings are measured, then joined into the
|
|
352
|
+
// jsonb text the db stores, so a 12 MB transcript is not stringified twice.
|
|
353
|
+
const parts = new Array(turns.length);
|
|
354
|
+
let totalBytes = 2;
|
|
355
|
+
for (let idx = 0; idx < turns.length; idx++) {
|
|
356
|
+
const t = turns[idx];
|
|
357
|
+
if (!t || typeof t !== 'object' || Array.isArray(t)) {
|
|
358
|
+
return res.fail('bad_transcript_turn', { status: 400, message: `turns[${idx}] must be an object` });
|
|
359
|
+
}
|
|
360
|
+
parts[idx] = JSON.stringify(t);
|
|
361
|
+
const turnBytes = Buffer.byteLength(parts[idx], 'utf8');
|
|
362
|
+
if (turnBytes > MAX_TRANSCRIPT_TURN_BYTES) {
|
|
363
|
+
return res.fail('transcript_turn_too_large', { status: 413, message: `turns[${idx}] is ${turnBytes} bytes; the per-turn cap is ${MAX_TRANSCRIPT_TURN_BYTES} bytes` });
|
|
364
|
+
}
|
|
365
|
+
totalBytes += turnBytes + 1;
|
|
366
|
+
if (totalBytes > MAX_TRANSCRIPT_TOTAL_BYTES) {
|
|
367
|
+
return res.fail('transcript_too_large', { status: 413, message: `transcript exceeds the ${MAX_TRANSCRIPT_TOTAL_BYTES}-byte total cap` });
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
const stored = await db.upsertSessionTranscript({
|
|
372
|
+
sessionId: sid,
|
|
373
|
+
builderId: req.builder.id, // owner = caller, never the body
|
|
374
|
+
turnsJson: `[${parts.join(',')}]`,
|
|
375
|
+
turnCount: turns.length,
|
|
376
|
+
truncated: body.truncated === true,
|
|
377
|
+
byteSize: totalBytes,
|
|
378
|
+
});
|
|
379
|
+
if (!stored) return res.fail('not_found', 404);
|
|
380
|
+
res.status(201).json({ transcript: stored });
|
|
381
|
+
} catch (err) {
|
|
382
|
+
api.logger('sessions').error(err, 'PUT /sessions/:sid/transcript failed');
|
|
383
|
+
res.fail('upload_failed', 500);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
|
|
318
387
|
// GET /sessions/search + /sessions/mine are declared BEFORE /sessions/:id so
|
|
319
388
|
// Express matches the literal paths first (the numeric :id never swallows them).
|
|
320
389
|
|
|
@@ -458,5 +527,33 @@ module.exports = function buildSessionsRouter() {
|
|
|
458
527
|
}
|
|
459
528
|
});
|
|
460
529
|
|
|
530
|
+
// rank: any-builder — the FULL transcript of one session (ADR 0051). Same rule as
|
|
531
|
+
// GET /sessions/:id: the owner reads their own; a cross-builder read is
|
|
532
|
+
// Archon-only, 404 (never 403) for everyone else, and the fail-closed audit row
|
|
533
|
+
// is written BEFORE any transcript content is served — transcripts are the most
|
|
534
|
+
// sensitive thing this module holds. Paged (?offset=&limit=, default 500 turns,
|
|
535
|
+
// max 2000): a transcript is MB-scale and the response carries turn_count so the
|
|
536
|
+
// reader knows how far to go.
|
|
537
|
+
router.get('/sessions/:id/transcript', auth.requireBuilder, readRateLimit, async (req, res) => {
|
|
538
|
+
const id = parseId(req, res, { code: 'bad_session_id', positiveInt: true });
|
|
539
|
+
if (id === null) return;
|
|
540
|
+
try {
|
|
541
|
+
const t = await db.getSessionTranscript(id, { offset: req.query.offset, limit: req.query.limit });
|
|
542
|
+
if (!t) return res.fail('not_found', 404);
|
|
543
|
+
if (String(t.builder_id) !== String(req.builder.id)) {
|
|
544
|
+
if (req.builder.rank !== 'archon') return res.fail('not_found', 404);
|
|
545
|
+
await recordSessionRead(req, {
|
|
546
|
+
route: '/api/bongos/sessions/:id/transcript',
|
|
547
|
+
targetBuilderId: t.builder_id,
|
|
548
|
+
target: `transcript:${id}`,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
res.json({ transcript: t });
|
|
552
|
+
} catch (err) {
|
|
553
|
+
api.logger('sessions').error(err, 'GET /sessions/:id/transcript failed');
|
|
554
|
+
res.fail('fetch_failed', 500);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
461
558
|
return router;
|
|
462
559
|
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "A demo born into genesis (task 1004506): 3 people, 4 hours each. The plan was settled at birth from the demo (Monarchy / BDFL, one pass, no advisors), so decide is behind and scope is in progress, each stage minutes long. Built from the real server shaper (genesis-home.js demoPlan + shapeGenesis).",
|
|
3
|
+
"genesis": {
|
|
4
|
+
"stage": "scope",
|
|
5
|
+
"stage_number": 1,
|
|
6
|
+
"stage_count": 5,
|
|
7
|
+
"day": 1,
|
|
8
|
+
"deciding": false,
|
|
9
|
+
"plan": {
|
|
10
|
+
"days": {
|
|
11
|
+
"scope": 1,
|
|
12
|
+
"strategize": 1,
|
|
13
|
+
"charter": 1,
|
|
14
|
+
"legalize": 1,
|
|
15
|
+
"legislation": 1
|
|
16
|
+
},
|
|
17
|
+
"passes": 1,
|
|
18
|
+
"advisors": "none",
|
|
19
|
+
"government": "monarchy",
|
|
20
|
+
"expected_builders": 3,
|
|
21
|
+
"existing": false,
|
|
22
|
+
"demo": {
|
|
23
|
+
"people": 3,
|
|
24
|
+
"hours_each": 4
|
|
25
|
+
},
|
|
26
|
+
"minutes": {
|
|
27
|
+
"scope": 14,
|
|
28
|
+
"strategize": 10,
|
|
29
|
+
"charter": 10,
|
|
30
|
+
"legalize": 5,
|
|
31
|
+
"legislation": 10
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"demo": {
|
|
35
|
+
"people": 3,
|
|
36
|
+
"hours_each": 4
|
|
37
|
+
},
|
|
38
|
+
"stages": [
|
|
39
|
+
{
|
|
40
|
+
"key": "scope",
|
|
41
|
+
"number": 1,
|
|
42
|
+
"label": "Scope",
|
|
43
|
+
"life": "primordial sea",
|
|
44
|
+
"plate": "r5-life-1-primordial-sea",
|
|
45
|
+
"done_when": "The scope is written: what you are making, for whom, and what is left out.",
|
|
46
|
+
"wakes": [
|
|
47
|
+
"roadmap",
|
|
48
|
+
"goals",
|
|
49
|
+
"goal-map"
|
|
50
|
+
],
|
|
51
|
+
"days": 1,
|
|
52
|
+
"minutes": 14,
|
|
53
|
+
"due": "2026-10-01T09:14:00.000Z",
|
|
54
|
+
"state": "now",
|
|
55
|
+
"tasks": [
|
|
56
|
+
{
|
|
57
|
+
"id": "101",
|
|
58
|
+
"title": "Write the project scope: what we are making, for whom, and what is out of scope",
|
|
59
|
+
"status": "active",
|
|
60
|
+
"finished": false
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"finished": 0,
|
|
64
|
+
"total": 1,
|
|
65
|
+
"board": null
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"key": "strategize",
|
|
69
|
+
"number": 2,
|
|
70
|
+
"label": "Strategize",
|
|
71
|
+
"life": "first cells",
|
|
72
|
+
"plate": "r5-life-2-first-cells",
|
|
73
|
+
"done_when": "The strategy is written out, or the one you have is linked.",
|
|
74
|
+
"wakes": [
|
|
75
|
+
"work"
|
|
76
|
+
],
|
|
77
|
+
"days": 1,
|
|
78
|
+
"minutes": 10,
|
|
79
|
+
"due": null,
|
|
80
|
+
"state": "later",
|
|
81
|
+
"tasks": [
|
|
82
|
+
{
|
|
83
|
+
"id": "102",
|
|
84
|
+
"title": "Write out the strategy, or link the one you already have",
|
|
85
|
+
"status": "ready",
|
|
86
|
+
"finished": false
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"finished": 0,
|
|
90
|
+
"total": 1,
|
|
91
|
+
"board": null
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"key": "charter",
|
|
95
|
+
"number": 3,
|
|
96
|
+
"label": "Charter",
|
|
97
|
+
"life": "the genome",
|
|
98
|
+
"plate": "r5-life-3-genome",
|
|
99
|
+
"done_when": "The starting government is chosen and the bylaws are written.",
|
|
100
|
+
"wakes": [
|
|
101
|
+
"board-room"
|
|
102
|
+
],
|
|
103
|
+
"days": 1,
|
|
104
|
+
"minutes": 10,
|
|
105
|
+
"due": null,
|
|
106
|
+
"state": "later",
|
|
107
|
+
"tasks": [
|
|
108
|
+
{
|
|
109
|
+
"id": "103",
|
|
110
|
+
"title": "Choose the starting government and write the bylaws",
|
|
111
|
+
"status": "ready",
|
|
112
|
+
"finished": false
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"finished": 0,
|
|
116
|
+
"total": 1,
|
|
117
|
+
"board": null
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"key": "legalize",
|
|
121
|
+
"number": 4,
|
|
122
|
+
"label": "Legalize",
|
|
123
|
+
"life": "the membrane",
|
|
124
|
+
"plate": "r5-life-4-membrane",
|
|
125
|
+
"done_when": "Rewards are decided, and who can be paid.",
|
|
126
|
+
"wakes": [
|
|
127
|
+
"economy"
|
|
128
|
+
],
|
|
129
|
+
"days": 1,
|
|
130
|
+
"minutes": 5,
|
|
131
|
+
"due": null,
|
|
132
|
+
"state": "later",
|
|
133
|
+
"tasks": [
|
|
134
|
+
{
|
|
135
|
+
"id": "104",
|
|
136
|
+
"title": "Decide how contributors are rewarded and who can be paid",
|
|
137
|
+
"status": "ready",
|
|
138
|
+
"finished": false
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"finished": 0,
|
|
142
|
+
"total": 1,
|
|
143
|
+
"board": null
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"key": "legislation",
|
|
147
|
+
"number": 5,
|
|
148
|
+
"label": "Legislation",
|
|
149
|
+
"life": "the nervous system",
|
|
150
|
+
"plate": "r5-life-5-nervous-system",
|
|
151
|
+
"done_when": "The voting rules are set: who votes, and how many must agree.",
|
|
152
|
+
"wakes": [
|
|
153
|
+
"ranks"
|
|
154
|
+
],
|
|
155
|
+
"days": 1,
|
|
156
|
+
"minutes": 10,
|
|
157
|
+
"due": null,
|
|
158
|
+
"state": "later",
|
|
159
|
+
"tasks": [
|
|
160
|
+
{
|
|
161
|
+
"id": "105",
|
|
162
|
+
"title": "Set the voting rules: who votes and how many must agree",
|
|
163
|
+
"status": "ready",
|
|
164
|
+
"finished": false
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"finished": 0,
|
|
168
|
+
"total": 1,
|
|
169
|
+
"board": null
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"can_close": false,
|
|
173
|
+
"can_come_alive": false
|
|
174
|
+
},
|
|
175
|
+
"viewer_id": "1"
|
|
176
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"builder": {
|
|
3
|
+
"id": "1",
|
|
4
|
+
"github_login": "builder",
|
|
5
|
+
"display_name": "A Builder",
|
|
6
|
+
"avatar_url": "/__kit/avatar.svg",
|
|
7
|
+
"rank": "archon"
|
|
8
|
+
},
|
|
9
|
+
"founding_mode": {
|
|
10
|
+
"active": true,
|
|
11
|
+
"stage": "scope",
|
|
12
|
+
"stage_number": 1,
|
|
13
|
+
"stage_count": 5,
|
|
14
|
+
"day": 1,
|
|
15
|
+
"asleep": {
|
|
16
|
+
"roadmap": "scope",
|
|
17
|
+
"goals": "scope",
|
|
18
|
+
"goal-map": "scope",
|
|
19
|
+
"work": "strategize",
|
|
20
|
+
"board-room": "charter",
|
|
21
|
+
"economy": "legalize",
|
|
22
|
+
"ranks": "legislation",
|
|
23
|
+
"collab": "alive",
|
|
24
|
+
"roster": "alive"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"last_verified_up": true,
|
|
17
17
|
"error_note": null,
|
|
18
18
|
"dns_note": null,
|
|
19
|
+
"dns_resolves": true,
|
|
19
20
|
"torn_down_at": null,
|
|
20
21
|
"open_intent": null,
|
|
21
22
|
"demo": {
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
"last_verified_up": true,
|
|
90
91
|
"error_note": null,
|
|
91
92
|
"dns_note": null,
|
|
93
|
+
"dns_resolves": true,
|
|
92
94
|
"torn_down_at": "2026-08-15T12:00:00.000Z",
|
|
93
95
|
"open_intent": null
|
|
94
96
|
},
|
|
@@ -107,6 +109,7 @@
|
|
|
107
109
|
"last_verified_up": false,
|
|
108
110
|
"error_note": null,
|
|
109
111
|
"dns_note": null,
|
|
112
|
+
"dns_resolves": true,
|
|
110
113
|
"torn_down_at": null,
|
|
111
114
|
"open_intent": null
|
|
112
115
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "Two projects whose address the platform's own lookup has not seen answer yet (task 1004505): beacon is active a minute after its standup, lumen is still standing up. The hub must link neither and name neither until dns_resolves is true. Only projects-dns-pending.states.json points at this.",
|
|
3
|
+
"instances": [
|
|
4
|
+
{
|
|
5
|
+
"id": 106,
|
|
6
|
+
"slug": "beacon",
|
|
7
|
+
"status": "active",
|
|
8
|
+
"domain": "beacon.cloudbongos.com",
|
|
9
|
+
"target_ref": "builder/beacon",
|
|
10
|
+
"hosting_shape": "cloud-host",
|
|
11
|
+
"owner_monthly_usd": null,
|
|
12
|
+
"onboard_mode": "scaffold",
|
|
13
|
+
"created_at": "$now-5m",
|
|
14
|
+
"provisioned_at": "$now-1m",
|
|
15
|
+
"updated_at": "$now-1m",
|
|
16
|
+
"last_verified_at": null,
|
|
17
|
+
"last_verified_up": null,
|
|
18
|
+
"error_note": null,
|
|
19
|
+
"dns_note": null,
|
|
20
|
+
"dns_resolves": false,
|
|
21
|
+
"torn_down_at": null,
|
|
22
|
+
"open_intent": null
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": 107,
|
|
26
|
+
"slug": "lumen",
|
|
27
|
+
"status": "provisioning",
|
|
28
|
+
"domain": "lumen.cloudbongos.com",
|
|
29
|
+
"target_ref": "builder/lumen",
|
|
30
|
+
"hosting_shape": "cloud-host",
|
|
31
|
+
"owner_monthly_usd": null,
|
|
32
|
+
"onboard_mode": "scaffold",
|
|
33
|
+
"created_at": "$now-3m",
|
|
34
|
+
"provisioned_at": null,
|
|
35
|
+
"updated_at": "$now-2m",
|
|
36
|
+
"last_verified_at": null,
|
|
37
|
+
"last_verified_up": null,
|
|
38
|
+
"error_note": null,
|
|
39
|
+
"dns_note": null,
|
|
40
|
+
"dns_resolves": false,
|
|
41
|
+
"torn_down_at": null,
|
|
42
|
+
"open_intent": {
|
|
43
|
+
"action": "provision",
|
|
44
|
+
"state": "running",
|
|
45
|
+
"created_at": "$now-3m"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"last_verified_up": true,
|
|
17
17
|
"error_note": null,
|
|
18
18
|
"dns_note": null,
|
|
19
|
+
"dns_resolves": true,
|
|
19
20
|
"torn_down_at": null,
|
|
20
21
|
"open_intent": null
|
|
21
22
|
},
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
"last_verified_up": true,
|
|
76
77
|
"error_note": null,
|
|
77
78
|
"dns_note": null,
|
|
79
|
+
"dns_resolves": true,
|
|
78
80
|
"torn_down_at": "2026-08-15T12:00:00.000Z",
|
|
79
81
|
"open_intent": null
|
|
80
82
|
},
|
|
@@ -93,6 +95,7 @@
|
|
|
93
95
|
"last_verified_up": false,
|
|
94
96
|
"error_note": null,
|
|
95
97
|
"dns_note": null,
|
|
98
|
+
"dns_resolves": true,
|
|
96
99
|
"torn_down_at": null,
|
|
97
100
|
"open_intent": null
|
|
98
101
|
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.21.1",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -8653,5 +8653,46 @@
|
|
|
8653
8653
|
"id": "1004509",
|
|
8654
8654
|
"text": "The Autobongos runner panel on the Gate page is now much lighter to read. While anything is missing it shows a short checklist saying what to do next; once everything is ready it shrinks to one status line with an on/off switc"
|
|
8655
8655
|
}
|
|
8656
|
+
],
|
|
8657
|
+
"1.20.83": [
|
|
8658
|
+
{
|
|
8659
|
+
"id": "1004513",
|
|
8660
|
+
"text": "A goal now finishes by itself when its last task ships. Before, if someone ticked off all of a goal's done-when criteria while one task was still being worked on, the goal stayed open forever once that task landed — it took ti"
|
|
8661
|
+
}
|
|
8662
|
+
],
|
|
8663
|
+
"1.21.0": [],
|
|
8664
|
+
"1.21.1": [
|
|
8665
|
+
{
|
|
8666
|
+
"id": "1004522",
|
|
8667
|
+
"text": "The Governor City design now lives in the project itself, not only in a private design canvas. This is the design where a governor's home is a small animated city and each part of the government is a building. All six design f"
|
|
8668
|
+
},
|
|
8669
|
+
{
|
|
8670
|
+
"id": "1004518",
|
|
8671
|
+
"text": "If GitHub refuses a Release for lack of permission, the deploy page now names the exact thing to fix: the server's access token or its GitHub App, whichever it uses."
|
|
8672
|
+
},
|
|
8673
|
+
{
|
|
8674
|
+
"id": "1004506",
|
|
8675
|
+
"text": "New projects now open their hall already in genesis. When a project is created through the startup flow, the platform marks it, and on the founder's first visit the hall switches into founding mode, once and safely. Projects m"
|
|
8676
|
+
},
|
|
8677
|
+
{
|
|
8678
|
+
"id": "1004521",
|
|
8679
|
+
"text": "Releases Bongos 1.21.0, the first bigger-step update, so we can watch cloudbongos.com take it on its own."
|
|
8680
|
+
},
|
|
8681
|
+
{
|
|
8682
|
+
"id": "1004515",
|
|
8683
|
+
"text": "The new way to start a project is now agreed. It is one short flow of six screens with no progress bar, plain words, and a calm night-time look. Before anything is created, you confirm every setting we filled in for you."
|
|
8684
|
+
},
|
|
8685
|
+
{
|
|
8686
|
+
"id": "1001004",
|
|
8687
|
+
"text": "Every session's complete conversation, with secrets removed, is now saved on the server so the review team can read what was actually asked and answered. Only the owner or an administrator can read it, and each administrator r"
|
|
8688
|
+
},
|
|
8689
|
+
{
|
|
8690
|
+
"id": "1004514",
|
|
8691
|
+
"text": "When a project is updated from the deploy page, the update now always saves its new version, even on projects whose history has no author name set. Before, those projects quietly kept the update unsaved, so the next update tri"
|
|
8692
|
+
},
|
|
8693
|
+
{
|
|
8694
|
+
"id": "1004505",
|
|
8695
|
+
"text": "A new project's address now works the first time you open it. The platform creates the address before anything else, and the hub only links it once the platform has seen it answer. Before, an early click could leave your home"
|
|
8696
|
+
}
|
|
8656
8697
|
]
|
|
8657
8698
|
}
|