@bongos/core 1.19.468 → 1.19.469

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 (32) hide show
  1. package/.bongos-core.json +81 -36
  2. package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +169 -0
  3. package/docs/adr/README.md +1 -0
  4. package/docs/copy-inventory.md +5 -10
  5. package/docs/copy-registry.json +15 -92
  6. package/docs/file-map.md +5 -1
  7. package/docs/module-api-changelog.md +2 -0
  8. package/docs/research/task-network-map/a-every-task-p2.png +0 -0
  9. package/docs/research/task-network-map/a-every-task-p40.png +0 -0
  10. package/docs/research/task-network-map/b-dependency-forest-p2.png +0 -0
  11. package/docs/research/task-network-map/b-dependency-forest-p40.png +0 -0
  12. package/docs/research/task-network-map/c-goals-p2.png +0 -0
  13. package/docs/research/task-network-map/c-goals-p40.png +0 -0
  14. package/docs/research/task-network-map/d-joined-by-goal.png +0 -0
  15. package/docs/research/task-network-map-readability.md +191 -0
  16. package/modules/hall-ui/public/people.css +3 -2
  17. package/modules/hall-ui/public/people.html +2 -2
  18. package/modules/hall-ui/public/people.js +5 -4
  19. package/modules/hall-ui/public/people.states.json +1 -1
  20. package/modules/hall-ui/public/settings.js +1 -1
  21. package/modules/hall-ui/public/style.css +4 -3
  22. package/package-lock.json +2 -2
  23. package/package.json +1 -1
  24. package/scripts/gds/spike-task-map.js +730 -0
  25. package/src/bongos/serve-internal.js +20 -8
  26. package/src/module-api.js +1 -1
  27. package/tests/hall_page_gate_map.mjs +4 -2
  28. package/tests/hall_reading_world.mjs +2 -2
  29. package/tests/hall_roster_world.mjs +40 -12
  30. package/tests/spike_task_map.mjs +0 -0
  31. package/modules/hall-ui/public/scouting.html +0 -69
  32. package/modules/hall-ui/public/scouting.states.json +0 -59
@@ -3,17 +3,17 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 1591,
6
+ "strings": 1586,
7
7
  "unique_ids": 1376,
8
- "certain": 1591,
8
+ "certain": 1586,
9
9
  "heuristic": 0,
10
- "duplicate_ids": 154
10
+ "duplicate_ids": 151
11
11
  },
12
12
  "surfaces": {
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
- "files": 73,
16
- "strings": 1148
15
+ "files": 72,
16
+ "strings": 1143
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -168,18 +168,6 @@
168
168
  "modules/public-landing/public/projects.html:1042"
169
169
  ]
170
170
  },
171
- {
172
- "id": "b95918d421ae",
173
- "surface": "builders-hall",
174
- "text": "People — {{worldName}}",
175
- "occurrences": 4,
176
- "at": [
177
- "modules/hall-ui/public/people.html:6",
178
- "modules/hall-ui/public/people.html:6",
179
- "modules/hall-ui/public/scouting.html:6",
180
- "modules/hall-ui/public/scouting.html:6"
181
- ]
182
- },
183
171
  {
184
172
  "id": "3eb67b892dea",
185
173
  "surface": "landing",
@@ -499,16 +487,6 @@
499
487
  "modules/hall-ui/public/watch.js:315"
500
488
  ]
501
489
  },
502
- {
503
- "id": "29bd516ad17e",
504
- "surface": "builders-hall",
505
- "text": "Builder directory",
506
- "occurrences": 2,
507
- "at": [
508
- "modules/hall-ui/public/people.html:37",
509
- "modules/hall-ui/public/scouting.html:42"
510
- ]
511
- },
512
490
  {
513
491
  "id": "563d29ed86d8",
514
492
  "surface": "builders-hall",
@@ -709,16 +687,6 @@
709
687
  "modules/hall-ui/public/drachmae.html:6"
710
688
  ]
711
689
  },
712
- {
713
- "id": "ddebbccd4b37",
714
- "surface": "builders-hall",
715
- "text": "Everyone building across hosted projects, ranked by track record. Descriptive only — cross-project numbers never carry authority here.",
716
- "occurrences": 2,
717
- "at": [
718
- "modules/hall-ui/public/people.html:29",
719
- "modules/hall-ui/public/scouting.html:34"
720
- ]
721
- },
722
690
  {
723
691
  "id": "f70f217504ce",
724
692
  "surface": "status-dashboard",
@@ -879,16 +847,6 @@
879
847
  "modules/status-ui/public/index.html:312"
880
848
  ]
881
849
  },
882
- {
883
- "id": "2bde9c630fd2",
884
- "surface": "builders-hall",
885
- "text": "Loading the directory…",
886
- "occurrences": 2,
887
- "at": [
888
- "modules/hall-ui/public/people.html:40",
889
- "modules/hall-ui/public/scouting.html:45"
890
- ]
891
- },
892
850
  {
893
851
  "id": "ceec2218efe3",
894
852
  "surface": "builders-hall",
@@ -1049,6 +1007,16 @@
1049
1007
  "modules/hall-ui/public/goals.html:187"
1050
1008
  ]
1051
1009
  },
1010
+ {
1011
+ "id": "b95918d421ae",
1012
+ "surface": "builders-hall",
1013
+ "text": "People — {{worldName}}",
1014
+ "occurrences": 2,
1015
+ "at": [
1016
+ "modules/hall-ui/public/people.html:6",
1017
+ "modules/hall-ui/public/people.html:6"
1018
+ ]
1019
+ },
1052
1020
  {
1053
1021
  "id": "ed8edd684547",
1054
1022
  "surface": "builders-hall",
@@ -2478,15 +2446,6 @@
2478
2446
  "origin": "html-text",
2479
2447
  "confidence": "certain"
2480
2448
  },
2481
- {
2482
- "id": "29bd516ad17e",
2483
- "surface": "builders-hall",
2484
- "text": "Builder directory",
2485
- "file": "modules/hall-ui/public/scouting.html",
2486
- "line": 42,
2487
- "origin": "html-text",
2488
- "confidence": "certain"
2489
- },
2490
2449
  {
2491
2450
  "id": "08bfcc9198ba",
2492
2451
  "surface": "builders-hall",
@@ -4431,15 +4390,6 @@
4431
4390
  "origin": "html-text",
4432
4391
  "confidence": "certain"
4433
4392
  },
4434
- {
4435
- "id": "ddebbccd4b37",
4436
- "surface": "builders-hall",
4437
- "text": "Everyone building across hosted projects, ranked by track record. Descriptive only — cross-project numbers never carry authority here.",
4438
- "file": "modules/hall-ui/public/scouting.html",
4439
- "line": 34,
4440
- "origin": "html-text",
4441
- "confidence": "certain"
4442
- },
4443
4393
  {
4444
4394
  "id": "4750e12477e7",
4445
4395
  "surface": "builders-hall",
@@ -5583,15 +5533,6 @@
5583
5533
  "origin": "html-text",
5584
5534
  "confidence": "certain"
5585
5535
  },
5586
- {
5587
- "id": "2bde9c630fd2",
5588
- "surface": "builders-hall",
5589
- "text": "Loading the directory…",
5590
- "file": "modules/hall-ui/public/scouting.html",
5591
- "line": 45,
5592
- "origin": "html-text",
5593
- "confidence": "certain"
5594
- },
5595
5536
  {
5596
5537
  "id": "fcd755e8dd30",
5597
5538
  "surface": "builders-hall",
@@ -6789,24 +6730,6 @@
6789
6730
  "origin": "html-title",
6790
6731
  "confidence": "certain"
6791
6732
  },
6792
- {
6793
- "id": "b95918d421ae",
6794
- "surface": "builders-hall",
6795
- "text": "People — {{worldName}}",
6796
- "file": "modules/hall-ui/public/scouting.html",
6797
- "line": 6,
6798
- "origin": "html-text",
6799
- "confidence": "certain"
6800
- },
6801
- {
6802
- "id": "b95918d421ae",
6803
- "surface": "builders-hall",
6804
- "text": "People — {{worldName}}",
6805
- "file": "modules/hall-ui/public/scouting.html",
6806
- "line": 6,
6807
- "origin": "html-title",
6808
- "confidence": "certain"
6809
- },
6810
6733
  {
6811
6734
  "id": "4a46b478edaf",
6812
6735
  "surface": "builders-hall",
package/docs/file-map.md CHANGED
@@ -68,6 +68,9 @@
68
68
  │ │ ├── overnight-builder.md ← (#358) operator runbook for the autonomous overnight session-runner
69
69
  │ │ ├── builder-box-lifecycle.md ← (#598/ADR 0031) operator runbook: provision/park/wake/deprovision dev boxes, the two sweeps, config env, going-live steps
70
70
  │ │ └── managed-settings-remote-control.md ← (#599/ADR 0031 §1, #760/ADR 0038) operator+builder guide: distribute the locked Claude Desktop SSH config; the browser web-terminal (ttyd + Cloudflare Tunnel) on-ramp
71
+ │ ├── research/ ← investigations that answer a question rather than record a decision (an ADR) or a procedure (a recipe)
72
+ │ │ ├── task-network-map-readability.md ← (task 1003282, goal 1000072) the spike verdict: a node-link map of ~1,357 tasks is unreadable at every dependency density — 59–97% isolated dots, and the dense fix overplots 84% of them into the 40-node goal graph. Build the goal graph + a per-goal dependency forest; measure the real density with `--from` first
73
+ │ │ └── task-network-map/ ← the figures that verdict argues from, rendered by scripts/gds/spike-task-map.js — regenerate, never hand-edit
71
74
  │ ├── reviews/ ← temporary audit/review docs (e.g. claude-md-audit-V3.R86.md)
72
75
  │ ├── session-logs/ ← one file per session, named YYYY-MM-DD-<slug>.md (CLAUDE.md §13 indexes them)
73
76
  │ └── session-log-archive.md ← compressed older session-log prose
@@ -127,7 +130,7 @@
127
130
  │ ├── sessions/ ← CORE-DOMAIN carve (BV1.R82, default-on): the session_records corpus + data plane (upload, cross-builder search/deep-dive), the BFG read-the-room scorer, session-pulse/stale-claim helpers. Registers no port — reached via HTTP POST to its own /sessions route; resolves the economy `reward` port for the cost-plus token reward. CLAUDE.md, module.json, routes/sessions.js, bfg.js, pulse.js.
128
131
  │ ├── autonomy/ ← CORE-DOMAIN carve (BV1.R83, default-on): the MAS autonomy surfaces — the $0 deterministic autonomy gate (System 3, inert until armed), the conductor/architect CLIs, and the in-process routine timers (incl. the weekly peer-vote-tally poller). Registers no port; resolves the lifecycle `tallyPeerVotes` doorway. CLAUDE.md, module.json, routes/autonomy.js, autonomy.js.
129
132
  │ ├── status-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R84, default-on): the public status dashboard at status.<apex>, moved from public-status/. NO routes/db/port — declares contributes.webSurfaces:[{host:"status.",dir:"public"}], served by the loader web-surface seam (moduleWebSurfaces → serve-internal). CLAUDE.md, module.json, public/{index.html,status.js,style.css,cursors/,snapshots/}.
130
- │ ├── hall-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R85, default-on): the authenticated builders' hall at builders.<apex>, moved from public-builders/ (~13k LOC, the largest). NO routes/db/port — declares contributes.webSurfaces:[{host:"builders.",dir:"public"}] for discovery, but UNLIKE status-ui keeps its DEDICATED serving block in serve-internal (the /builders shim + canonical redirects + citizen/Archon page gates + asset-stamping + hall-widget injection), partitioned out of the generic web-surface loop and served from BUILDERS_DIR. CLAUDE.md, module.json, public/{index.html,builders.js,style.css,work,watch,harbor,gate,pair,settings,ranks,sessions,primer,diagrams,atlas,...}.{html,js},cursors/,chime.wav. Three ARCHETYPE sheets sit beside the per-page ones: `record.css` (the task + idea records, task 1003305), `room.css` (the READING ROOMS — the primer + the diagrams index, task 1003306: the reading measure and rhythm, the table of contents, the progress hairline and the framed figure diagram-viewer.js upgrades on both pages) and `oversight.css` (the OVERSIGHT family — government, watch, harbor, gate, task 1003469: the row, the figure band inside a panel, the group head, the inline form, the grant toggle and the shared confirm modal; named oversight because the vocabulary wall bans "governance" on the government surface). A page sheet then holds only what one page has and the others do not — `primer.css` the acknowledge gate, `diagrams.css` the legend strip, `gate.css` the hold reasons and file chips, `watch.css` the severities, the report reader and the grade trend, `people.css` the directory's identity cell (task 1003470: `/scouting` is the LEGACY DOOR to `/people` and ships the same body and script — `scouting.html` loads `people.js`; there is no `scouting.js`).
133
+ │ ├── hall-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R85, default-on): the authenticated builders' hall at builders.<apex>, moved from public-builders/ (~13k LOC, the largest). NO routes/db/port — declares contributes.webSurfaces:[{host:"builders.",dir:"public"}] for discovery, but UNLIKE status-ui keeps its DEDICATED serving block in serve-internal (the /builders shim + canonical redirects + citizen/Archon page gates + asset-stamping + hall-widget injection), partitioned out of the generic web-surface loop and served from BUILDERS_DIR. CLAUDE.md, module.json, public/{index.html,builders.js,style.css,work,watch,harbor,gate,pair,settings,ranks,sessions,primer,diagrams,atlas,...}.{html,js},cursors/,chime.wav. Three ARCHETYPE sheets sit beside the per-page ones: `record.css` (the task + idea records, task 1003305), `room.css` (the READING ROOMS — the primer + the diagrams index, task 1003306: the reading measure and rhythm, the table of contents, the progress hairline and the framed figure diagram-viewer.js upgrades on both pages) and `oversight.css` (the OVERSIGHT family — government, watch, harbor, gate, task 1003469: the row, the figure band inside a panel, the group head, the inline form, the grant toggle and the shared confirm modal; named oversight because the vocabulary wall bans "governance" on the government surface). A page sheet then holds only what one page has and the others do not — `primer.css` the acknowledge gate, `diagrams.css` the legend strip, `gate.css` the hold reasons and file chips, `watch.css` the severities, the report reader and the grade trend, `people.css` the directory's identity cell (task 1003470 made the legacy door `/scouting` ship the same body and script as `/people`; task 1003440 then deleted the door — `scouting.html` and `scouting.states.json` are gone and the URL 301s to `/people`).
131
134
  │ ├── lifecycle/ ← CORE-DOMAIN carve (BV1.R86, default-on): the build state machine — tasks · claims · versions · done-when · goals · dependencies · claim→ship→grade→credit · github-push + merge-lock + conflict-resolve + publish-reconciler. The LAST, most-coupled carve; registers the `lifecycle` kernel port (createTask, classifyTaskKind, tallyPeerVotes, work-tracking reads). CLAUDE.md, module.json, routes/{tasks,claims,versions,done-when,goals,dependencies,gate-approvals,analytics}.js, lifecycle.js, github-push.js, merge-lock.js, conflict-resolve.js, publish-reconciler.js, task-visuals.js + task-visual-db.js + routes/visuals.js (the optional ship-time visual, task 1003109 — the route file is NOT named task-*.js on purpose; see the module CLAUDE.md).
132
135
  │ ├── copy-desk/ ← CORE-DOMAIN module (task 1003113 / R02 of goal 1000074, default-on): the COPY half of the design contract — ADR 0081 gave design TOKENS a repo-owned source of truth, text had none. Owns the FLAG (anyone in any role marks a string or a whole surface as needing an artist's attention, with a reason required in BOTH the route and a CHECK constraint) and the QUEUE an artist reads it from. READS R01's committed registry (docs/copy-registry.json, generated by scripts/gds/copy-inventory.js) and never writes it. NO LIVE CMS is the hard non-goal: no column here could hold replacement text, and copy_no_cms.mjs pins that. Key is `copy-desk`, not `copy` — the branding contract already owns a `copy` block, so the bare word reds the "core carries no module code" fitness check. CLAUDE.md, module.json, registry.js, flags.js, routes/copy-desk.js, migrations/copy_desk_001_flags.sql, tests/{copy_flags,copy_queue,copy_no_cms}.mjs. Its hall surface lives with hall-ui (public/copy-desk.{html,js,css}), per the one-key-per-web-surface precedent.
133
136
  │ ├── ui-design/ ← CORE-DOMAIN module (task 1003324 / ADR 0197, default-on): the design capability every builder on every instance gets — ships METHOD, never a world. Absorbs ADR 0081's layer: adapters/{claude-design,figma}/index.js (moved from src/ui/adapters/), scripts/{design-sync,figma-design-sync,validate-design}.js (moved from scripts/gds/, which keeps thin shims), docs/design-contract.md (moved from docs/). Declaration-only: contributes.skills design (the /design playbook, new) + design-sync + figma-design-sync; no routes/port/migrations, so disabling it touches no served surface. CLAUDE.md separates the platform FLOORS (the Fifteen Rule, the dark twins, no page :root, the 24px floor, AA in both modes, the kill switch) from the instance's WORLD (its packs + DESIGN.md). kit/ (task 1003320) is the look-before-you-ship kit: lib.js (the floors, the states/probes contracts, the action + expectation vocabularies, the WCAG maths, the stub spawner), serve.js (any surface through the doorway's real transforms + fixtures/), render.js (states × 1440/390/320 × dark/light, audited), probe.js (PASS/FAIL contracts), check-mock.js (platform rules + the instance's DESIGN.md taste bans, then the detector), tells.js (task 1003329 / ADR 0223: the anti-pattern detector as two tiers — floors FAIL, craft tells WARN, kit-ignore waivers per file; the rendered twins live in lib.js factsInPage), contrast.js + png.js; a page's <page>.states.json / <page>.probes.json sit BESIDE the surface (modules/public-landing/public/, modules/hall-ui/public/). Recipe: docs/recipes/ui-look-before-you-ship.md. skills/ (task 1003321 / ADR 0198) is the vendoring policy: policy.json (licence allowlist, verdicts that may land, sidecar fields, preamble markers), PREAMBLE.md (the platform preamble every vendored SKILL.md opens with), README.md (the roster — both origins scanned dangerous on 2026-08-28, landed as rebuild specs; nothing vendored — and the landing recipe); a landable copy goes under skills/vendor/<name>/ with PROVENANCE.md + LICENSE and is materialised into an instance's .claude/skills/ by claude-materialize.js step 1b. skills/<name>/ (task 1003325 / ADR 0220) are the in-house rebuilds from the specs, first-party and counted: design-taste-frontend (the default taste skill), design-taste-frontend-v1 (the earlier generation, kept distinct), high-end-visual-design (the polish pass), redesign-existing-projects (the in-place upgrade); each opens with the same preamble and carries a "Rebuilt from" section instead of a sidecar. impeccable/ (task 1003326 / ADR 0221) is the design-craft method as ONE skill with sub-commands: SKILL.md (the router, the menu, the craft floor, the bounded finish) + reference/<sub-command>.md (eighteen playbooks, one loaded per request); no hooks, no bypass agent, no telemetry, no live server. image-to-code/, imagegen-frontend-web/, imagegen-frontend-mobile/, brandkit/ (task 1003328 / ADR 0222) are the image family under the hero-only rule: plates and hero objects only, the pair (JPEG dark plate + WebP alpha cutout) with a <file>.json sidecar beside each, through the instance's art pipeline key; the grove specimen's styles/grove/assets/ is the worked example. styles/ (task 1003323 / ADR 0219) is the style library: <name>/pack.json (the theme.ui overlay, exactly the fifteen) + DESIGN.md (the look's derived recipes, thirteen in lockstep with the pack) + mock.html (the specimen); chrome-world (pinned to the neutral pack), expedition, grove; STYLE=<name> on the kit lays a look over the instance's pack through GDS_BRANDING_FILE; tests/ui_design_styles.mjs runs the world/cosmos/hall token suites once per alternate (UI_DESIGN_PACK). config/design-tokens.* + design-sources.json STAY in config/ as the host boundary; public-landing keeps its own key (first customer, not owner).
@@ -159,6 +162,7 @@
159
162
  │ ├── next-number.js ← deterministic next-free ADR + migration number from origin/main ∪ local working tree; supersedes the drift-prone claim-time DB counter (V4.R26, [#964](https://example.com/builders#/task/964)). `node scripts/gds/next-number.js [--json]`
160
163
  │ ├── gen-repo-map.js ← Path B (ADR 0063, [#1224](https://example.com/builders#/task/1224)): zero-dep, deterministic symbol-skeleton generator → docs/repo-map.md + the generated factual block in each nested CLAUDE.md; ranks exported symbols by reference frequency. `--check` = CI gate; regenerated on deploy by ship.js regenerateRepoMap()
161
164
  │ ├── gen-file-map.js ← ADR 0066 ([#1276](https://example.com/builders#/task/1276)): generates the `.claude/skills/` + `.claude/scheduled-tasks/` sections of docs/file-map.md from disk; per-entry notes come from docs/file-map.notes.json; `--check` fails on drift OR a skill/routine with no note (CI gate via fitness.js); regenerated on deploy by ship.js regenerateFileMap()
165
+ │ ├── spike-task-map.js ← (task 1003282, goal 1000072) the task-network-map spike harness: builds a ~1,357-task graph across a swept dependency density, lays it out for real (<redacted> + Barnes-Hut), counts what decides readability (isolated nodes, components, distinguishable marks / overplot, crossings, ink budget, payload) and renders PNGs with a hand-rolled encoder. Zero deps, no DB. `--from <tasks.json>` measures the REAL corpus instead of the model. Verdict: docs/research/task-network-map-readability.md
162
166
  │ ├── fitness.js ← Path C (ADR 0062 §9, [#1225](https://example.com/builders#/task/1225)): architecture fitness functions — core↔host import boundary (zero grandfathered exceptions as of R52/#1191), CLAUDE.md budget + nested-doc presence (ADR 0061), no route bypasses rank checks (reuses route-rank-check), repo-map freshness. Enforced in CI via tests/fitness.mjs (the `unit` gate). `node scripts/gds/fitness.js`
163
167
  │ ├── ship.js ← resolve claim as shipped, award credits (`/builder-ship`); also uploads a secret-scrubbed session digest to the corpus (6D.1, ADR 0027); on a dev box runs the sandbox-first review gate before resolving (#927, ADR 0046)
164
168
  │ ├── ship-visual.js ← the OPTIONAL `--visual <image> [--visual-alt "…"]` leg of a ship (task 1003109): screens the file BEFORE the claim resolves (bad input costs no claim), uploads it AFTER (a failed picture never fails a ship)
@@ -1373,5 +1373,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
1373
1373
  landed since 1.19.466 with no explicit bump. run 33638703282. (task 1002620)
1374
1374
  1.19.468 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1375
1375
  landed since 1.19.467 with no explicit bump. run 33640370009. (task 1002620)
1376
+ 1.19.469 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1377
+ landed since 1.19.468 with no explicit bump. run 33643388774. (task 1002620)
1376
1378
  ---------------------------------------------------------------------------
1377
1379
  ```
@@ -0,0 +1,191 @@
1
+ # Can a network map of ~1,357 tasks be read? — the spike
2
+
3
+ - **Task:** [1003282](https://cloudbongos.com/builders#/task/1003282) · goal 1000072 (Human legibility)
4
+ - **Date:** 2026-08-28
5
+ - **Harness:** [`scripts/gds/spike-task-map.js`](../../scripts/gds/spike-task-map.js) — no dependencies, no network, no DB. Re-run with `node scripts/gds/spike-task-map.js`.
6
+ - **Figures:** [`docs/research/task-network-map/`](task-network-map/) — rendered by the harness, not drawn by hand.
7
+
8
+ ## The answer
9
+
10
+ **No — not as a map of tasks, at any dependency density, and the obvious fix makes it
11
+ worse.** What *is* readable is the forty-node **goal** graph as the entry point, with a
12
+ per-goal **dependency forest** as the drill-down. That second view is 3–40% of the
13
+ corpus, never 100%, and that is the finding rather than a compromise: the tasks it
14
+ leaves out are the ones with nothing to connect them to.
15
+
16
+ The map worth building is therefore **not** "the task graph, zoomed out". It is the
17
+ goal graph, drawn once, with a task-level view that opens *inside* a goal.
18
+
19
+ ## How it was measured
20
+
21
+ The house rule for questions of this shape was set by this goal's own shipped criterion
22
+ `legibility-sort-and-staleness`: *"~1,357 tasks on BONGOS-V1 alone. A view that does not
23
+ survive that number is not a solution"* — answered in `tests/goal_sort_staleness.mjs` by
24
+ **executing** the board pipeline over a corpus that size. A network map is the same
25
+ question one dimension harder, so it got the same treatment: build the graph, lay it out
26
+ with a real Fruchterman–Reingold pass (Barnes–Hut quadtree, θ=0.8 — the approximation
27
+ d3-force uses), draw it to a real raster, and count.
28
+
29
+ **The dependency density is swept, not guessed.** How many tasks actually declare a
30
+ `task_dependencies` row is a fact of the live DB, which this environment cannot reach.
31
+ Rather than assume one number, the harness sweeps p ∈ {2, 5, 10, 20, 40}% — the share of
32
+ tasks declaring at least one dependency — and reports where each conclusion would flip.
33
+ Every conclusion below holds across the whole range, which is why the missing fact does
34
+ not change the verdict. Point the harness at a real dump to replace every number here
35
+ with a measured one:
36
+
37
+ ```
38
+ node scripts/gds/spike-task-map.js --from tasks.json # a GET /tasks?include=deps dump
39
+ ```
40
+
41
+ Four views of the same corpus, all at 1280×720 (a laptop viewport with the hall's chrome
42
+ removed — quoting a 4K desktop would flatter the result for a screen most readers do not
43
+ have):
44
+
45
+ | | view | what it is |
46
+ |---|---|---|
47
+ | **A** | every task | the literal reading of "a network map of 1,300 tasks" |
48
+ | **B** | tasks with a dependency | A with the unconnected nodes swept off |
49
+ | **C** | goals | one node per goal, cross-goal dependencies aggregated |
50
+ | **D** | tasks joined by goal | the obvious second attempt, once A turns out to be sparse |
51
+
52
+ ## The numbers
53
+
54
+ ```
55
+ density view nodes edges isol% comps marks lost% cross pitch label? ms KB
56
+ ------------------------------------------------------------------------------------------------------------------------
57
+ p=2% every task 1357 21 97.2 1336 1355 0.1 0 26.1px NO 861 6
58
+ p=2% tasks with a dependency 38 21 0 17 38 0 0 155.7px yes 34 0
59
+ p=2% goals 40 5 80 35 40 0 0 151.8px yes 54 0
60
+ any tasks joined by goal 1357 22341 0 40 222 83.6 1492927~ 26.1px NO 1003 232
61
+ p=5% every task 1357 44 94.1 1313 1352 0.4 1 26.1px NO 855 6
62
+ p=5% tasks with a dependency 80 44 0 36 80 0 1 107.3px yes 24 1
63
+ p=5% goals 40 7 72.5 33 40 0 2 151.8px yes 8 0
64
+ p=10% every task 1357 81 89.7 1277 1351 0.4 2 26.1px NO 818 6
65
+ p=10% tasks with a dependency 140 81 0 60 140 0 2 81.1px yes 54 1
66
+ p=10% goals 40 13 52.5 27 40 0 2 151.8px yes 9 0
67
+ p=20% every task 1357 168 79.2 1189 1336 1.5 5 26.1px NO 884 7
68
+ p=20% tasks with a dependency 282 168 0 114 282 0 8 57.2px yes 143 3
69
+ p=20% goals 40 31 22.5 12 40 0 12 151.8px yes 9 0
70
+ p=40% every task 1357 351 59.5 1006 1320 2.7 33 26.1px NO 897 9
71
+ p=40% tasks with a dependency 549 351 0 198 549 0 30 41px NO 325 6
72
+ p=40% goals 40 50 2.5 2 40 0 44 151.8px yes 10 0
73
+ ```
74
+
75
+ `marks` = distinct 7px cells any node lands in — what a reader can actually tell apart.
76
+ `lost%` = share of nodes overplotting a mark another node already owns.
77
+ `pitch` = √(canvas area ÷ nodes), the square each node owns. `label?` asks whether that
78
+ square is as wide as a six-character label (48px).
79
+
80
+ ## What each row says
81
+
82
+ ### A — "every task" is confetti, not a hairball
83
+
84
+ This is the surprise, and it is the whole result. A 1,357-node graph is *expected* to
85
+ fail as a tangle: too many edges, too many crossings, one illegible ball of yarn. It
86
+ fails the other way. **59.5% to 97.2% of tasks have no dependency at all**, so the
87
+ picture is 1,006–1,336 separate components — overwhelmingly single dots — and **33
88
+ crossings at the densest end**, which is nothing.
89
+
90
+ ![Every task, dependency edges, p=40% — the densest case measured](task-network-map/a-every-task-p40.png)
91
+
92
+ That disc of 1,320 distinguishable dots with a few three-node chains in the middle is
93
+ the *best* case in the sweep. Two things follow, and neither is fixable by drawing
94
+ better:
95
+
96
+ - **A force layout is arranging nothing.** It has no opinion about where a thousand
97
+ unconnected islands go, so their positions carry no information. Every dot's location
98
+ is an accident of the seed. A reader who infers anything from proximity here is being
99
+ misled by the renderer.
100
+ - **Labels never fit.** 26.1px of pitch against the 48px a six-character label needs —
101
+ and that is the *average*, before clustering. So the map cannot say which task any dot
102
+ is, which means the only way to read it is to hover 1,357 times.
103
+
104
+ Sparse-and-disconnected is a *different failure* from dense-and-tangled, and it needs a
105
+ different answer. You cannot declutter your way out of it; there is no clutter.
106
+
107
+ ### D — making it dense draws the goal graph, badly
108
+
109
+ The natural next move once A turns out sparse: connect tasks by something that actually
110
+ relates them. Shared goal is the obvious candidate, and it is the one a "task network"
111
+ would reach for.
112
+
113
+ ![Tasks joined by goal — 1,357 nodes and 22,341 edges](task-network-map/d-joined-by-goal.png)
114
+
115
+ Forty dots. **22,341 edges, ~1.49 million crossings, 232 KB of graph, a full second of
116
+ layout — and 83.6% of the nodes overplot, leaving 222 marks that read as forty.** Each
117
+ goal is a clique, so each clique contracts to a blob smaller than the dot used to draw
118
+ it, and every edge inside it is sub-pixel.
119
+
120
+ The dense view spends three orders of magnitude more edges to draw, illegibly, the
121
+ picture that view C draws legibly with fifty. That is not a tuning problem. It is the
122
+ data saying that at this scale the only structure the eye can extract from 1,357 tasks
123
+ **is** the forty goals.
124
+
125
+ ### C — the goal graph is readable, everywhere in the sweep
126
+
127
+ ![Goals, p=40%](task-network-map/c-goals-p40.png)
128
+
129
+ Forty nodes, 5–50 edges, zero overplot, 151.8px of pitch — three times what a label
130
+ needs — and it stays readable at every density measured. It is also the view the system
131
+ already computes: `modules/lifecycle/goal-rollup.js` produces one row per goal in
132
+ Postgres precisely because the browser could not hold the task corpus (5,229,455 bytes
133
+ for ~1,400 tasks, measured 2026-08-25). **The readable map is the one whose data layer
134
+ already exists.**
135
+
136
+ At low density it has its own honest weakness — 80% of goals are isolated at p=2%,
137
+ because cross-goal dependencies are what draw its edges. A goal graph with no edges is a
138
+ legend, not a map. Which side of that line the live corpus falls on is the one number
139
+ worth measuring before building.
140
+
141
+ ### B — the dependency forest is the drill-down
142
+
143
+ ![Tasks with a dependency, p=40% — 549 nodes](task-network-map/<redacted>.png)
144
+
145
+ Sweeping the isolated nodes off A leaves 38–549 nodes and zero overplot, readable with
146
+ labels up to p=20% and as unlabelled dots beyond. This is the task-level view that
147
+ works, and it works *because* it does not try to show every task.
148
+
149
+ Note what it costs: at p=40% it still fails the label test (41px pitch), so even the
150
+ forest wants scoping. Per goal it is 1–14 nodes — comfortably readable, and the natural
151
+ thing to open when someone clicks a goal in C.
152
+
153
+ ## What to build, if anything
154
+
155
+ 1. **The goal graph (C) as the map.** Forty nodes, edges = cross-goal dependencies,
156
+ weight = task count, colour = the ADR 0192 category the goals already carry. Its data
157
+ is one `goalRollups` query plus a cross-goal dependency count; the payload is bytes,
158
+ not megabytes.
159
+ 2. **The dependency forest (B), scoped to one goal, as the drill-down.** Not a zoom of a
160
+ global map — a separate, small query answered when a goal is opened.
161
+ 3. **Never view A, and never view D.** Not as a default, not as an "advanced" toggle.
162
+ A shows a thousand dots whose positions are noise; D shows forty dots and calls it a
163
+ thousand. Both invite a reader to believe they are seeing structure that is not there,
164
+ which is a worse outcome than no map — this goal is about legibility, and a confident
165
+ picture of nothing is the opposite of it.
166
+
167
+ **Before any of it is built, measure the real density**, with `--from`. Two numbers
168
+ decide the shape: the share of tasks carrying a dependency (does B have anything in it?)
169
+ and the share of dependencies that cross a goal boundary (does C have any edges?). If
170
+ cross-goal dependencies are near zero, C is forty disconnected dots and the honest
171
+ deliverable is a grid of goal cards, not a graph — which the hall's Goals page already
172
+ is. **That is a real possible outcome of this spike, and it would mean the right amount
173
+ of new map to build is none.**
174
+
175
+ ## Caveats, stated plainly
176
+
177
+ - **The corpus is synthetic.** The live DB is unreachable from the environment this ran
178
+ in, so the harness sweeps a plausible range instead of measuring one truth. The sweep
179
+ is what makes the verdict robust — every conclusion above holds from p=2% to p=40% —
180
+ but the *specific* numbers are model output, not observation. Re-run with `--from`.
181
+ - **The goal count (40) and the 85% within-goal edge preference are modelled**, from the
182
+ repo's own record of ~38 open goals on BONGOS-V1 (`scripts/gds/goal-names.js`) and from
183
+ how dependencies are declared in practice. A very different real distribution would
184
+ change C's edge count — not A's isolation, which is bounded by p by construction.
185
+ - **`marks` is a proxy for legibility, not legibility.** It counts what is
186
+ *distinguishable*, not what is *understandable*; a map can clear it and still say
187
+ nothing. It is used here only to make overplotting a number, which is the one thing
188
+ looking at a picture reliably fails to do.
189
+ - **The layout is one algorithm.** A hierarchical or Sugiyama-style layout would draw
190
+ view B's chains better than a force pass does. It would not change A (there is nothing
191
+ to lay out) or D (the overplotting is arithmetic, not layout).
@@ -1,6 +1,7 @@
1
1
  /* people.css — page-local styles for the cross-project builder directory
2
- (task 1003470, goal 1000081), served at /people and at its legacy door
3
- /scouting, which loads the same page. Only the identity cell lives here: the
2
+ (task 1003470, goal 1000081), served at /people — its legacy door /scouting
3
+ was deleted in task 1003440 and now 301s here. Only the identity cell lives
4
+ here: the
4
5
  ledger, the filter bar, the pager, the chips and the empty state are the
5
6
  shell's and the kit's. Tokens are the world's real names — no compat alias. */
6
7
 
@@ -30,8 +30,8 @@
30
30
  </div>
31
31
 
32
32
  <main id="hall-main">
33
- <!-- task 1003470: ONE directory. /people and its legacy door /scouting
34
- render this same page from the same script — the same
33
+ <!-- task 1003470: ONE directory. Its legacy door /scouting rendered this
34
+ same page from the same script, and task 1003440 deleted it — the same
35
35
  /api/gds/scouting rollup used to be drawn three different ways. -->
36
36
  <section class="scroll" id="people-scroll" aria-labelledby="people-h">
37
37
  <h2 id="people-h" class="scroll__h">Builder directory<span class="count-pill" id="people-count" hidden></span></h2>
@@ -1,10 +1,11 @@
1
1
  // people.js — the People page: the cross-project builder directory (task
2
- // 1002577; ADR 0141 §4 is the data's design of record), served at /people and
3
- // at its legacy door /scouting — task 1003470 made the two ONE page, this
4
- // script, because the same /api/gds/scouting rollup was being drawn three
2
+ // 1002577; ADR 0141 §4 is the data's design of record), served at /people —
3
+ // task 1003470 first made its legacy door /scouting render this same script,
4
+ // and task 1003440 deleted that door (the URL 301s here), because the
5
+ // same /api/gds/scouting rollup was being drawn three
5
6
  // different ways (a kit ledger here, a hand-built table there, and the same
6
7
  // columns in a different order). The endpoint is gated server-side on
7
- // 'project.curate' OR being a project owner (R14, ADR 0210); both pages are
8
+ // 'project.curate' OR being a project owner (R14, ADR 0210); this page is
8
9
  // gated in serve-internal.js on that same pair, so a visitor who is neither
9
10
  // never sees this shell.
10
11
  //
@@ -1,5 +1,5 @@
1
1
  {
2
- "_": "People — the cross-project directory behind requireProjectCuratePage (metic+ via project.curate, or a project owner). Descriptive only; it grants no authority, and the page says so above the fold. Written with the re-skin (task 1003307); the redesign (task 1003470) added the identity cell, the sort and craft lenses, and the `search` state. /scouting is the legacy door to this same page (scouting.states.json measures it separately). Populated look: the hall-preview harness (scripts/hall-preview/fixtures/scouting.json carries five builders, three with avatars).",
2
+ "_": "People — the cross-project directory behind requireProjectCuratePage (metic+ via project.curate, or a project owner). Descriptive only; it grants no authority, and the page says so above the fold. Written with the re-skin (task 1003307); the redesign (task 1003470) added the identity cell, the sort and craft lenses, and the `search` state. Its legacy door /scouting was deleted in task 1003440; the URL 301s here. Populated look: the hall-preview harness (scripts/hall-preview/fixtures/scouting.json carries five builders, three with avatars).",
3
3
  "page": "/builders/people",
4
4
  "surface": "hall-ui",
5
5
  "stub": {
@@ -555,7 +555,7 @@
555
555
  // GET/PATCH /api/bongos/me/cross-project (platform-identity module — hub only).
556
556
  // One on/off toggle: "show my cross-project record on my public profile".
557
557
  // Enforcement is server-side in the module's read functions, so flipping it
558
- // takes effect immediately on the profile, /people, and /scouting. The section
558
+ // takes effect immediately on the profile and /people. The section
559
559
  // reveals ONLY when the module's endpoint answers (404 on a vanilla instance →
560
560
  // stays hidden, no error state). P04 later re-homes this in the Profile rail
561
561
  // group; storage is account-global on the hub (not behavior_prefs).
@@ -2859,9 +2859,10 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
2859
2859
  does not exist, so "Someone helped" rendered full-size on every row. */
2860
2860
  .btn--sm { min-height: 26px; padding: 0 11px; font-size: 12.5px; }
2861
2861
 
2862
- /* people.js:45 — the builder's name cell. The only definition in the hall lives
2863
- in scouting.html's inline <style>, so the same data read bold on Scouting and
2864
- plain on People. One definition, both pages. */
2862
+ /* people.js:45 — the builder's name cell. The hall's only definition used to
2863
+ live in scouting.html's inline <style>, so the same data read bold on
2864
+ Scouting and plain on People. It lives here now, and scouting.html itself
2865
+ went with task 1003440. */
2865
2866
  .who { font-weight: 600; }
2866
2867
 
2867
2868
  /* people.js:105 — notice() sets body.className='note'. .note lives in
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.468",
3
+ "version": "1.19.469",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.468",
9
+ "version": "1.19.469",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.468",
3
+ "version": "1.19.469",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",