@bongos/core 1.19.319 → 1.19.320

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.bongos-core.json +121 -36
  2. package/.claude/skills/merge-mode/SKILL.md +3 -1
  3. package/.devcontainer/init-firewall.sh +41 -5
  4. package/clients/bongos-client/examples/hello-world.mjs +11 -2
  5. package/docs/copy-inventory.md +20 -20
  6. package/docs/copy-registry.json +22 -22
  7. package/modules/hall-ui/public/builders.js +0 -1
  8. package/package-lock.json +2 -2
  9. package/package.json +1 -1
  10. package/scripts/gds/audit-replay.sh +20 -3
  11. package/scripts/gds/box.js +79 -86
  12. package/scripts/gds/db-backup-nightly.sh +17 -4
  13. package/scripts/gds/db-backup-offsite.sh +23 -6
  14. package/scripts/gds/discipline-modes.json +1 -1
  15. package/scripts/gds/doc-cli-guard.js +34 -2
  16. package/scripts/gds/fitness.js +8 -4
  17. package/scripts/gds/gds-db-backup.service +15 -4
  18. package/scripts/gds/gds-db-backup.timer +12 -1
  19. package/scripts/gds/gds-db-offsite.service +16 -5
  20. package/scripts/gds/gds-db-offsite.timer +12 -1
  21. package/scripts/gds/instance-apex.js +28 -0
  22. package/scripts/gds/newcomer-floor.templates.json +2 -2
  23. package/scripts/gds/provision.js +7 -3
  24. package/scripts/gds/ship-deploy-target.js +388 -0
  25. package/scripts/gds/ship-finish.js +377 -0
  26. package/scripts/gds/ship-flow.js +814 -0
  27. package/scripts/gds/ship-git.js +100 -0
  28. package/scripts/gds/ship-grade-steps.js +376 -0
  29. package/scripts/gds/ship-grade.js +319 -0
  30. package/scripts/gds/ship-io.js +100 -0
  31. package/scripts/gds/ship-land.js +539 -0
  32. package/scripts/gds/ship-memory-sync.js +192 -0
  33. package/scripts/gds/ship-merge.js +297 -0
  34. package/scripts/gds/ship-preflight-steps.js +323 -0
  35. package/scripts/gds/ship-regen.js +286 -0
  36. package/scripts/gds/ship-resume.js +151 -0
  37. package/scripts/gds/ship-session-upload.js +216 -0
  38. package/scripts/gds/ship-state.js +73 -0
  39. package/scripts/gds/ship-surprise.js +84 -0
  40. package/scripts/gds/ship.js +105 -4195
  41. package/scripts/status-probe.mjs +30 -2
  42. package/src/module-api.js +3 -1
  43. package/tests/helpers.mjs +13 -0
  44. package/tests/provision.mjs +7 -1
  45. package/tests/regrade_eligibility.mjs +43 -0
  46. package/tests/ship_assurance.mjs +4 -2
  47. package/tests/ship_publish_error.mjs +43 -0
@@ -15,7 +15,7 @@
15
15
  //
16
16
  // WHAT IT CHECKS, deliberately narrow: for every `node scripts/gds/<x>.js` in a
17
17
  // tracked .md, (a) the script exists, and (b) each `--flag` written next to it
18
- // appears in that script's source. Nothing else — this is not a prose linter.
18
+ // appears in that script's source (or in a same-directory module it requires — a CLI may be carved into phase files). Nothing else — this is not a prose linter.
19
19
  //
20
20
  // STATIC ONLY, NEVER EXECUTED. The reason matters: running every documented CLI
21
21
  // would be slow, would need a session, and a command that blocks on a prompt
@@ -203,6 +203,38 @@ const readFileOrNull = (rel) => {
203
203
 
204
204
  const NAME = 'a doc that names a CLI matches the real CLI (G01)';
205
205
 
206
+ // readCliSource — a CLI's source PLUS the source of the same-directory modules
207
+ // it requires, concatenated.
208
+ //
209
+ // A big CLI is allowed to be more than one file: task 1003205 carved
210
+ // scripts/gds/ship.js into ship-*.js phase modules, and the flag parsing for
211
+ // e.g. `--no-surprise` moved with the phase that reads it. Scanning ship.js
212
+ // alone then reports a documented, genuinely-honoured flag as a violation —
213
+ // a false alarm that pushes the next agent toward silencing the rule via
214
+ // DOC_CLI_ALLOW, which is exactly the outcome the rule exists to prevent.
215
+ //
216
+ // One level of same-directory relative requires is enough (it is where a carve
217
+ // puts things) and keeps the read bounded. A missing sibling is skipped: the
218
+ // point is to find the flag, not to prove the require graph.
219
+ function readCliSource(rel) {
220
+ const src = readFileOrNull(rel);
221
+ if (src === null) return null;
222
+ const dir = path.dirname(rel);
223
+ const seen = new Set([rel]);
224
+ const parts = [src];
225
+ const RE = /require\(\s*['"](\.\/[^'"]+)['"]\s*\)/g;
226
+ let m;
227
+ while ((m = RE.exec(src))) {
228
+ let sib = path.join(dir, m[1]);
229
+ if (!sib.endsWith('.js')) sib += '.js';
230
+ if (seen.has(sib)) continue;
231
+ seen.add(sib);
232
+ const sibSrc = readFileOrNull(sib);
233
+ if (sibSrc !== null) parts.push(sibSrc);
234
+ }
235
+ return parts.join('\n');
236
+ }
237
+
206
238
  function checkDocNamesRealCli({ files = null } = {}) {
207
239
  const docs = files || trackedMarkdown();
208
240
  // A lint that reports on nothing is worse than one that fails (the
@@ -217,7 +249,7 @@ function checkDocNamesRealCli({ files = null } = {}) {
217
249
  }
218
250
 
219
251
  const { violations, warnings, invocations } = scanDocs(docs, {
220
- readDoc: readFileOrNull, readScript: readFileOrNull,
252
+ readDoc: readFileOrNull, readScript: readCliSource,
221
253
  });
222
254
 
223
255
  return {
@@ -1023,9 +1023,14 @@ const IDENTITY_SCAN_ALLOWED = {
1023
1023
  'scripts/gds/fitness.js': 2,
1024
1024
  'scripts/gds/package-core.js': 1,
1025
1025
  'scripts/gds/seed-v3-tasks.js': 14,
1026
- 'scripts/gds/audit-replay.sh': 1,
1027
- 'scripts/gds/db-backup-nightly.sh': 1,
1028
- 'scripts/gds/db-backup-offsite.sh': 2,
1026
+ // audit-replay.sh + db-backup-{nightly,offsite}.sh were carved out here as
1027
+ // "host-pinned ops"; task 1002757 RETIRED that justification for them — each
1028
+ // now resolves its database from the branding pack (the migrate.sh pattern) and
1029
+ // fails loudly rather than defaulting, so their counts ratcheted to zero and the
1030
+ // entries are gone. seed-staging.sh keeps its carve-out on the OPPOSITE
1031
+ // rationale: its literal is a fail-CLOSED safety interlock (the script wipes
1032
+ // STAGING_DB and refuses unless the name matches exactly), so on any other
1033
+ // instance it declines to run instead of destroying the wrong database.
1029
1034
  'scripts/gds/seed-staging.sh': 4,
1030
1035
  'scripts/gds/backfill-sessions.js': 2,
1031
1036
  'scripts/gds/box.js': 3,
@@ -1649,7 +1654,6 @@ function checkWriteRoutesValidated() {
1649
1654
  };
1650
1655
  }
1651
1656
 
1652
-
1653
1657
  // ── Check 19 — the ADR 0174 vocabulary wall (goal 1000068 R07, task 1003008) ──
1654
1658
  //
1655
1659
  // "Role" means ONE thing in this codebase: a craft — Artist, Ideator, or Builder.
@@ -1,13 +1,24 @@
1
+ # TEMPLATE — not installable as-is (task 1002757). The __TOKENS__ below MUST be
2
+ # substituted at install time; a provisioned instance gets a correct per-instance
3
+ # unit pair generated by scripts/gds/provision.js (task 2213) and never needs this
4
+ # file. Substitute:
5
+ # __SERVICE_USER__ the unix user that owns the instance + its Postgres role
6
+ # __INSTANCE_ROOT__ the instance checkout root (docs live under it)
7
+ # __CORE_SCRIPTS_DIR__ the deployed core's scripts/gds dir:
8
+ # standalone: <instance-root>/node_modules/@bongos/core/scripts/gds
9
+ # full clone: <instance-root>/scripts/gds
10
+ # __CONFIG_DIR__ the instance config dir name (offsite unit only)
11
+ # One-liner: sed -e 's|__SERVICE_USER__|<user>|g' -e … gds-db-backup.service | sudo tee /etc/systemd/system/…
1
12
  [Unit]
2
13
  Description=Nightly pg_dump of GDS Postgres DB (filed by #412)
3
- Documentation=file:///home/lars/amazonprimea/docs/recipes/gds-db-backup.md
14
+ Documentation=file://__INSTANCE_ROOT__/docs/recipes/gds-db-backup.md
4
15
  After=postgresql.service
5
16
  Requires=postgresql.service
6
17
 
7
18
  [Service]
8
19
  Type=oneshot
9
- User=lars
10
- Group=lars
20
+ User=__SERVICE_USER__
21
+ Group=__SERVICE_USER__
11
22
  # Run the canonical script from the deployed repo. Keep this path stable;
12
23
  # the recipe doc points at it for restore + manual-one-shot use cases.
13
24
  #
@@ -25,7 +36,7 @@ Group=lars
25
36
  # vendored/standalone: /home/<user>/<instance>/node_modules/@bongos/core/scripts/gds/db-backup-nightly.sh
26
37
  # full clone: /home/<user>/<repo>/scripts/gds/db-backup-nightly.sh
27
38
  # See docs/recipes/gds-db-backup.md.
28
- ExecStart=/usr/bin/env bash /home/lars/amazonprimea/scripts/gds/db-backup-nightly.sh
39
+ ExecStart=/usr/bin/env bash __CORE_SCRIPTS_DIR__/db-backup-nightly.sh
29
40
  # Modest hardening — the script only needs read on the socket + write on
30
41
  # /var/backups/gds. Anything else is gravy for forensics.
31
42
  NoNewPrivileges=true
@@ -1,6 +1,17 @@
1
+ # TEMPLATE — not installable as-is (task 1002757). The __TOKENS__ below MUST be
2
+ # substituted at install time; a provisioned instance gets a correct per-instance
3
+ # unit pair generated by scripts/gds/provision.js (task 2213) and never needs this
4
+ # file. Substitute:
5
+ # __SERVICE_USER__ the unix user that owns the instance + its Postgres role
6
+ # __INSTANCE_ROOT__ the instance checkout root (docs live under it)
7
+ # __CORE_SCRIPTS_DIR__ the deployed core's scripts/gds dir:
8
+ # standalone: <instance-root>/node_modules/@bongos/core/scripts/gds
9
+ # full clone: <instance-root>/scripts/gds
10
+ # __CONFIG_DIR__ the instance config dir name (offsite unit only)
11
+ # One-liner: sed -e 's|__SERVICE_USER__|<user>|g' -e … gds-db-backup.service | sudo tee /etc/systemd/system/…
1
12
  [Unit]
2
13
  Description=Daily timer for GDS DB pg_dump (filed by #412)
3
- Documentation=file:///home/lars/amazonprimea/docs/recipes/gds-db-backup.md
14
+ Documentation=file://__INSTANCE_ROOT__/docs/recipes/gds-db-backup.md
4
15
 
5
16
  [Timer]
6
17
  # Run every day at 04:00 UTC. That's a low-traffic window for a
@@ -1,13 +1,24 @@
1
+ # TEMPLATE — not installable as-is (task 1002757). The __TOKENS__ below MUST be
2
+ # substituted at install time; a provisioned instance gets a correct per-instance
3
+ # unit pair generated by scripts/gds/provision.js (task 2213) and never needs this
4
+ # file. Substitute:
5
+ # __SERVICE_USER__ the unix user that owns the instance + its Postgres role
6
+ # __INSTANCE_ROOT__ the instance checkout root (docs live under it)
7
+ # __CORE_SCRIPTS_DIR__ the deployed core's scripts/gds dir:
8
+ # standalone: <instance-root>/node_modules/@bongos/core/scripts/gds
9
+ # full clone: <instance-root>/scripts/gds
10
+ # __CONFIG_DIR__ the instance config dir name (offsite unit only)
11
+ # One-liner: sed -e 's|__SERVICE_USER__|<user>|g' -e … gds-db-backup.service | sudo tee /etc/systemd/system/…
1
12
  [Unit]
2
13
  Description=Weekly offsite archive of GDS Postgres dumps to DigitalOcean Spaces (filed by #415)
3
- Documentation=file:///home/lars/amazonprimea/docs/recipes/gds-db-backup.md
14
+ Documentation=file://__INSTANCE_ROOT__/docs/recipes/gds-db-backup.md
4
15
  After=network-online.target
5
16
  Wants=network-online.target
6
17
 
7
18
  [Service]
8
19
  Type=oneshot
9
- User=lars
10
- Group=lars
20
+ User=__SERVICE_USER__
21
+ Group=__SERVICE_USER__
11
22
  # Run the canonical script from the deployed repo. Keep this path stable;
12
23
  # the recipe doc points at it for restore + manual-one-shot use cases.
13
24
  #
@@ -18,12 +29,12 @@ Group=lars
18
29
  # this unit would have hit it on its first run.
19
30
  #
20
31
  # EDIT THIS PATH when installing (layout differs — vendored vs full clone).
21
- ExecStart=/usr/bin/env bash /home/lars/amazonprimea/scripts/gds/db-backup-offsite.sh
32
+ ExecStart=/usr/bin/env bash __CORE_SCRIPTS_DIR__/db-backup-offsite.sh
22
33
  # S3 credentials for the Space live outside the repo (ADR 0022). systemd
23
34
  # injects them; the file is chmod 600, owned by lars. Missing-file is
24
35
  # tolerated here (the script fails loudly if the vars are unset) so a
25
36
  # fresh install doesn't fail to load the unit before creds are dropped in.
26
- EnvironmentFile=-/etc/amazonprimea/spaces.env
37
+ EnvironmentFile=-/etc/__CONFIG_DIR__/spaces.env
27
38
  # Hardening — the script reads dumps from /var/backups/gds and talks to the
28
39
  # network. It needs no write access to the local filesystem.
29
40
  NoNewPrivileges=true
@@ -1,6 +1,17 @@
1
+ # TEMPLATE — not installable as-is (task 1002757). The __TOKENS__ below MUST be
2
+ # substituted at install time; a provisioned instance gets a correct per-instance
3
+ # unit pair generated by scripts/gds/provision.js (task 2213) and never needs this
4
+ # file. Substitute:
5
+ # __SERVICE_USER__ the unix user that owns the instance + its Postgres role
6
+ # __INSTANCE_ROOT__ the instance checkout root (docs live under it)
7
+ # __CORE_SCRIPTS_DIR__ the deployed core's scripts/gds dir:
8
+ # standalone: <instance-root>/node_modules/@bongos/core/scripts/gds
9
+ # full clone: <instance-root>/scripts/gds
10
+ # __CONFIG_DIR__ the instance config dir name (offsite unit only)
11
+ # One-liner: sed -e 's|__SERVICE_USER__|<user>|g' -e … gds-db-backup.service | sudo tee /etc/systemd/system/…
1
12
  [Unit]
2
13
  Description=Weekly timer for GDS DB offsite archive to Spaces (filed by #415)
3
- Documentation=file:///home/lars/amazonprimea/docs/recipes/gds-db-backup.md
14
+ Documentation=file://__INSTANCE_ROOT__/docs/recipes/gds-db-backup.md
4
15
 
5
16
  [Timer]
6
17
  # Run weekly on Sunday at 05:00 UTC. That's one hour after the daily local
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+ // instance-apex.js — the instance's own registrable apex + the addresses derived
3
+ // from it, read from its branding pack (task 1002757).
4
+ //
5
+ // WHY THIS EXISTS. Operator-plane defaults used to carry the founder's apex as a
6
+ // literal (box DNS zones, the Cloudflare zone, the provisioner's git identity),
7
+ // so another operator's fleet pointed at the founder's DNS and its commits were
8
+ // attributed to the hub. These resolvers return NULL rather than guessing when the
9
+ // pack names no public origin: every caller treats null as "not configured" and
10
+ // refuses, which fails loudly instead of minting an unreachable host.
11
+ function instanceApex() {
12
+ try {
13
+ const host = new URL(require('../../src/branding').branding().domains.publicOrigin).hostname;
14
+ return host && !/^localhost$/i.test(host) ? host : null;
15
+ } catch { return null; }
16
+ }
17
+
18
+ // The git identity a provisioner commits scaffold/pin changes as: explicit env →
19
+ // bot@<instance apex> → a neutral, deliberately-undeliverable platform address
20
+ // (never a live inbox by accident).
21
+ function provisionerBotEmail(env = process.env) {
22
+ const explicit = String(env.PROVISION_BOT_EMAIL || '').trim();
23
+ if (explicit) return explicit;
24
+ const apex = instanceApex();
25
+ return apex ? `bot@${apex}` : 'bot@cloud-bongos.invalid';
26
+ }
27
+
28
+ module.exports = { instanceApex, provisionerBotEmail };
@@ -49,7 +49,7 @@
49
49
  "est_minutes": 55,
50
50
  "credits_reward": 15,
51
51
  "title": "Generate one new in-world art asset of your choice (a prop, plant, animal, or accessory) via the pixel-art pipeline",
52
- "description": "## What you're doing\n\nAmazonprimea is a young ancient-Greek coastal world, and it always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the engineer or ideator newcomer chore instead, or ask an Archon to enable it.)\n5. **Confirm it PASSED** the rubric (`modules/art-pipeline/iterations/run_summary.json` shows `status: \"pass\"`). If it only reached best-effort, regenerate with the fix_hint — never paper over a fail.\n6. **See it in-game**: `node scripts/gds/art-stage.js` rebuilds the atlas and surfaces it on your sandbox preview (`sandbox-<login>.amazonprimea.com`).\n7. Ship via `/builder-ship`; the ship notes name your asset and its rubric score.\n\n## Stay clear of (READ-ONLY)\n\nNever modify the locked palette (`modules/art-pipeline/template/palette.json`) or the rubric (`modules/art-pipeline/template/rubric.json`) — those are theme-defining and changing them needs an ADR (out of scope for a first ship). Touch only art assets/vocabulary under `art/` (and, only if your asset is a genuinely new base-tile TYPE, the `public/game/world/tilePalette.js` mapping). Do not touch any permission/pipeline-core/migration/infra file.",
52
+ "description": "## What you're doing\n\nThis instance's world always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the engineer or ideator newcomer chore instead, or ask an Archon to enable it.)\n5. **Confirm it PASSED** the rubric (`modules/art-pipeline/iterations/run_summary.json` shows `status: \"pass\"`). If it only reached best-effort, regenerate with the fix_hint — never paper over a fail.\n6. **See it in-game**: `node scripts/gds/art-stage.js` rebuilds the atlas and surfaces it on your sandbox preview (`sandbox-<login>.<instance-apex>`).\n7. Ship via `/builder-ship`; the ship notes name your asset and its rubric score.\n\n## Stay clear of (READ-ONLY)\n\nNever modify the locked palette (`modules/art-pipeline/template/palette.json`) or the rubric (`modules/art-pipeline/template/rubric.json`) — those are theme-defining and changing them needs an ADR (out of scope for a first ship). Touch only art assets/vocabulary under `art/` (and, only if your asset is a genuinely new base-tile TYPE, the `public/game/world/tilePalette.js` mapping). Do not touch any permission/pipeline-core/migration/infra file.",
53
53
  "done_when": [
54
54
  "Exactly one NEW in-world asset (your chosen subject) was generated via the pixel-art pipeline (`/otb-tile-generate` / orchestrator.py); the final PNG exists at modules/art-pipeline/generated/tiles/<id>.png.",
55
55
  "It PASSED the rubric (modules/art-pipeline/iterations/run_summary.json status 'pass', or /otb-design-review numbers reported at/above the bar) and every pixel is on the locked palette (modules/art-pipeline/template/palette.json).",
@@ -70,7 +70,7 @@
70
70
  "est_minutes": 45,
71
71
  "credits_reward": 12,
72
72
  "title": "Idea-seed pass: file 5-8 fresh in-world ideas (names, lore, or builder-UX) into the idea inbox",
73
- "description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship as an ideator is simple and concrete: **add a small batch of fresh ideas to that inbox.** You write no code, touch no game, change no system — you think, write a few short well-shaped ideas, and file them with one command each.\n\nIt is identical for every newcomer and **always valuable**: the inbox is never \"done\" — every triage pass consumes ideas (promote/discard/merge), so it is permanently hungry for fresh input, and a world the size of Amazonprimea always wants more names, more lore, more texture. Your batch is brand-new content, so it can never duplicate \"the work\" — the supply IS the work.\n\n## Pick ONE branch\n\n- **Branch A — PRODUCT flavour (default).** Ideas that grow the world players see: **names + one line of lore** for a character, structure, landmark, district, ship, or festival; or a world-background snippet (a custom, a superstition, a line a named NPC says). Tone: mythic, mysterious, a \"discovered secret\"; Mediterranean ancient-Greek coast (just south of Athens); NOT modern, NOT tongue-in-cheek-modern. Ground it in CLAUDE.md §3/§6/§7.\n- **Branch B — INTERNAL / builder-UX.** Small user-experience improvement ideas for the GDS / builders' hall (the surfaces builders use, not the game): a clearer label, a confusing onboarding message, a quality-of-life tweak to a status page or a slash-command's output. See `docs/TRACKS.md` for product vs internal.\n\n## How to find your way in\n\n1. (Branch A) Read CLAUDE.md §3/§6/§7 for tone. (Branch B) Skim `docs/TRACKS.md` and click around https://amazonprimea.com/builders for the rough edge you'll write up.\n2. See what's already filed so you don't repeat it: `node scripts/gds/api.js GET /api/gds/inbox`.\n3. File each idea with `node scripts/gds/capture.js \"<title>\" --body-file <path>` (filing an idea is the open-to-all path — a Xenos/Thetes session is allowed to do it; you just can't triage them). Put one line of detail + a `track: product` or `track: internal` line in each body file.",
73
+ "description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship as an ideator is simple and concrete: **add a small batch of fresh ideas to that inbox.** You write no code, touch no game, change no system — you think, write a few short well-shaped ideas, and file them with one command each.\n\nIt is identical for every newcomer and **always valuable**: the inbox is never \"done\" — every triage pass consumes ideas (promote/discard/merge), so it is permanently hungry for fresh input, and a growing world always wants more names, more lore, more texture. Your batch is brand-new content, so it can never duplicate \"the work\" — the supply IS the work.\n\n## Pick ONE branch\n\n- **Branch A — PRODUCT flavour (default).** Ideas that grow the world players see: **names + one line of lore** for a character, structure, landmark, district, ship, or festival; or a world-background snippet (a custom, a superstition, a line a named NPC says). Tone: mythic, mysterious, a \"discovered secret\"; Mediterranean ancient-Greek coast (just south of Athens); NOT modern, NOT tongue-in-cheek-modern. Ground it in CLAUDE.md §3/§6/§7.\n- **Branch B — INTERNAL / builder-UX.** Small user-experience improvement ideas for the GDS / builders' hall (the surfaces builders use, not the game): a clearer label, a confusing onboarding message, a quality-of-life tweak to a status page or a slash-command's output. See `docs/TRACKS.md` for product vs internal.\n\n## How to find your way in\n\n1. (Branch A) Read CLAUDE.md §3/§6/§7 for tone. (Branch B) Skim `docs/TRACKS.md` and click around your instance's /builders hall for the rough edge you'll write up.\n2. See what's already filed so you don't repeat it: `node scripts/gds/api.js GET /api/gds/inbox`.\n3. File each idea with `node scripts/gds/capture.js \"<title>\" --body-file <path>` (filing an idea is the open-to-all path — a Xenos/Thetes session is allowed to do it; you just can't triage them). Put one line of detail + a `track: product` or `track: internal` line in each body file.",
74
74
  "done_when": [
75
75
  "You filed 5–8 fresh ideas into the idea_inbox via `node scripts/gds/capture.js` (each prints a `Captured idea #N` confirmation), all in ONE branch (A product, or B internal/UX).",
76
76
  "Each idea has a clear title and a body with at least one sentence of detail — for Branch A, one line of in-world lore (who/what it is); for Branch B, what the rough edge is and what 'better' looks like.",
@@ -169,6 +169,10 @@ const CONFIG = {
169
169
  sshOpts: process.env.PROVISION_SSH_OPTS || '-o BatchMode=yes -o StrictHostKeyChecking=accept-new',
170
170
  };
171
171
 
172
+ // task 1002757 — the provisioner's git identity resolves from the instance's own
173
+ // apex (env → bot@<apex> → neutral), never the hub's address hardcoded.
174
+ const { provisionerBotEmail } = require('./instance-apex');
175
+
172
176
  const MAX_INTENT_ATTEMPTS = numEnv('PROVISION_INTENT_MAX_ATTEMPTS', 3);
173
177
 
174
178
  // Manifest-placement verification budget (task 1002720 / QA-walk F23): a child
@@ -709,7 +713,7 @@ async function scaffoldStandaloneRepo(inst, deps) {
709
713
  writeFile(giPath, INSTANCE_GITIGNORE);
710
714
  }
711
715
  exec(`git -C ${root} config user.name "Cloud Bongos"`);
712
- exec(`git -C ${root} config user.email "bot@cloudbongos.com"`);
716
+ exec(`git -C ${root} config user.email "${provisionerBotEmail()}"`);
713
717
  exec(`git -C ${root} add -A`);
714
718
  const commitMsg = mode === 'adopt'
715
719
  ? `chore: adopt Cloud Bongos onto ${inst.slug} on a pinned @bongos/core ${coreV} (provision.js, ADR 0121)`
@@ -939,7 +943,7 @@ function buildCorePinRefreshCommit({ root, fromVersion, toVersion, repo, ownerTo
939
943
  return {
940
944
  config: [
941
945
  `git -C ${root} config user.name "Cloud Bongos"`,
942
- `git -C ${root} config user.email "bot@cloudbongos.com"`,
946
+ `git -C ${root} config user.email "${provisionerBotEmail()}"`,
943
947
  ],
944
948
  add: `git -C ${root} add -A`,
945
949
  commit: `git -C ${root} commit -m "${msg}"`,
@@ -2477,7 +2481,7 @@ async function main() {
2477
2481
  if (require.main === module) main();
2478
2482
 
2479
2483
  module.exports = {
2480
- CONFIG, MAX_INTENT_ATTEMPTS,
2484
+ CONFIG, MAX_INTENT_ATTEMPTS, provisionerBotEmail,
2481
2485
  // pure step generators (tested directly)
2482
2486
  dbName, dbCreateCmd, migrateCmd, seedFirstVersionCmd, serviceUnit, serviceUnitPath, webEnvBody, webEnvPath,
2483
2487
  backupUnitName, backupServicePath, backupTimerPath, backupScriptPath, backupService, backupTimer,