@bongos/core 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/.bongos-core.json +363 -312
  2. package/.claude/skills/blocker-review/SKILL.md +1 -1
  3. package/.claude/skills/builder-claim/SKILL.md +2 -0
  4. package/.claude/skills/builder-exit/SKILL.md +2 -2
  5. package/.claude/skills/builder-redteam/SKILL.md +3 -3
  6. package/.claude/skills/builder-setup/SKILL.md +2 -2
  7. package/.claude/skills/builder-start/SKILL.md +1 -1
  8. package/.claude/skills/dev/SKILL.md +1 -1
  9. package/.claude/skills/idea-triage/SKILL.md +2 -2
  10. package/.claude/skills/ideate/SKILL.md +1 -1
  11. package/.claude/skills/merge-mode/SKILL.md +1 -1
  12. package/.claude/skills/new-project/SKILL.md +3 -0
  13. package/.claude/skills/paint/SKILL.md +1 -1
  14. package/CLAUDE.md +11 -238
  15. package/GOVERNANCE.md +1 -1
  16. package/README.md +1 -1
  17. package/bin/bongos.js +1 -0
  18. package/clients/bongos-client/README.md +4 -4
  19. package/clients/bongos-client/bongos-client.global.js +14 -1
  20. package/clients/bongos-client/index.cjs +14 -1
  21. package/clients/bongos-client/index.d.ts +19 -2
  22. package/clients/bongos-client/index.mjs +11 -1
  23. package/clients/bongos-client/package.json +1 -1
  24. package/docs/adr/0001-stack-choice-single-vps.md +1 -1
  25. package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
  26. package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
  27. package/docs/adr/0020-security-model-public-stub.md +1 -1
  28. package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
  29. package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
  30. package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
  31. package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
  32. package/docs/adr/0030-builders-hall-live-updates.md +1 -1
  33. package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
  34. package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
  35. package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
  36. package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
  37. package/docs/adr/0044-per-box-live-game-preview.md +1 -1
  38. package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
  39. package/docs/adr/0057-container-cost-ledger.md +3 -3
  40. package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
  41. package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
  42. package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
  43. package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
  44. package/docs/adr/0096-require-reward-before-workable.md +9 -9
  45. package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
  46. package/docs/adr/0100-two-repo-instance-model.md +1 -1
  47. package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
  48. package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
  49. package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
  50. package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
  51. package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -0
  52. package/docs/adr/0134-private-first-npm-distribution.md +20 -6
  53. package/docs/adr/0135-module-upstream-submission-interim-queue.md +34 -0
  54. package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
  55. package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
  56. package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
  57. package/docs/adr/README.md +14 -10
  58. package/docs/api/openapi.json +315 -6
  59. package/docs/api-reference.md +10 -5
  60. package/docs/architecture.md +3 -3
  61. package/docs/branding-contract.md +5 -4
  62. package/docs/canonical-permissions.md +1 -1
  63. package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
  64. package/docs/file-map.md +20 -19
  65. package/docs/handoff-template.md +2 -2
  66. package/docs/modules-contract.md +8 -1
  67. package/docs/onboarding/diagrams/README.md +7 -7
  68. package/docs/onboarding/drachmae-and-karma.md +18 -18
  69. package/docs/onboarding/primer.md +9 -9
  70. package/docs/recipes/audit-replay.md +2 -2
  71. package/docs/recipes/builder-box-lifecycle.md +2 -2
  72. package/docs/recipes/claude-design-loop.md +1 -1
  73. package/docs/recipes/instance-deploy.md +4 -4
  74. package/docs/recipes/instance-first-run.md +51 -0
  75. package/docs/recipes/ops-gotchas.md +1 -1
  76. package/docs/recipes/packaging-the-core.md +5 -5
  77. package/docs/recipes/private-npm-distribution.md +39 -12
  78. package/docs/recipes/self-host.md +1 -1
  79. package/docs/recipes/staging-environment.md +1 -1
  80. package/docs/recipes/upgrading-the-core.md +63 -8
  81. package/docs/research/cursor-competitive-analysis.md +1 -1
  82. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
  83. package/migrations/core_191_module_submissions.sql +45 -0
  84. package/modules/autonomy/CLAUDE.md +11 -45
  85. package/modules/builder-settings/CLAUDE.md +11 -54
  86. package/modules/dev-box/CLAUDE.md +11 -34
  87. package/modules/discord/CLAUDE.md +11 -46
  88. package/modules/discord/module.json +2 -1
  89. package/modules/economy/CLAUDE.md +11 -44
  90. package/modules/economy/achievements.js +9 -1
  91. package/modules/economy/credits.js +10 -1
  92. package/modules/grading/CLAUDE.md +11 -38
  93. package/modules/hall-ui/CLAUDE.md +11 -57
  94. package/modules/hall-ui/public/builders.js +71 -4
  95. package/modules/hall-ui/public/goal-inbox.js +191 -0
  96. package/modules/hall-ui/public/goals-page.js +227 -22
  97. package/modules/hall-ui/public/goals.html +16 -0
  98. package/modules/hall-ui/public/hall-widgets.js +14 -1
  99. package/modules/hall-ui/public/index.html +3 -0
  100. package/modules/hall-ui/public/new-project-wizard.js +6 -4
  101. package/modules/hall-ui/public/style.css +43 -0
  102. package/modules/ideas/CLAUDE.md +11 -33
  103. package/modules/lifecycle/CLAUDE.md +11 -59
  104. package/modules/lifecycle/routes/goals.js +80 -0
  105. package/modules/memory/CLAUDE.md +11 -36
  106. package/modules/onboarding/CLAUDE.md +11 -51
  107. package/modules/provisioning/CLAUDE.md +15 -0
  108. package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
  109. package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
  110. package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
  111. package/modules/provisioning/module.json +17 -0
  112. package/modules/provisioning/onboard-plan.js +37 -0
  113. package/modules/provisioning/provisioning.js +539 -0
  114. package/modules/provisioning/rate-limit.js +35 -0
  115. package/modules/provisioning/routes/provisioning.js +607 -0
  116. package/modules/provisioning/tests/provisioning.mjs +232 -0
  117. package/modules/provisioning/ui/.gitkeep +0 -0
  118. package/modules/security/CLAUDE.md +11 -40
  119. package/modules/security/secret-scrub.js +10 -0
  120. package/modules/sessions/CLAUDE.md +11 -35
  121. package/modules/status-ui/CLAUDE.md +11 -43
  122. package/package-lock.json +2 -2
  123. package/package.json +7 -2
  124. package/public-landing/create.html +58 -12
  125. package/public-landing/index.html +40 -21
  126. package/scripts/discord/README.md +1 -1
  127. package/scripts/gds/CLAUDE.md +11 -140
  128. package/scripts/gds/claim.js +98 -48
  129. package/scripts/gds/claude-materialize.js +174 -20
  130. package/scripts/gds/claude-md-template.js +84 -0
  131. package/scripts/gds/cli-lib.js +112 -2
  132. package/scripts/gds/cutover-codemod.js +241 -0
  133. package/scripts/gds/fitness.js +54 -0
  134. package/scripts/gds/gen-api-client.js +10 -10
  135. package/scripts/gds/init.js +112 -16
  136. package/scripts/gds/lint-infra-exec.js +70 -0
  137. package/scripts/gds/lint-workflow-exec.js +63 -0
  138. package/scripts/gds/migration-namespace.js +1 -1
  139. package/scripts/gds/mirror-lineage-check.js +14 -5
  140. package/scripts/gds/mirror-redact.js +8 -0
  141. package/scripts/gds/module.js +118 -4
  142. package/scripts/gds/npmrc.js +93 -0
  143. package/scripts/gds/onboard.js +17 -24
  144. package/scripts/gds/package-core.js +74 -17
  145. package/scripts/gds/provision.js +102 -17
  146. package/scripts/gds/publish-manifest.js +60 -2
  147. package/scripts/gds/run-routine.js +51 -13
  148. package/scripts/gds/run-unit-tests.js +4 -0
  149. package/scripts/gds/seed-adopt-tasks.js +1 -1
  150. package/scripts/gds/status.js +6 -1
  151. package/scripts/gds/task.js +166 -0
  152. package/scripts/gds/update-channel.js +186 -0
  153. package/scripts/gds/upgrade.js +172 -25
  154. package/src/bongos/CLAUDE.md +11 -60
  155. package/src/bongos/auth.js +24 -0
  156. package/src/bongos/module-submissions.js +56 -0
  157. package/src/bongos/route-rank-check.js +6 -0
  158. package/src/bongos/routes/CLAUDE.md +11 -39
  159. package/src/bongos/routes/auth.js +6 -1
  160. package/src/bongos/routes/modules.js +123 -21
  161. package/src/bongos/serve-internal.js +10 -1
  162. package/src/branding.js +1 -0
  163. package/src/instance-config.js +1 -1
  164. package/src/module-api.js +24 -1
  165. package/src/module-loader/loader.js +36 -4
  166. package/src/module-loader/manifest-schema.js +16 -1
  167. package/tests/branding.mjs +11 -0
  168. package/tests/claim_error_surface.mjs +107 -0
  169. package/tests/claude_materialize.mjs +125 -0
  170. package/tests/consumer_layout_boot.mjs +302 -0
  171. package/tests/cutover_codemod.mjs +184 -0
  172. package/tests/device_flow_start.mjs +96 -0
  173. package/tests/first_run_engine_check.mjs +79 -0
  174. package/tests/goal_membership_requests.mjs +33 -3
  175. package/tests/hook_core_resolve.mjs +125 -0
  176. package/tests/hook_user_agents.mjs +62 -0
  177. package/tests/infra_exec_paths.mjs +104 -0
  178. package/tests/init.mjs +106 -6
  179. package/tests/instance_mismatch_guard.mjs +101 -0
  180. package/tests/mirror_lineage_proof.mjs +14 -0
  181. package/tests/module_cli.mjs +130 -2
  182. package/tests/module_loader.mjs +42 -0
  183. package/tests/module_manifest.mjs +19 -0
  184. package/tests/module_submissions.mjs +135 -0
  185. package/tests/npmrc.mjs +105 -0
  186. package/tests/onboard.mjs +62 -0
  187. package/tests/package_core.mjs +76 -22
  188. package/tests/platform_boot.mjs +5 -2
  189. package/tests/provision.mjs +107 -8
  190. package/tests/provisioning_github_app_routes.mjs +44 -2
  191. package/tests/publish_manifest.mjs +52 -1
  192. package/tests/run_routine.mjs +26 -1
  193. package/tests/run_routine_two_root.mjs +77 -0
  194. package/tests/scrubber_corpus.mjs +17 -0
  195. package/tests/served_paths_shipped.mjs +2 -2
  196. package/tests/task_cli.mjs +99 -0
  197. package/tests/update_channel.mjs +186 -0
  198. package/tests/upgrade.mjs +220 -25
  199. package/tests/workflow_exec_paths.mjs +98 -0
  200. package/docs/design/container-exterior-tiles.md +0 -49
  201. package/docs/design/container-exterior.md +0 -81
  202. package/docs/design/container-interior.md +0 -77
  203. package/docs/design/world-map-scroll.md +0 -41
  204. package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
  205. package/docs/recipes/cloudbongos-staging-gate.md +0 -139
  206. package/docs/recipes/cloudbongos-standup.md +0 -111
  207. package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
  208. package/docs/recipes/parallel-load-test.md +0 -32
  209. package/docs/recipes/pixel-art-pipeline.md +0 -411
  210. package/docs/recipes/staging-data.md +0 -50
  211. package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
  212. package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
  213. package/migrations/instance/110_medusa_planning_seed.sql +0 -70
  214. package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
  215. package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
  216. package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
  217. package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
  218. package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
  219. package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
  220. package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
  221. package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
  222. package/scripts/gds/art-stage.js +0 -152
  223. package/server.js +0 -212
  224. package/tests/art_key.mjs +0 -121
  225. package/tests/day_cycle.mjs +0 -197
  226. package/tests/snapshot_picker.mjs +0 -233
  227. package/tests/sprite_facing.mjs +0 -35
  228. package/tests/terrain_picker.mjs +0 -298
  229. package/tests/turn_in_place.mjs +0 -280
  230. package/tests/walkability.mjs +0 -188
@@ -1,188 +0,0 @@
1
- // tests/walkability.mjs
2
- //
3
- // Verify the ground-object split's walkability semantics:
4
- // - Ground SEA blocks (terrain-level).
5
- // - Ground GRASS / SAND walkable on their own.
6
- // - Object ROCK on grass blocks (object-level).
7
- // - Object CONTAINER_PANEL_DOOR on sand blocks (object-level).
8
- // - Both also blocks (object wins, same outcome).
9
- // - Out-of-bounds blocks.
10
- //
11
- // Exercises the SERVER-side isBlocked() — that's the source of truth
12
- // for movement validation. Loads it via require() since the server
13
- // module is CJS.
14
- //
15
- // Run: node tests/walkability.mjs
16
-
17
- import { strict as assert } from 'node:assert';
18
- import { createRequire } from 'node:module';
19
-
20
- const require = createRequire(import.meta.url);
21
- const serverMap = require('../modules/game/world/map.js');
22
- const serverTerrain = require('../modules/game/world/terrain.js');
23
-
24
- const { isBlocked, INITIAL_MAP, WORLD_COLS, WORLD_ROWS } = serverMap;
25
- const { TERRAIN, OBJECT, isBlockingTerrain, isBlockingObject } = serverTerrain;
26
-
27
- let passed = 0;
28
- function test(name, fn) {
29
- try {
30
- fn();
31
- console.log(` ok ${name}`);
32
- passed++;
33
- } catch (e) {
34
- console.error(` FAIL ${name}: ${e.message}`);
35
- process.exitCode = 1;
36
- }
37
- }
38
-
39
- // ---------- helpers ------------------------------------------------------------
40
-
41
- // Find a sample cell of each kind in the actual INITIAL_MAP.
42
- function findCell(predicate) {
43
- for (let y = 0; y < WORLD_ROWS; y++) {
44
- for (let x = 0; x < WORLD_COLS; x++) {
45
- if (predicate(INITIAL_MAP.ground[y][x], INITIAL_MAP.objects[y][x], x, y)) {
46
- return { x, y };
47
- }
48
- }
49
- }
50
- return null;
51
- }
52
-
53
- const seaCell = findCell((g, _o) => g === TERRAIN.SEA);
54
- const sandCell = findCell((g, o) => g === TERRAIN.SAND && !o);
55
- const grassCell = findCell((g, o) => g === TERRAIN.GRASS && !o);
56
- const rockCell = findCell((_g, o) => o === OBJECT.ROCK);
57
- const doorCell = findCell((_g, o) => o === OBJECT.CONTAINER_PANEL_DOOR);
58
- const panelCell = findCell((_g, o) => o === OBJECT.CONTAINER_PANEL_TL);
59
-
60
- // ---------- registry shape ------------------------------------------------------
61
-
62
- test('TERRAIN + OBJECT enums export expected ids', () => {
63
- assert.equal(TERRAIN.GRASS, 'grass');
64
- assert.equal(TERRAIN.SAND, 'sand');
65
- assert.equal(TERRAIN.SEA, 'sea');
66
- assert.equal(OBJECT.ROCK, 'rock');
67
- assert.equal(OBJECT.CONTAINER_PANEL_DOOR, 'container_panel_door');
68
- });
69
-
70
- test('isBlockingTerrain: SEA blocks; GRASS, SAND, MARBLE_PATH walkable', () => {
71
- assert.equal(isBlockingTerrain(TERRAIN.SEA), true);
72
- assert.equal(isBlockingTerrain(TERRAIN.GRASS), false);
73
- assert.equal(isBlockingTerrain(TERRAIN.SAND), false);
74
- assert.equal(isBlockingTerrain(TERRAIN.MARBLE_PATH), false);
75
- });
76
-
77
- test('isBlockingObject: ROCK + all 8 container panels block; null + PATH_MARBLE walkable', () => {
78
- assert.equal(isBlockingObject(null), false);
79
- assert.equal(isBlockingObject(undefined), false);
80
- assert.equal(isBlockingObject(OBJECT.ROCK), true);
81
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_TL), true);
82
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_T1), true);
83
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_T2), true);
84
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_TR), true);
85
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_DOOR), true);
86
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_B1), true);
87
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_B2), true);
88
- assert.equal(isBlockingObject(OBJECT.CONTAINER_PANEL_BR), true);
89
- // ADR 0013: paths are non-blocking objects (you walk on the path).
90
- assert.equal(isBlockingObject(OBJECT.PATH_MARBLE), false);
91
- });
92
-
93
- // ---------- isBlocked over the actual INITIAL_MAP ------------------------------
94
-
95
- test('INITIAL_MAP contains the expected cell kinds', () => {
96
- assert.ok(seaCell, 'no sea cell in INITIAL_MAP');
97
- assert.ok(sandCell, 'no plain-sand cell in INITIAL_MAP');
98
- assert.ok(grassCell, 'no plain-grass cell in INITIAL_MAP');
99
- assert.ok(rockCell, 'no rock cell in INITIAL_MAP');
100
- assert.ok(doorCell, 'no container-door cell in INITIAL_MAP');
101
- assert.ok(panelCell, 'no container_panel_tl cell in INITIAL_MAP');
102
- });
103
-
104
- test('sea cells are blocked', () => {
105
- assert.equal(isBlocked(seaCell.x, seaCell.y), true);
106
- });
107
-
108
- test('plain sand cells are walkable', () => {
109
- assert.equal(isBlocked(sandCell.x, sandCell.y), false);
110
- });
111
-
112
- test('plain grass cells are walkable', () => {
113
- assert.equal(isBlocked(grassCell.x, grassCell.y), false);
114
- });
115
-
116
- test('rock-on-grass cells are blocked (object wins)', () => {
117
- // The ground at the rock cell is GRASS (walkable). The object is ROCK
118
- // (blocking). isBlocked must return true.
119
- assert.equal(INITIAL_MAP.ground[rockCell.y][rockCell.x], TERRAIN.GRASS);
120
- assert.equal(INITIAL_MAP.objects[rockCell.y][rockCell.x], OBJECT.ROCK);
121
- assert.equal(isBlocked(rockCell.x, rockCell.y), true);
122
- });
123
-
124
- test('container-door cells are blocked (object wins; ground is sand)', () => {
125
- assert.equal(INITIAL_MAP.ground[doorCell.y][doorCell.x], TERRAIN.SAND);
126
- assert.equal(INITIAL_MAP.objects[doorCell.y][doorCell.x], OBJECT.CONTAINER_PANEL_DOOR);
127
- assert.equal(isBlocked(doorCell.x, doorCell.y), true);
128
- });
129
-
130
- test('all 8 container-panel cells share GROUND=sand and have an OBJECT', () => {
131
- // Container is at anchor (13, 8), 2 rows x 4 cols.
132
- for (let r = 0; r < 2; r++) {
133
- for (let c = 0; c < 4; c++) {
134
- const x = 13 + c, y = 8 + r;
135
- assert.equal(INITIAL_MAP.ground[y][x], TERRAIN.SAND, `(${x},${y}) ground should be sand`);
136
- assert.ok(INITIAL_MAP.objects[y][x], `(${x},${y}) object should not be null`);
137
- assert.equal(isBlocked(x, y), true, `(${x},${y}) should be blocked`);
138
- }
139
- }
140
- });
141
-
142
- test('vendor cell (13, 9) ground=sand, object=container_panel_door, blocked', () => {
143
- // Door MUST be at bottom-LEFT to preserve vendor placement.
144
- assert.equal(INITIAL_MAP.ground[9][13], TERRAIN.SAND);
145
- assert.equal(INITIAL_MAP.objects[9][13], OBJECT.CONTAINER_PANEL_DOOR);
146
- assert.equal(isBlocked(13, 9), true);
147
- });
148
-
149
- test('cell south of vendor (13, 10) is walkable (path-as-object on sand)', () => {
150
- // ADR 0013: marble path is now an OBJECT overlay over SAND ground.
151
- // The player approaches the vendor from here (faces up + presses E),
152
- // so the cell must remain walkable: sand is non-blocking ground +
153
- // path_marble is a non-blocking object.
154
- assert.equal(INITIAL_MAP.ground[10][13], TERRAIN.SAND);
155
- assert.equal(INITIAL_MAP.objects[10][13], OBJECT.PATH_MARBLE);
156
- assert.equal(isBlockingObject(OBJECT.PATH_MARBLE), false,
157
- 'PATH_MARBLE must be non-blocking (you walk on a path)');
158
- assert.equal(isBlocked(13, 10), false);
159
- });
160
-
161
- test('marble path strip (rows 10-13, col 13) all walkable, all path-on-sand', () => {
162
- // ADR 0013: the entire path strip is OBJECT.PATH_MARBLE over SAND.
163
- // No cell should block; ground should be uniform sand.
164
- for (let y = 10; y <= 13; y++) {
165
- assert.equal(INITIAL_MAP.ground[y][13], TERRAIN.SAND,
166
- `(13,${y}) ground should be SAND under the path overlay`);
167
- assert.equal(INITIAL_MAP.objects[y][13], OBJECT.PATH_MARBLE,
168
- `(13,${y}) object should be PATH_MARBLE`);
169
- assert.equal(isBlocked(13, y), false,
170
- `(13,${y}) should be walkable (sand + non-blocking path)`);
171
- }
172
- });
173
-
174
- test('out-of-bounds is blocked', () => {
175
- assert.equal(isBlocked(-1, 0), true);
176
- assert.equal(isBlocked(0, -1), true);
177
- assert.equal(isBlocked(WORLD_COLS, 0), true);
178
- assert.equal(isBlocked(0, WORLD_ROWS), true);
179
- });
180
-
181
- // ---------- summary -------------------------------------------------------------
182
-
183
- if (process.exitCode !== 1) {
184
- console.log(`\n[walkability] OK — ${passed} tests passed`);
185
- } else {
186
- console.error(`\n[walkability] FAIL — see output above`);
187
- process.exit(1);
188
- }