@bongos/core 1.17.1 → 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 +244 -264
- 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/GOVERNANCE.md +1 -1
- package/README.md +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/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 +9 -6
- package/docs/architecture.md +2 -2
- 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 +18 -17
- package/docs/handoff-template.md +2 -2
- package/docs/modules-contract.md +6 -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/ops-gotchas.md +1 -1
- package/docs/recipes/private-npm-distribution.md +17 -0
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/upgrading-the-core.md +55 -0
- package/docs/research/cursor-competitive-analysis.md +1 -1
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
- package/modules/discord/module.json +2 -1
- package/modules/economy/achievements.js +9 -1
- package/modules/economy/credits.js +10 -1
- package/modules/hall-ui/public/builders.js +71 -4
- package/modules/hall-ui/public/goal-inbox.js +191 -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 +1 -0
- package/modules/hall-ui/public/style.css +43 -0
- 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/secret-scrub.js +10 -0
- package/package-lock.json +2 -2
- package/package.json +7 -2
- package/scripts/discord/README.md +1 -1
- package/scripts/gds/claim.js +98 -48
- package/scripts/gds/claude-materialize.js +30 -9
- package/scripts/gds/cutover-codemod.js +241 -0
- package/scripts/gds/fitness.js +54 -0
- package/scripts/gds/init.js +59 -3
- package/scripts/gds/lint-infra-exec.js +70 -0
- package/scripts/gds/lint-workflow-exec.js +63 -0
- package/scripts/gds/mirror-lineage-check.js +14 -5
- package/scripts/gds/mirror-redact.js +8 -0
- package/scripts/gds/onboard.js +4 -0
- package/scripts/gds/package-core.js +18 -3
- 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/update-channel.js +186 -0
- package/scripts/gds/upgrade.js +116 -9
- package/src/bongos/auth.js +14 -1
- package/src/bongos/serve-internal.js +10 -1
- package/src/branding.js +1 -0
- package/src/module-api.js +12 -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 +20 -0
- package/tests/consumer_layout_boot.mjs +302 -0
- package/tests/cutover_codemod.mjs +184 -0
- package/tests/device_flow_start.mjs +25 -2
- package/tests/hook_core_resolve.mjs +125 -0
- package/tests/infra_exec_paths.mjs +104 -0
- package/tests/init.mjs +47 -0
- package/tests/mirror_lineage_proof.mjs +14 -0
- package/tests/module_loader.mjs +42 -0
- package/tests/module_manifest.mjs +19 -0
- package/tests/onboard.mjs +13 -0
- package/tests/package_core.mjs +8 -0
- package/tests/provisioning_github_app_routes.mjs +15 -0
- 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/update_channel.mjs +186 -0
- package/tests/upgrade.mjs +131 -0
- 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
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// tests/workflow_exec_paths.mjs — task 2136 (ADR 0108 configurable-root).
|
|
2
|
+
//
|
|
3
|
+
// Proves every .github/workflows/*.yml invocation of a CORE script resolves in a consumer layout
|
|
4
|
+
// after the R89 split: each is routed through the both-layouts resolver (infra/core-node.sh for
|
|
5
|
+
// node targets, infra/core-path.sh for bash targets) AND names a PUBLISHABLE core file that ships
|
|
6
|
+
// in @bongos/core (→ present at node_modules/@bongos/core post-install) and exists at the core root
|
|
7
|
+
// today. No workflow still invokes a core script the bare repo-relative way. Also proves
|
|
8
|
+
// core-path.sh resolves both layouts (the bash-target sibling of core-node.sh).
|
|
9
|
+
//
|
|
10
|
+
// DB-free, no network. Run: node tests/workflow_exec_paths.mjs
|
|
11
|
+
|
|
12
|
+
import { strict as assert } from 'node:assert';
|
|
13
|
+
import { execFileSync } from 'node:child_process';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import os from 'node:os';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
const require = createRequire(import.meta.url);
|
|
21
|
+
const { lintWorkflowExec } = require('../scripts/gds/lint-workflow-exec.js');
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const REPO = path.resolve(__dirname, '..');
|
|
24
|
+
const CORE_PATH_SH = path.join(REPO, 'infra', 'core-path.sh');
|
|
25
|
+
|
|
26
|
+
let passed = 0, failed = 0;
|
|
27
|
+
function test(name, fn) {
|
|
28
|
+
try { fn(); console.log(` ok ${name}`); passed++; }
|
|
29
|
+
catch (err) { console.error(` FAIL ${name}`); console.error(' ', err.message); failed++; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---- A. the workflow lint is green on the real tree ---------------------------------------------
|
|
33
|
+
test('lint-workflow-exec: every core invocation is routed + publishable (no bare repo-relative form)', () => {
|
|
34
|
+
const r = lintWorkflowExec({ instanceRoot: REPO, coreRoot: REPO });
|
|
35
|
+
assert.deepEqual(r.violations, [], `expected no violations, got: ${JSON.stringify(r.violations, null, 2)}`);
|
|
36
|
+
assert.ok(r.ok, 'lint should pass');
|
|
37
|
+
// The security-critical merge gates + the functional gates must all be covered.
|
|
38
|
+
const rels = r.checked.map((c) => `${c.file}:${c.rel}`);
|
|
39
|
+
assert.ok(rels.some((s) => s.startsWith('gate-review.yml:') && s.endsWith('scripts/gds/gate-review.js')), 'gate-review routed');
|
|
40
|
+
assert.ok(rels.some((s) => s.startsWith('grade-gate.yml:') && s.endsWith('scripts/gds/ci-grade.js')), 'grade-gate routed');
|
|
41
|
+
assert.ok(rels.some((s) => s.endsWith('scripts/migrate.sh')), 'selfhost migrate routed via core-path.sh');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('lint-workflow-exec: a bare core invocation in a workflow is flagged (counterfactual)', () => {
|
|
45
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'wf-lint-'));
|
|
46
|
+
try {
|
|
47
|
+
fs.mkdirSync(path.join(tmp, '.github', 'workflows'), { recursive: true });
|
|
48
|
+
fs.mkdirSync(path.join(tmp, 'scripts', 'gds'), { recursive: true });
|
|
49
|
+
fs.writeFileSync(path.join(tmp, 'scripts', 'gds', 'gate-review.js'), '// probe\n');
|
|
50
|
+
fs.writeFileSync(path.join(tmp, '.github', 'workflows', 'legacy.yml'),
|
|
51
|
+
'jobs:\n x:\n steps:\n - run: node scripts/gds/gate-review.js\n');
|
|
52
|
+
const r = lintWorkflowExec({ instanceRoot: tmp, coreRoot: tmp });
|
|
53
|
+
assert.ok(!r.ok, 'a bare core invocation must fail the lint');
|
|
54
|
+
assert.equal(r.violations.length, 1);
|
|
55
|
+
assert.match(r.violations[0].why, /bare core invocation/);
|
|
56
|
+
} finally { fs.rmSync(tmp, { recursive: true, force: true }); }
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ---- B. core-path.sh resolves BOTH layouts ------------------------------------------------------
|
|
60
|
+
function runCorePath(rel, instanceRoot) {
|
|
61
|
+
return execFileSync('bash', [CORE_PATH_SH, rel], {
|
|
62
|
+
encoding: 'utf8', env: { ...process.env, BONGOS_INSTANCE_ROOT: instanceRoot },
|
|
63
|
+
}).trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test('core-path.sh prints the monolith path (script at the instance root)', () => {
|
|
67
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cp-mono-'));
|
|
68
|
+
try {
|
|
69
|
+
fs.mkdirSync(path.join(tmp, 'scripts'), { recursive: true });
|
|
70
|
+
fs.writeFileSync(path.join(tmp, 'scripts', 'migrate.sh'), '# probe\n');
|
|
71
|
+
assert.equal(runCorePath('scripts/migrate.sh', tmp), path.join(tmp, 'scripts', 'migrate.sh'));
|
|
72
|
+
} finally { fs.rmSync(tmp, { recursive: true, force: true }); }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('core-path.sh falls back to the consumer path (script only under node_modules/@bongos/core)', () => {
|
|
76
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cp-cons-'));
|
|
77
|
+
try {
|
|
78
|
+
const pkg = path.join(tmp, 'node_modules', '@bongos', 'core', 'scripts');
|
|
79
|
+
fs.mkdirSync(pkg, { recursive: true });
|
|
80
|
+
fs.writeFileSync(path.join(pkg, 'migrate.sh'), '# probe\n');
|
|
81
|
+
assert.equal(runCorePath('scripts/migrate.sh', tmp), path.join(pkg, 'migrate.sh'));
|
|
82
|
+
} finally { fs.rmSync(tmp, { recursive: true, force: true }); }
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('core-path.sh exits non-zero (no stdout) when the script is in NO layout', () => {
|
|
86
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cp-none-'));
|
|
87
|
+
let threw = false;
|
|
88
|
+
try {
|
|
89
|
+
execFileSync('bash', [CORE_PATH_SH, 'scripts/nope.sh'], {
|
|
90
|
+
encoding: 'utf8', stdio: 'pipe', env: { ...process.env, BONGOS_INSTANCE_ROOT: tmp },
|
|
91
|
+
});
|
|
92
|
+
} catch (_) { threw = true; }
|
|
93
|
+
finally { fs.rmSync(tmp, { recursive: true, force: true }); }
|
|
94
|
+
assert.ok(threw, 'expected a non-zero exit when the script resolves in no layout');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(`\nworkflow_exec_paths: ${passed} passed, ${failed} failed`);
|
|
98
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# Container building — exterior tile spec (pixel-precise, V2)
|
|
2
|
-
|
|
3
|
-
- **Task:** [#643](https://example.com/builders#/task/643) (V2 · product). Promoted from idea_inbox [#162](https://example.com/builders#/idea/162) (builder 25 art/design brief, 2026-06-04).
|
|
4
|
-
- **Date:** 2026-06-18
|
|
5
|
-
- **Builds on:** [`container-exterior.md`](container-exterior.md) (the structure-level design, task [#641](https://example.com/builders#/task/641)) — this is the **tile-level** layer it calls out. Colour is governed by the locked decisions below.
|
|
6
|
-
- **Status:** Spec ready. **Generation waits for [#640](https://example.com/builders#/task/640) / [ADR 0044](../adr/0044-mediterranean-palette-replacement.md)** (every tile here re-quantizes against the replacement palette — per `container-exterior.md` §Pipeline). Spec work proceeds now.
|
|
7
|
-
|
|
8
|
-
## ⚠️ Colour: the original brief is superseded — weathered charcoal, not clean red
|
|
9
|
-
|
|
10
|
-
idea [#162](https://example.com/builders#/idea/162)'s brief (this task's source) asked for **"saturated industrial red (or black) — clean, no weathering, no rust."** That was written **before** Lars resolved the question. The authoritative decision (later wins):
|
|
11
|
-
|
|
12
|
-
- **Blocker [#7](https://example.com/builders#/blocker/7) (Lars, 2026-06-10, now a locked `style_guide.md` rule):** the container body is **weathered BLACK/CHARCOAL corrugated metal with rust streaks** — *not* red, *not* teal. Faded **Hermes-teal is an ACCENT only** (the door panel + a faint caduceus etch). It must read as an *ancient, heavy, discovered relic*, not factory-new cargo.
|
|
13
|
-
- **`style_guide.md` (Lars, 2026-06-14):** **no white / light edge rim** on container (or other large-object) tiles — strip near-white edge pixels in post ("the white on the container actually sucks").
|
|
14
|
-
- **`style_guide.md`:** non-ground objects carry a **1-px darker outline** against their interior (FireRed rock/tree convention); ground tiles do not.
|
|
15
|
-
|
|
16
|
-
So every tile below is **weathered charcoal corrugated metal with rust streaks**; the brief's "clean red, no weathering, single-pixel white highlight" wording does **not** apply. The brief's *geometry* (corrugation, glass panels, deck, roof, staircase) is still the source — only the colour/finish is corrected.
|
|
17
|
-
|
|
18
|
-
## Per-tile pixel specs (32×32 base)
|
|
19
|
-
|
|
20
|
-
### Container wall tile
|
|
21
|
-
- **Base:** weathered charcoal/gunmetal (a matte near-black with a faint blue-grey undertone), drawn from the replacement palette's charcoal/grey value steps (ADR 0044 guarantees enough steps).
|
|
22
|
-
- **Corrugation:** 4–5 vertical ridges per tile. Each ridge: 1px lighter charcoal on the left (highlight), 1px darker on the right (shadow); flat panels between are the base.
|
|
23
|
-
- **Weathering:** sparse vertical **rust streaks** (a desaturated orange-brown from the palette) bleeding downward from ridge tops + seams — irregular, 1–2px wide, not on every ridge. Reads as age, not damage.
|
|
24
|
-
- **Edges:** 1px darker-charcoal **outline** on the object silhouette (per the style rule). Top edge 2px darker (top face in oblique light); bottom edge 1px near-black ground shadow. **No white/light rim** anywhere on the silhouette.
|
|
25
|
-
- **Stacked-unit seam:** a 2px dark recessed line with a 1px shadow below, where the ground-floor container meets the second storey.
|
|
26
|
-
|
|
27
|
-
### Glass storefront / window panel (32×32, or 64×32 wide)
|
|
28
|
-
- **Fill:** semi-transparent blue-grey (≈40–50% over what's behind). Through dark glass, a faint **warm amber interior glow** (the "dark exterior, warm interior" mood from `container-exterior.md`).
|
|
29
|
-
- **Frame:** 3px matte-charcoal border (the frames dissolve into the dark facade rather than reading as separate elements).
|
|
30
|
-
- **Interior hint:** extremely faint 1–2-shade-darker silhouettes (a plant, shelving) — not detailed.
|
|
31
|
-
- **Reflection highlight — FLAG for Lars:** the brief wanted a white upper-left reflection strip; the no-white-rim rule targets the *object silhouette*, not interior glass. Proposed: a restrained **pale-blue** (not white) 1px upper-left reflection inside the frame. If Lars wants zero light pixels here, drop it. *(Open pixel-detail decision.)*
|
|
32
|
-
|
|
33
|
-
### Door tile (the one Hermes-teal accent)
|
|
34
|
-
- Charcoal frame; the door panel is **faded Hermes-teal** (the single body-color exception), with a **faint caduceus etch** (1–2px lighter teal lines) where space allows, and a warm interior glow at the threshold. This is the only teal on the building.
|
|
35
|
-
|
|
36
|
-
### Deck tile (wood apron, walkable)
|
|
37
|
-
- Horizontal planks: light warm tan base, 4–5 planks/tile, 1px dark gap between; very subtle 1–2px grain lines; 2px darker far-edge shadow. Drawn as an alpha-margined object overlay over sand (the `marble_path` technique, [ADR 0013](../adr/<redacted>.md)) so it works on any ground.
|
|
38
|
-
|
|
39
|
-
### Roof tile (top-down)
|
|
40
|
-
- Flat charcoal surface, 1 shade darker than the wall face (less direct light); small 3×3 dark **container corner castings** at each corner; a faint 2px panel seam every 8px; 1px railing line on edge tiles (per `container-exterior.md` rooftop band).
|
|
41
|
-
|
|
42
|
-
### Exterior staircase (object overlay)
|
|
43
|
-
- Dark charcoal steel: two parallel rails with treads between (top-down ladder look); 1px railing line each outer edge, 1px lighter than the tread. Arrives at the rooftop stair-landing tile.
|
|
44
|
-
|
|
45
|
-
## Mood target
|
|
46
|
-
A **designed object, not a found one** — presence from its *shape and glass*, not from bright paint. The charcoal recedes into the coastal scene; at dusk the warm interior light through dark glass is the most dramatic visual in the game. The contradiction (an *ancient* shipping container) lives in the weathering + the teal-and-caduceus door.
|
|
47
|
-
|
|
48
|
-
## Pipeline mapping (generate after [#640](https://example.com/builders#/task/640))
|
|
49
|
-
Per [ADR 0008](../adr/<redacted>.md) (families, not per-tile): `container_building_facade` (wall · glass · door · light-fixture · staircase), `container_building_roof` (field · railing · landing), `deck_wood` (16-autotile alpha-tile line family). Furniture singletons (table, chairs) per-tile. **Nothing generates until the palette swap ([#640](https://example.com/builders#/task/640)/ADR 0044) lands** — else every tile re-quantizes against the old palette and is redone.
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Container building — exterior structure design (V2)
|
|
2
|
-
|
|
3
|
-
- **Task:** [#641](https://example.com/builders#/task/641) (V2 · product · creative). Promoted from idea_inbox [#160](https://example.com/builders#/idea/160) (builder 25 art/design brief, 2026-06-04).
|
|
4
|
-
- **Date:** 2026-06-11
|
|
5
|
-
- **Status:** Design locked at the structure level. Pixel-precise tile specs are [#643](https://example.com/builders#/task/643) (facade); interior is [#642](https://example.com/builders#/task/642)/[#644](https://example.com/builders#/task/644)/[#645](https://example.com/builders#/task/645); palette implementation is [#640](https://example.com/builders#/task/640) / [ADR 0044](../adr/0044-mediterranean-palette-replacement.md).
|
|
6
|
-
|
|
7
|
-
## What changes
|
|
8
|
-
|
|
9
|
-
The container is no longer a single beached 4×2 object (the current `CONTAINER_LAYOUT` in
|
|
10
|
-
`src/world/terrain.js`). It becomes a **two-storey trading house built from stacked
|
|
11
|
-
shipping containers** — a designed building with a glass storefront, a wooden deck,
|
|
12
|
-
an exterior staircase, and a flat rooftop. It reads as a *destination*, not salvage.
|
|
13
|
-
|
|
14
|
-
The narrative survives intact: Hermes placed the containers; the Examplens stacked
|
|
15
|
-
them into a trading house. The mythic contradiction (an *ancient* shipping container)
|
|
16
|
-
stays — see the colour note below.
|
|
17
|
-
|
|
18
|
-
## Colour: weathered black/charcoal (decided)
|
|
19
|
-
|
|
20
|
-
The original brief asked for "bold, uniform red or black — not rusted or weathered."
|
|
21
|
-
That option was resolved **after** the brief was written: blocker [#7](https://example.com/builders#/blocker/7) (Lars, 2026-06-10,
|
|
22
|
-
recorded as a style-guide rule) fixed the container as **weathered black/charcoal
|
|
23
|
-
corrugated metal with rust streaks**, with faded Hermes-teal as an *accent only* (the
|
|
24
|
-
door + the caduceus etch). The later decision wins. So the building is charcoal and
|
|
25
|
-
weathered — it is "designed architecture" by its *shape and glass*, not by looking
|
|
26
|
-
factory-new. ADR 0044 already constrains the replacement palette to carry enough
|
|
27
|
-
charcoal/grey value steps for this.
|
|
28
|
-
|
|
29
|
-
## Top-down structure (32×32 tiles, FireRed building anatomy)
|
|
30
|
-
|
|
31
|
-
FireRed conveys buildings top-down as **facade rows (what you'd see standing in front)
|
|
32
|
-
topped by roof rows**. A second storey = a taller facade. The reference is the Celadon
|
|
33
|
-
department store: tall glass-fronted facade, flat roof.
|
|
34
|
-
|
|
35
|
-
The building is an **8-wide × 6-tall** visual block (up from 4×2):
|
|
36
|
-
|
|
37
|
-
| Rows (top→bottom) | Band | Content |
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| 1–2 | **Rooftop** | Flat container roof; 1-px railing along the edge tiles; a stair landing tile at the right edge where the exterior staircase arrives. Future rooftop deck props (parasol, crates) are object overlays, not part of this family. |
|
|
40
|
-
| 3–4 | **Upper-storey facade** | Charcoal corrugated container walls with a continuous band of **large glass panels** (semi-transparent blue-grey, single white highlight stripe). Container seams visible at the stacked-unit joints. |
|
|
41
|
-
| 5–6 | **Ground-storey facade** | Glass storefront panels flanking a centred **door** (the one Hermes-teal accent, faint caduceus etch, warm interior glow). Wall-mounted light fixtures as small details on the wall tiles either side — lit at dusk/night. |
|
|
42
|
-
|
|
43
|
-
Attached, outside the 8×6 block:
|
|
44
|
-
|
|
45
|
-
- **Exterior staircase** — a 1-tile-wide steel stair strip climbing the building's right
|
|
46
|
-
side from ground to the rooftop landing. Purely visual in V2 (blocking); the rooftop
|
|
47
|
-
becomes walkable in a later version, the art shouldn't need to change for that.
|
|
48
|
-
- **Wooden deck** — a 2-row walkable apron in front of the ground facade, drawn as an
|
|
49
|
-
alpha-margined object overlay over sand (the marble-path technique, [ADR 0013](../adr/<redacted>.md)),
|
|
50
|
-
so it works on any ground. Outdoor furniture (table, two chairs) are blocking
|
|
51
|
-
object singletons placed on the deck, off the door's walking line.
|
|
52
|
-
|
|
53
|
-
### Placement and collision
|
|
54
|
-
|
|
55
|
-
Anchor at **(11, 4)** in the current 30×18 world: the building spans cols 11–18,
|
|
56
|
-
rows 4–9 (grass behind, sand in front), the deck spans rows 10–11, and the existing
|
|
57
|
-
marble path (col 13, rows 10–13) shifts to meet the door at col 14–15. All building
|
|
58
|
-
tiles **block**, including the door — interaction stays "stand adjacent and face it,"
|
|
59
|
-
exactly like the current container door. Deck tiles are walkable; furniture blocks.
|
|
60
|
-
|
|
61
|
-
## Pipeline mapping (what gets generated, later)
|
|
62
|
-
|
|
63
|
-
Per [ADR 0008](../adr/<redacted>.md), coherent groups are families, not per-tile calls:
|
|
64
|
-
|
|
65
|
-
1. **`container_building_facade`** family — wall, glass-panel, door, light-fixture, and
|
|
66
|
-
staircase tiles (replaces the 2×4 `container` family in `art/template/families.json`).
|
|
67
|
-
2. **`container_building_roof`** family — roof field, railing edges/corners, stair landing.
|
|
68
|
-
3. **`deck_wood`** — 16-autotile alpha-tile line family (same recipe as `marble_path`).
|
|
69
|
-
4. **Furniture singletons** (table, chair) — per-tile pipeline.
|
|
70
|
-
|
|
71
|
-
**Sequencing rule: generate nothing until [#640](https://example.com/builders#/task/640) / ADR 0044 lands.** Every one of these
|
|
72
|
-
tiles would be quantized against the old palette and re-done after the Mediterranean
|
|
73
|
-
swap. Spec work ([#643](https://example.com/builders#/task/643)) can proceed now; generation waits.
|
|
74
|
-
|
|
75
|
-
## Engineering follow-up (not this task)
|
|
76
|
-
|
|
77
|
-
The world model needs a matching change in **both** `map.js`/`terrain.js` twins
|
|
78
|
-
(server + client): new OBJECT ids, an 8×6 building layout replacing
|
|
79
|
-
`CONTAINER_PANELS_ROWMAJOR`/`CONTAINER_LAYOUT`, deck overlay placement, and blocking
|
|
80
|
-
rules as above. That is an engineering task to be filed when the facade family's tile
|
|
81
|
-
ids are final (after [#643](https://example.com/builders#/task/643)).
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# Container building — interior atmosphere design (V2)
|
|
2
|
-
|
|
3
|
-
- **Task:** [#645](https://example.com/builders#/task/645) (V2 · product · creative). Promoted from idea_inbox [#164](https://example.com/builders#/idea/164) (builder 25 art/design brief, 2026-06-04).
|
|
4
|
-
- **Date:** 2026-06-21
|
|
5
|
-
- **Builds on:** [`container-exterior.md`](container-exterior.md) — this is the **interior** counterpart of that structure-level brief. The granular interior layers it calls out are the **layout** ([#642](https://example.com/builders#/task/642)) and the **floor-tile pixel spec** ([#644](https://example.com/builders#/task/644)); palette is governed by [ADR 0044](../adr/0044-mediterranean-palette-replacement.md).
|
|
6
|
-
- **Status:** Atmosphere direction locked. Colour reconciled to ADR 0044 (below). Tile generation waits for [#640](https://example.com/builders#/task/640) / ADR 0044, same sequencing rule as the exterior.
|
|
7
|
-
|
|
8
|
-
## ⚠️ Colour: the source brief is superseded — Mediterranean palette, not Poke-Mart blue
|
|
9
|
-
|
|
10
|
-
idea_inbox [#164](https://example.com/builders#/idea/164)'s brief (this task's source, 2026-06-04) built the interior on a **saturated Pokémon "Poke Mart" blue wave-scale floor** and **orange-red container walls** as the nostalgia anchor. That was written **before** the palette was locked. The authoritative decisions win (exactly as the exterior briefs defer):
|
|
11
|
-
|
|
12
|
-
- **[ADR 0044](../adr/0044-mediterranean-palette-replacement.md)** (2026-06-10): the world has **one Mediterranean palette**, and it states explicitly — *"there is no 'Poke Mart interior' context that needs the temperate palette."* So the interior is **not** a palette exception; it shares the world's hues.
|
|
13
|
-
- **Blocker [#7](https://example.com/builders#/blocker/7)** (2026-06-10, locked `style_guide.md` rule): the container body is **weathered black/charcoal corrugated metal**, not red/orange. The interior-facing walls are the same shell.
|
|
14
|
-
- **Lars (2026-06-21, this session):** Mediterranean governs the interior. The layered *geometry* survives untouched — only the hues are corrected.
|
|
15
|
-
|
|
16
|
-
Concretely (geometry preserved, hue corrected — the exact move [`container-exterior-tiles.md`](container-exterior-tiles.md) made for the facade):
|
|
17
|
-
|
|
18
|
-
| Source brief's colour | Corrected (Mediterranean) |
|
|
19
|
-
|---|---|
|
|
20
|
-
| Saturated Poke-Mart **blue** wave floor | **Aqua / turquoise** wave-scale, from the palette's turquoise-sea steps — the *pattern* is the nostalgia anchor; the *hue* matches the sea just outside the glass |
|
|
21
|
-
| **Orange-red** container walls | **Weathered charcoal** corrugated (blocker [#7](https://example.com/builders#/blocker/7)) — warmth comes from the *lighting*, not the paint |
|
|
22
|
-
| Lush **forest-green** featured rug | Deep **olive / sage** rug (the palette's darkest greens) — still "rich and set apart," in-palette |
|
|
23
|
-
| Pure-**white** shelving | **Cream / pale-stone** (warm-neutral steps); no white edge rim, 1-px darker object outline (style rules) |
|
|
24
|
-
|
|
25
|
-
> **[#642](https://example.com/builders#/task/642) / [#644](https://example.com/builders#/task/644) note:** both still say "blue" floor; [#644](https://example.com/builders#/task/644)'s title already hedges to "aqua-tinted." Read their "blue" as this brief's Mediterranean turquoise/aqua — this doc is the colour authority for the interior, the way `container-exterior.md` is for the facade.
|
|
26
|
-
|
|
27
|
-
## The unified interior — a layered space
|
|
28
|
-
|
|
29
|
-
Three layers on three surfaces, none fighting because each owns a different plane:
|
|
30
|
-
|
|
31
|
-
1. **Shell (walls):** raw weathered-charcoal corrugated container — the industrial bones are never hidden. Double-height: tall wall columns, a second-floor balcony railing along the top of the map, pendant lamps on cables from a high ceiling.
|
|
32
|
-
2. **Floor (the centerpiece):** aqua/turquoise wave-scale tile — the nostalgia geometry underfoot, in Mediterranean hue.
|
|
33
|
-
3. **Retail layer (objects + light):** boutique principles — items displayed like sculpture with as much negative space as product; warm directional track lighting makes the illuminated patches the brightest things in the room.
|
|
34
|
-
|
|
35
|
-
You walk into a space that *feels* like a game (the floor pattern), *looks* like a container (the charcoal shell), and *shops* like a boutique (the sculptural, warm-lit display). That triple recognition in the first two seconds is the emotional design goal — unchanged by the palette correction, because it lives in **pattern, material, and light**, not in saturation.
|
|
36
|
-
|
|
37
|
-
## Architecture extracted from the references (geometry — all survives)
|
|
38
|
-
|
|
39
|
-
The source brief studied four interior reference images (idea_inbox [#164](https://example.com/builders#/idea/164)); the *geometry* below is the durable takeaway, independent of hue.
|
|
40
|
-
|
|
41
|
-
- **Double-height shell** (Interior1/2): two storeys of open vertical space → taller wall columns, a balcony-railing band at the top of the map, pendant lamps on cables from a high ceiling.
|
|
42
|
-
- **Symmetrical industrial staircases** (Interior1): black-metal flights on the left/right walls — key navigation + vertical drama. Visual/blocking in V2; walkable upper floor is a later version.
|
|
43
|
-
- **Warm pooled light** (Interior2): tall warm lamps and track lights pool on the floor — 2–3 shade steps radiating from each source. This is where *all* the warmth now comes from, since the walls are charcoal.
|
|
44
|
-
- **Sculptural display** (interior3): freestanding display tables (gallery plinths, not Poke-Mart shelves), few items, generous spacing. Negative space ≈ product — the single biggest luxury signal.
|
|
45
|
-
- **Featured "room within a room"** (interiorShelving1): an architecturally framed VIP zone with a deep olive/sage rug and warm ochre/gold fixtures — the inner sanctum / featured-product zone.
|
|
46
|
-
- **Rear glass panel** (Interior1): full-width north-wall glass showing the coast beyond — ties the interior back to the Mediterranean exterior (and reuses the facade's glass language).
|
|
47
|
-
|
|
48
|
-
## Key tile assets (priority order)
|
|
49
|
-
|
|
50
|
-
1. **Aqua wave-scale floor tile** — the foundational tile; pixel spec is [#644](https://example.com/builders#/task/644).
|
|
51
|
-
2. **Interior container wall tile** — weathered charcoal corrugated (interior-facing variant of the exterior wall family).
|
|
52
|
-
3. **Cream shelving unit** — wall-mounted, pale-stone, items displayed; no white rim, 1-px outline.
|
|
53
|
-
4. **Track-light tile** — small dark ceiling/beam fixture + a warm cone splashing onto the surface below.
|
|
54
|
-
5. **Display-table object overlay** — freestanding wood top, few items, spaced.
|
|
55
|
-
6. **Deep olive/sage featured rug** — the premium-zone floor (alpha-tile overlay over the wave floor).
|
|
56
|
-
7. **Industrial pendant lamp** — hung from above, warm light source.
|
|
57
|
-
8. **Balcony railing** — dark metal, top of the map (second-storey read).
|
|
58
|
-
9. **Polished-concrete floor** — grey, smooth, for back-of-house / secondary areas.
|
|
59
|
-
10. **Rear glass panel** — full-width north wall, coast visible through it.
|
|
60
|
-
|
|
61
|
-
## Pipeline mapping (generate after [#640](https://example.com/builders#/task/640) / ADR 0044)
|
|
62
|
-
|
|
63
|
-
Per [ADR 0008](../adr/<redacted>.md) (families, not per-tile calls):
|
|
64
|
-
|
|
65
|
-
1. **`interior_container_shell`** family — wall · balcony railing · staircase · pendant lamp.
|
|
66
|
-
2. **`interior_floor`** family — aqua wave-scale + polished-concrete, with the olive/sage rug as an alpha-tile overlay (the `marble_path` technique, [ADR 0013](../adr/<redacted>.md)).
|
|
67
|
-
3. **Display singletons** — table, shelving, track light, glass panel — per-tile.
|
|
68
|
-
|
|
69
|
-
**Nothing generates until the palette swap ([#640](https://example.com/builders#/task/640) / ADR 0044) lands** — every tile would re-quantize against the old palette and be redone, the same sequencing rule the exterior brief states.
|
|
70
|
-
|
|
71
|
-
## Mood target
|
|
72
|
-
|
|
73
|
-
Walk in and feel like you discovered something — not a supermarket, not an unaffordable boutique, not a video-game shop, but all three at once. The charcoal shell says *industrial secret*; the aqua wave floor says *I know this place*; the spaced, warm-lit display says *this is worth something*. The floor now reads as a **sun-bleached aqua wave** rather than an arcade-saturated blue, which is the point: it belongs to the same coast you can see through the rear glass.
|
|
74
|
-
|
|
75
|
-
## Engineering follow-up (not this task)
|
|
76
|
-
|
|
77
|
-
A walkable interior needs a second world map / room and a door transition (the `container-exterior.md` entrance door leads here), plus interior OBJECT ids in **both** `map.js`/`terrain.js` twins (server + client). File as an engineering task once the interior layout ([#642](https://example.com/builders#/task/642)) and floor tile ([#644](https://example.com/builders#/task/644)) are final.
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# World map — the "Sealed Ruins Chart" (diegetic artifact, V2)
|
|
2
|
-
|
|
3
|
-
- **Task:** [#648](https://example.com/builders#/task/648) (V2 · product). Promoted from idea_inbox [#169](https://example.com/builders#/idea/169) (builder 25 art/design brief, 2026-06-04).
|
|
4
|
-
- **Date:** 2026-06-18
|
|
5
|
-
- **Status:** Art + design spec ready. Two follow-ups it implies are separate tasks: the **diegetic-item mechanic** (engineering) and **tile generation** (waits on the palette swap, [#640](https://example.com/builders#/task/640)/[ADR 0044](../adr/0044-mediterranean-palette-replacement.md)). See *Follow-ups* below.
|
|
6
|
-
|
|
7
|
-
## Concept
|
|
8
|
-
|
|
9
|
-
The world map is **not a UI overlay or menu** — it is a **diegetic in-game artifact**: an ancient sealed chart the player *discovers*. This reframes navigation from a game mechanic into a story beat. The in-world item name is the **"Sealed Ruins Chart"** — "sealed" is load-bearing: the map is a mystery; not everything is visible from the start.
|
|
10
|
-
|
|
11
|
-
> Reference concept art (builder-25 planning input): `art/references/concept-art-original/world-map/Map Item Pop Up Concept.webp` + `Shipping container Concept.png`. **NB:** these planning images are not committed to the repo (the `art/references/concept-art-original/` tree is empty here); this written spec is the durable, self-contained source.
|
|
12
|
-
|
|
13
|
-
## Behaviour (the "diegetic" part — engineering follow-up, not this art task)
|
|
14
|
-
|
|
15
|
-
- The player does **not** open a menu. They find / are given the scroll — an inventory item, like the FireRed Town Map received from a character.
|
|
16
|
-
- Examining it plays an **unroll animation**.
|
|
17
|
-
- It is **progressive / fog-of-war**: sealed areas begin as blank parchment and fill in as the player explores. (This is the "sealed" mystery made mechanical.)
|
|
18
|
-
|
|
19
|
-
## Scroll art spec (pixel art)
|
|
20
|
-
|
|
21
|
-
- **Rollers (top + bottom):** dark mahogany brown, visible wood grain (2–3 horizontal pixel lines per plank); cylindrical implied by a lighter highlight strip along the top edge.
|
|
22
|
-
- **Corner accents:** round emerald-green gemstone insets, 8×8px each, single light highlight pixel upper-left.
|
|
23
|
-
- **Parchment:** aged warm tan, **not uniform** — subtle darker age/wear patches; slight edge vignette.
|
|
24
|
-
- **Title banner:** a rectangular banner at the top, slightly darker parchment, **"SEALED RUINS CHART"** in a serif pixel font (dark brown, not black).
|
|
25
|
-
- **Footer:** bottom roller repeats the title left + right with a centred **emblem** between — a 32×32 pixel creature (dragon / sea-monster), dark-outline style.
|
|
26
|
-
|
|
27
|
-
### Map surface — the swirling currents (what makes it feel ancient)
|
|
28
|
-
The parchment is densely covered in hand-drawn-style arrows + curved flowing lines — **ocean-current / wind notations**, the language of old nautical charts (NOT paths):
|
|
29
|
-
- thin 1px dark-brown curved, directional lines; arrow tips = simple 2–3px chevrons;
|
|
30
|
-
- density ≈ one arrow/curve segment per 16×16 area over **sea**; **land** areas sparser;
|
|
31
|
-
- this texture — organic, dense, directional — is what reads as *ancient + discovered*, not *designed*.
|
|
32
|
-
|
|
33
|
-
## Colour note (palette)
|
|
34
|
-
The hexes in the source brief (mahogany, emerald, #D4A96A parchment) are **targets, not literals**: when generated, every pixel quantizes to the locked 64-colour palette — and that palette is being **replaced** ([#640](https://example.com/builders#/task/640) / [ADR 0044](../adr/0044-mediterranean-palette-replacement.md)). The replacement must carry warm-tan / mahogany / emerald steps for this artifact; flag if it doesn't. This is a UI/item artifact (not a world ground tile), so it is less palette-constrained than terrain, but it should still read in-world.
|
|
35
|
-
|
|
36
|
-
## Mood target
|
|
37
|
-
The player should feel they found something they were **not supposed to find** — a real object from another time, not a game menu. First unroll = one of the most memorable moments in the game.
|
|
38
|
-
|
|
39
|
-
## Follow-ups (separate tasks, not this art spec)
|
|
40
|
-
1. **Diegetic-item mechanic** (engineering): inventory item + examine→unroll animation + progressive fog-of-war reveal keyed to discovered areas. File when the inventory/item system exists (relates to the V3 purchase-loop / bag work, e.g. [#635](https://example.com/builders#/task/635)).
|
|
41
|
-
2. **Scroll tile/sprite generation** (art pipeline): produce the scroll frame + parchment + emblem assets — **after** the palette swap ([#640](https://example.com/builders#/task/640)) so it isn't re-quantized + redone.
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
# Recipe — stand up claudebongos.com + geminibongos.com (vanity doors → cloudbongos.com)
|
|
2
|
-
|
|
3
|
-
> Brings **claudebongos.com** and **geminibongos.com** live as public "vanity
|
|
4
|
-
> doors": each shows the exact Cloud Bongos landing **gate** (`brand/cloud-bongos/gate`),
|
|
5
|
-
> rebranded to **"Claude Bongos" / `> claude`** (resp. **"Gemini Bongos" / `> gemini`**)
|
|
6
|
-
> at runtime — the visitor still types `bongos`, and on enter the gate redirects
|
|
7
|
-
> to **cloudbongos.com**, landing straight on the orbs. Same task also **promotes
|
|
8
|
-
> the gate onto the cloudbongos.com apex** (it replaces the buddha placeholder,
|
|
9
|
-
> which is what makes the redirect land "on the orbs"). Task
|
|
10
|
-
> [#1746](https://example.com/builders#/task/1746).
|
|
11
|
-
|
|
12
|
-
## Architecture (what we're building)
|
|
13
|
-
|
|
14
|
-
One gate bundle, three domains, a runtime switch — **no forked copies**. `space.js`
|
|
15
|
-
reads `location.hostname`; a door rebrands the entry + redirects on enter, and the
|
|
16
|
-
default (cloudbongos.com) reveals the orbs in place. Any future change to the gate
|
|
17
|
-
shows up on all three automatically.
|
|
18
|
-
|
|
19
|
-
| Domain | Serves | On typing `bongos` + enter |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| **claudebongos.com** | the gate, rebranded "Claude Bongos" / `> claude` | `→ https://cloudbongos.com/?entered=1` (orbs) |
|
|
22
|
-
| **geminibongos.com** | the gate, rebranded "Gemini Bongos" / `> gemini` | `→ https://cloudbongos.com/?entered=1` (orbs) |
|
|
23
|
-
| **cloudbongos.com** | the gate, "Cloud Bongos" (**was** the buddha page) | reveals the orbs in place |
|
|
24
|
-
|
|
25
|
-
Hosting is the **cheapest possible**, same as the staging gate:
|
|
26
|
-
|
|
27
|
-
| Thing | Value |
|
|
28
|
-
|---|---|
|
|
29
|
-
| Served by | Caddy `file_server`, web root `/home/lars/example/brand/cloud-bongos` |
|
|
30
|
-
| The two doors | **pure static** — no app process, no port, no password |
|
|
31
|
-
| cloudbongos.com apex | static gate for `/` + gate assets; **still proxies** `/api`, `/healthz`, etc. to the platform app (`:3002`) so sign-in keeps working |
|
|
32
|
-
| TLS | **one new Cloudflare Origin Cert per new domain** — the cloudbongos cert's SANs do NOT cover claudebongos.com / geminibongos.com |
|
|
33
|
-
| Cost | **$0/mo** — same droplet, no new process/port |
|
|
34
|
-
|
|
35
|
-
Why static (not a reverse-proxy block): the gate is plain HTML/CSS/JS + PNGs.
|
|
36
|
-
There is nothing to run. The `?entered=1` query flag is how a door hands off — the
|
|
37
|
-
gate honors it (only on non-door hosts) to skip its own entry and open the orbs.
|
|
38
|
-
|
|
39
|
-
Everything in the repo (`space.js`, the `infra/Caddyfile` blocks, the gate art)
|
|
40
|
-
auto-deploys to the droplet checkout on merge. What is **NOT** automatic — and is
|
|
41
|
-
this recipe — is the per-domain **DNS + Origin Cert** and the **live Caddyfile
|
|
42
|
-
merge + reload**. Until those are done, the two domains show a TLS error (expected).
|
|
43
|
-
|
|
44
|
-
## Phase ① — DNS (Cloudflare, ~1 min per domain)
|
|
45
|
-
|
|
46
|
-
Both domains are already on Cloudflare. For **each** zone (claudebongos.com,
|
|
47
|
-
geminibongos.com):
|
|
48
|
-
|
|
49
|
-
1. Cloudflare → pick the zone → **DNS** → **Add record**.
|
|
50
|
-
2. Type **A**, Name **`@`**, IPv4 **`REDACTED_IP`**, Proxy status **Proxied**
|
|
51
|
-
(orange cloud ON), TTL Auto. Save.
|
|
52
|
-
3. Cloudflare → **SSL/TLS** → **Overview** → set encryption mode to **Full (strict)**.
|
|
53
|
-
|
|
54
|
-
(Optional: add a **`www`** A record → same IP, proxied, if you want `www.` to work.
|
|
55
|
-
The Caddyfile blocks are apex-only today; add `www.claudebongos.com` to the site
|
|
56
|
-
line if you add the record.)
|
|
57
|
-
|
|
58
|
-
## Phase ② — Origin Certificate (own domain → own cert)
|
|
59
|
-
|
|
60
|
-
Each new domain needs its **own** Origin Cert. For **each** zone:
|
|
61
|
-
|
|
62
|
-
Cloudflare → **SSL/TLS → Origin Server → Create Certificate**:
|
|
63
|
-
- Hostnames: `claudebongos.com` **and** `*.claudebongos.com` (resp. the gemini pair)
|
|
64
|
-
- Let Cloudflare generate the key; **copy both** the certificate and the private key.
|
|
65
|
-
|
|
66
|
-
Install on the droplet (operator). Caddy runs as user `caddy` and **must be able to
|
|
67
|
-
READ the key** — match the existing `origin.key` ownership (`640 root:caddy`). A
|
|
68
|
-
root-only `600` key makes Caddy's reload fail (learned during the cloudbongos
|
|
69
|
-
bring-up). The `.crt` can be world-readable.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
# claudebongos
|
|
73
|
-
sudo install -m 644 -g caddy /dev/stdin /etc/caddy/claudebongos-origin.crt # paste cert, Ctrl-D
|
|
74
|
-
sudo install -m 640 -g caddy /dev/stdin /etc/caddy/claudebongos-origin.key # paste key, Ctrl-D
|
|
75
|
-
# geminibongos
|
|
76
|
-
sudo install -m 644 -g caddy /dev/stdin /etc/caddy/geminibongos-origin.crt # paste cert, Ctrl-D
|
|
77
|
-
sudo install -m 640 -g caddy /dev/stdin /etc/caddy/geminibongos-origin.key # paste key, Ctrl-D
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Phase ③ — apply the Caddy blocks + reload
|
|
81
|
-
|
|
82
|
-
Three blocks ship in `infra/Caddyfile`: the two new **door** blocks
|
|
83
|
-
(`claudebongos.com`, `geminibongos.com`) and the **modified `cloudbongos.com`
|
|
84
|
-
apex** block (now serves the gate + proxies functional paths). The live
|
|
85
|
-
`/etc/caddy/Caddyfile` has drifted slightly from the repo copy, so **do not blindly
|
|
86
|
-
`cp` the whole file** — apply the two pieces by hand:
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
cd /home/lars/example
|
|
90
|
-
git pull # or wait for the auto-deploy; the brand/ bundle + infra/Caddyfile land here
|
|
91
|
-
|
|
92
|
-
# 1. Append the two door blocks (idempotent: skip if already present).
|
|
93
|
-
grep -q '^claudebongos.com {' /etc/caddy/Caddyfile || \
|
|
94
|
-
awk '/^# ── Claude Bongos \+ Gemini Bongos — vanity DOORS/{p=1} p' infra/Caddyfile \
|
|
95
|
-
| sed '/^# ── Cloud Bongos — STAGING/,$d' >> /etc/caddy/Caddyfile
|
|
96
|
-
# (or just open the file and paste the two blocks from infra/Caddyfile — the
|
|
97
|
-
# awk above copies from the DOORS comment to end-of-file.)
|
|
98
|
-
|
|
99
|
-
# 2. Modify the LIVE cloudbongos.com apex block. Open the file, find the
|
|
100
|
-
# `cloudbongos.com {` block, and replace its body — the single
|
|
101
|
-
# `reverse_proxy 127.0.0.1:3002 { … }` — with the gate-serving body from
|
|
102
|
-
# infra/Caddyfile (root + @hidden + @gate handle + the catch-all reverse_proxy).
|
|
103
|
-
# Leave builders.cloudbongos.com and status.cloudbongos.com UNTOUCHED.
|
|
104
|
-
sudo nano /etc/caddy/Caddyfile
|
|
105
|
-
|
|
106
|
-
# 3. Validate (as root — it must read the 640 origin keys), then reload.
|
|
107
|
-
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
|
108
|
-
sudo systemctl reload caddy # fails closed on a bad config
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
> A bad config makes `reload` error out and **keep serving the old config** — the
|
|
112
|
-
> live sites never go down. If a cert path is wrong, that one site fails to come
|
|
113
|
-
> up while the others keep serving.
|
|
114
|
-
|
|
115
|
-
## Phase ④ — verify
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
# from anywhere — the two doors:
|
|
119
|
-
curl -sSL https://claudebongos.com/ | grep -o 'space.css\|gate' # gate HTML served ✓
|
|
120
|
-
curl -sSL https://geminibongos.com/ | grep -o 'space.css\|gate' # gate HTML served ✓
|
|
121
|
-
curl -sS -o /dev/null -w '%{http_code}\n' https://claudebongos.com/ # 200 (via the /gate/ redirect) ✓
|
|
122
|
-
|
|
123
|
-
# cloudbongos.com apex now serves the gate, and the app still answers:
|
|
124
|
-
curl -sSL https://cloudbongos.com/ | grep -o 'space.css\|gate' # gate served at apex ✓
|
|
125
|
-
curl -sS https://cloudbongos.com/healthz # "ok" — app still reachable ✓
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Then open each in **Chrome or Edge** (the live-glass refraction needs Chromium
|
|
129
|
-
`backdrop-filter` + SVG displacement; Safari falls back to a plain frost, still
|
|
130
|
-
fine):
|
|
131
|
-
|
|
132
|
-
- `https://claudebongos.com` → gate reads **"Claude Bongos" / `> claude`**. Type
|
|
133
|
-
`bongos`, press enter → you land on **cloudbongos.com showing the orbs**.
|
|
134
|
-
- `https://geminibongos.com` → same, **"Gemini Bongos" / `> gemini`**.
|
|
135
|
-
- `https://cloudbongos.com` → gate reads "Cloud Bongos"; type `bongos` → orbs in place.
|
|
136
|
-
- **Sign-in still works:** `https://builders.cloudbongos.com` → sign in with GitHub
|
|
137
|
-
(the OAuth callback is on the apex, `/api/gds/auth/web/callback`, which the
|
|
138
|
-
catch-all still proxies to `:3002`). If sign-in breaks, the apex `@gate`/`handle`
|
|
139
|
-
split is wrong — recheck step ③.2.
|
|
140
|
-
|
|
141
|
-
## Rollback
|
|
142
|
-
|
|
143
|
-
- **Take a door down:** delete its block from `/etc/caddy/Caddyfile`,
|
|
144
|
-
`sudo systemctl reload caddy`, remove the Cloudflare `@` A record.
|
|
145
|
-
- **Revert cloudbongos.com to the buddha page:** restore the apex block to the
|
|
146
|
-
single `reverse_proxy 127.0.0.1:3002 { … }` body, reload. The buddha landing
|
|
147
|
-
(`public-landing/index.html`) is untouched in the repo and served again
|
|
148
|
-
immediately.
|
|
149
|
-
|
|
150
|
-
## Files this recipe uses
|
|
151
|
-
|
|
152
|
-
- `infra/Caddyfile` — the two door blocks + the modified `cloudbongos.com` apex block.
|
|
153
|
-
- `brand/cloud-bongos/gate/space.js` — the runtime hostname switch (branding +
|
|
154
|
-
redirect + `?entered` auto-open).
|
|
155
|
-
- `brand/cloud-bongos/` — the gate bundle + the world art it refracts (shared by all three).
|