Skip to content

feat(explorer): ship Effort Graph visualizer with flatbread start - #230

Closed
tonyketcham wants to merge 2 commits into
mainfrom
stack/tonyketcham/feat/explorer-package/ship-effort-graph-visualizer-flatbread-start--5b888060
Closed

feat(explorer): ship Effort Graph visualizer with flatbread start#230
tonyketcham wants to merge 2 commits into
mainfrom
stack/tonyketcham/feat/explorer-package/ship-effort-graph-visualizer-flatbread-start--5b888060

Conversation

@tonyketcham

@tonyketcham tonyketcham commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Distribute the content-relation explorer as @flatbread/explorer so
consumers no longer need a separate Next app. When config uses
effortGraphContent(), flatbread start serves the SPA at / and
--open launches it; Apollo stays at /graphql.

Summary

  • Add @flatbread/explorer (Vite SPA + Node helpers) with a generic
    graph core and an Effort Graph preset migrated from examples/effort-viz
  • Hard-depend from flatbread; auto-mount on known presets with bootstrap
    injection for same-origin GraphQL/SSE
  • Support server-only flatbread start (no corunner) and static deploys
    via ?endpoint=
  • Point pnpm play:efforts at flatbread start --watch --open; retire
    examples/effort-viz; update CONTRIBUTING and Effort Graph setup skill

Test plan

  • pnpm --filter @flatbread/explorer test (79 tests)
  • pnpm --filter @flatbread/explorer typecheck
  • AVA: explorerMount, openPath, liveServerEffortGraph, liveServer
  • Smoke: flatbread start serves explorer HTML at / and GraphQL at
    /graphql with bootstrap injection
  • CI green on the stacked PR

Co-authored-by: Cursor cursoragent@cursor.com

Distribute the content-relation explorer as `@flatbread/explorer` so
consumers no longer need a separate Next app. When config uses
`effortGraphContent()`, `flatbread start` serves the SPA at `/` and
`--open` launches it; Apollo stays at `/graphql`.

## Summary
- Add `@flatbread/explorer` (Vite SPA + Node helpers) with a generic
  graph core and an Effort Graph preset migrated from `examples/effort-viz`
- Hard-depend from `flatbread`; auto-mount on known presets with bootstrap
  injection for same-origin GraphQL/SSE
- Support server-only `flatbread start` (no corunner) and static deploys
  via `?endpoint=`
- Point `pnpm play:efforts` at `flatbread start --watch --open`; retire
  `examples/effort-viz`; update CONTRIBUTING and Effort Graph setup skill

## Test plan
- [x] `pnpm --filter @flatbread/explorer test` (79 tests)
- [x] `pnpm --filter @flatbread/explorer typecheck`
- [x] AVA: explorerMount, openPath, liveServerEffortGraph, liveServer
- [x] Smoke: `flatbread start` serves explorer HTML at `/` and GraphQL at
  `/graphql` with bootstrap injection
- [ ] CI green on the stacked PR

Co-authored-by: Cursor <cursoragent@cursor.com>
Change-Id: I5b888060d859bb06b64528115ef89b098a3ae62c

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI remediation

Status: fixed and pushed

Failing checks:

  • build (20.x, ubuntu-latest) / build (22.x, ubuntu-latest): Cannot find module '@flatbread/effort-graph' during pnpm typecheck

Root cause:

  • packages/explorer/tsconfig.json set its own paths with only @/*. TypeScript replaces (does not merge) parent paths, so the root monorepo aliases — including @flatbread/effort-graph — were gone. CI typechecks before build, so there is no dist yet and the path alias is required.

Fix (bc899ad):

  • Re-declare the monorepo path map under baseUrl: "../.."
  • Set lib to ESNext and skipLibCheck: true so transitive source typechecks cleanly

Verified locally: pnpm typecheck (proof + explorer) passes.

Note: mergify stack push returned HTTP 401 here, so this landed as a follow-up commit on the stack branch via a normal push (not a force push).

Open in Web View Automation 

Sent by Cursor Automation: Flatbread - Fix CI

"@flatbread/resolver-svimg": ["./packages/resolver-svimg/src/index.ts"],
"@flatbread/utils": ["./packages/utils/src/index.ts"],
"@flatbread/explorer": ["./packages/explorer/src/node/index.ts"],
"@flatbread/effort-graph": ["./packages/effort-graph/src/index.ts"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failed here: child paths replaced the root map, so @flatbread/effort-graph was missing at typecheck-before-build. Restored the monorepo aliases (plus @/*) under baseUrl: "../..".

@mergify

mergify Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Review verdict

REQUEST_CHANGES — Consensus HIGH: CLI/--open/welcome treat Effort Graph preset match as “mounted,” while mountExplorerIfMatched can soft-fail when explorer assets are missing, plus the SPA+/graphql+/events coexistence paths are under-tested.

Blocking / high findings (priority)

  1. resolveCliOpenPath / welcome vs mount--open and the banner can send users to / while server.explorer is false and Apollo still owns /.
  2. resolveOpenPath contract drift — docs say “when mounted” but only check preset match; duplicates the CLI helper and skips the assets gate.
  3. explorerMount tests — title claims Apollo retention, but there are no behavioral hits on /graphql, /events, SPA fallback, or missing-index.htmlnull.

Coverage plan (top items)

  1. explorerMount.test.ts — missing index.html → warn + null
  2. explorerMount.test.ts — with assets, /graphql is not SPA HTML; /events calls next()
  3. openPath.test.ts — preset matches but no assets → open /graphql, not /
  4. liveServerEffortGraph.test.tsserver.explorer === true keeps SSE; missing assets → explorer === false
  5. endpoints.test.ts — relative ?endpoint=, host-without-scheme, whitespace, custom eventsPath

Reviewer scoreboard

  • correctness-and-contracts: 6 findings, 5 coverage gaps, signal:HIGH
  • test-coverage-robustness: 6 findings, 9 coverage gaps, signal:HIGH
  • cli-and-runtime: 6 findings, 5 coverage gaps, signal:HIGH
  • explorer-package: 6 findings, 6 coverage gaps, signal:MED
  • dx-and-examples: 6 findings, 4 coverage gaps, signal:MED

Perspectives: correctness-and-contracts, test-coverage-robustness, cli-and-runtime, explorer-package, dx-and-examples → judge. Models: grok-4.5 (HIGH), composer-2.5 (MED/LOW).

Open in Web View Automation 

Sent by Cursor Automation: Flatbread PR Review

Comment thread packages/flatbread/src/cli/index.ts
Comment thread packages/flatbread/src/cli/openPath.ts
Comment thread packages/flatbread/src/graphql/explorerMount.ts
Comment thread packages/flatbread/src/graphql/explorerMount.ts
Comment thread packages/flatbread/src/graphql/explorerMount.test.ts
Comment thread packages/explorer/vite.config.ts
Comment thread package.json

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Review verdict

REQUEST_CHANGES — consensus HIGH: three AVA suites rename the shared dist/static/index.html under concurrency: 4, and positive paths still soft-skip with t.pass('skipped…'), so mount/open proofs can go false-green. Rule: consensus HIGH / two+ independent HIGHs.

Prior openPath/assets-gate findings at bc899ad are closed in 4965f2f (resolveOpenPath / mount share explorerAssetsPresent). Vite dev proxy and preplay:efforts are also addressed.

Blocking (do these first)

  1. Isolate explorer asset hide/restore across openPath.test.ts, explorerMount.test.ts, and liveServerEffortGraph.test.ts (temp static dir, injectable FS, or cross-file mutex). test.serial only serializes within one file.
  2. Hard-fail (or require CI-built assets) on positive openPath / /events cases — drop soft-skip t.pass that masks races.

Also worth fixing

  • Vite base: './' breaks asset URLs for extensionless deep SPA routes served by the mount fallback — prefer base: '/' (or inject <base href="/">).
  • Explorer mount is startup-only; --watch config reload does not remount/clear SPA.
  • Skill setup still implies explorer always lands at / after --open without the assets-gate caveat.

Coverage plan (ordered)

  1. Cross-file isolation + hard-fail positives for the three suites above.
  2. Co-assert resolveOpenPath(content) === (mountExplorerIfMatched(…)?.openPath ?? '/graphql') for assets present and missing.
  3. From deep-route HTML, fetch script src and expect 200 (relative base edge).
  4. Extensioned unknown path is not injected SPA HTML; non-GET extensionless still next()s.
  5. endpoints.test.ts: same-origin /graphql when window + http origin, no bootstrap.
  6. Watch reload that drops the preset: remount/clear or document startup-only.

Reviewer scoreboard

Perspective Signal
correctness-and-contracts HIGH — race + SPA base; prior gate closed
test-coverage-robustness HIGH — race + soft-skip + fallthrough/endpoints gaps
explorer-package HIGH — soft-skip + vite base
cli-and-runtime MED — watch stickiness; under-weighted race
dx-and-examples MED — skill/play gaps; prior gate closed

Perspectives: correctness, coverage, cli-runtime, explorer-package, dx. Models: grok-4.5 (HIGH), composer-2.5 (MED/LOW).

Open in Web View Automation 

Sent by Cursor Automation: Flatbread PR Review

Comment thread packages/flatbread/src/cli/openPath.test.ts Outdated
Comment thread packages/flatbread/src/graphql/explorerMount.test.ts Outdated
Comment thread packages/flatbread/src/graphql/liveServerEffortGraph.test.ts Outdated
export default defineConfig({
plugins: [react()],
root: '.',
base: './',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED (consensus)base: './' emits relative ./assets/…. HTML served for extensionless nested routes (e.g. /effort-graph/view via SPA fallback) resolves assets under the wrong prefix → 404.

Minimal fix: Use base: '/' for the Flatbread-mounted SPA, or absolutize asset URLs / inject <base href="/"> at mount time.

});
// Explorer SPA first so `/` is the visualizer when a preset matches. It must
// `next()` for `/events` and `/graphql` (see explorerMount).
const explorerMount = mountExplorerIfMatched(app, config.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED — Explorer mounts once at boot. --watch config reload replaces GraphQL config but does not remount/clear the SPA, so server.explorer can stick after the preset is removed.

Minimal fix: Remount (or clear) explorer on config reload, or document mount as startup-only.

Comment on lines +87 to +100

## 4. Open the explorer (optional)

With a complete `effortGraphContent()` preset in config, Flatbread serves the
content-relation explorer automatically:

```bash
flatbread start --watch --open
```

- Explorer UI: `http://localhost:5057/`
- Apollo GraphQL sandbox: `http://localhost:5057/graphql`

No separate app install is required; `@flatbread/explorer` ships with

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED — Skill implies explorer is always at http://localhost:5057/ after flatbread start --watch --open, omitting that missing dist/static soft-fails mount and --open falls back to /graphql.

Minimal fix: One sentence that built assets are required (pnpm play:efforts or explorer build) and that --open shares the mount assets gate. Mirror in the packaged skill under packages/effort-graph/skills/.

@tonyketcham
tonyketcham force-pushed the stack/tonyketcham/feat/explorer-package/ship-effort-graph-visualizer-flatbread-start--5b888060 branch from 4965f2f to 20bfa76 Compare July 28, 2026 09:42
@tonyketcham

tonyketcham commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 feat(explorer): ship Effort Graph visualizer with flatbread start #230 👈
2 fix(explorer): restore monorepo path aliases for typecheck #231
3 fix(explorer): harden mount gate, tests, and watch lifecycle #232

@tonyketcham

Copy link
Copy Markdown
Collaborator Author

Revision history

# Type Changes Reason Date
1 initial 4965f2f 2026-07-28 09:42 UTC
2 content 4965f2f → 20bfa76 2026-07-28 09:42 UTC

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Review verdict

REQUEST_CHANGES — CLI welcome/--open advertise explorer from preset match alone while mountExplorerIfMatched soft-fails when assets are missing, so public open-path UX drifts from the real Express mount (consensus HIGH).

Prior threads closed on this HEAD: shared dist/static/index.html rename race and soft-skip false greens (mount suite now fails closed). Still open: vite base:'./' vs deep SPA routes, --watch mount stickiness, skill/docs assets caveat.

Blocking / high

  1. Unify open-path with mountabilityresolveCliOpenPath / resolveOpenPath / welcome must require preset and dist/static/index.html (same predicate as mountExplorerIfMatched), else /graphql.
  2. Prove /graphql + /events coexistence — mount tests claim Apollo retention but never fetch those paths; add missing-assets negative without renaming shared assets.
  3. --watch explorer stickiness — mount is once-at-boot; remount/tear-down on config reload or document + test.

Coverage plan (priority)

  1. Missing assets → mount returns null (isolated temp dir).
  2. With mount: GET /graphql and GET /events not stolen as SPA HTML.
  3. Preset + missing assets → open path /graphql; welcome omits explorer lines.
  4. Watch/replaceConfig documents or reconciles explorer mount.
  5. staticDir.test.ts fail closed when index.html absent under root pnpm test.

Reviewer scoreboard

correctness / coverage / cli-runtime / explorer-package / dx — all HIGH signal on the open-path contract; DX uniquely flagged play:efforts without build guard.

Open in Web View Automation 

Sent by Cursor Automation: Flatbread PR Review

export function resolveCliOpenPath(
content: readonly ContentEntry[] | undefined
): string {
if (content && matchExplorerPreset(content)) return EXPLORER_ENDPOINT;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus)resolveCliOpenPath returns / on matchExplorerPreset only. That diverges from mountExplorerIfMatched, which soft-fails when dist/static/index.html is missing. Welcome/--open then claim explorer while Express never mounted it.

Minimal fix: Share one helper with the mount layer: return / only when preset matches and assets exist; else /graphql.

let explorer = false;
try {
const loaded = await loadFlatbreadConfig(process.cwd());
openPath = resolveCliOpenPath(loaded.config?.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — Welcome/--open set openPath/explorer from preset-only resolveCliOpenPath before/without knowing mount succeeded. Fresh clones that skip pnpm build hit the soft-fail path while CLI still opens / and prints explorer URLs.

Minimal fix: Derive both flags from the same mountability predicate as mountExplorerIfMatched (preset + assets).


if (details.internal) {
// Always show HTTP endpoint
if (explorer) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH — HTTP welcome prints explorer: whenever open-path resolved to /, even after soft-fail left Apollo owning /.

Minimal fix: Print explorer lines only when assets+preset would mount; otherwise GraphQL-only banner. Same for the HTTPS block below.

export function resolveOpenPath(
content: readonly ContentEntry[] | undefined
): string {
if (content && matchExplorerPreset(content)) return '/';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — JSDoc says “when mounted,” but this only calls matchExplorerPreset and ignores the assets gate above. It also duplicates resolveCliOpenPath, so the two can (and do) drift.

Minimal fix: Export canMountExplorer / reuse mountability; make both resolvers and welcome consume it (or fail hard for matched presets without assets).

async (t) => {
const staticDir = getExplorerStaticDir();
const indexPath = join(staticDir, 'index.html');
// Build must have produced assets; skip soft-fail would hide regressions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (coverage) — Suite claims Apollo retention / SPA mount but never hits /graphql or /events, never exercises extensionless SPA fallback, and never covers missing-assets → null. Prior rename-race is fixed (t.fail instead of hiding assets); coexistence is still unproven.

Minimal fix: Stack Apollo//events handlers after mount and assert they win; add an isolated-temp missing-assets negative (do not rename shared dist/static/index.html).

});
// Explorer SPA first so `/` is the visualizer when a preset matches. It must
// `next()` for `/events` and `/graphql` (see explorerMount).
const explorerMount = mountExplorerIfMatched(app, config.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — Explorer mounts once at boot. --watch replaceConfig never remounts/clears the SPA, so server.explorer can stick after the Effort Graph preset is removed (and stay false after it is added).

Minimal fix: Remount/tear-down on config reload, or document boot-sticky explorer and add a negative test that reload does not change mount.

Comment thread package.json
"typecheck": "pnpm --filter @flatbread/proof --filter @flatbread/explorer typecheck",
"play": "cd examples/nextjs && pnpm dev",
"play:efforts": "cd examples/effort-viz && pnpm dev",
"play:efforts": "pnpm exec flatbread start --watch --open",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (DX)play:efforts runs flatbread start --watch --open with no build precondition. Fresh pnpm install && pnpm play:efforts hits the assets soft-fail while CLI still claims/opens explorer. CONTRIBUTING already says “after pnpm build.”

Minimal fix: preplay:efforts (pnpm build or explorer build) or inline pnpm build && ….

export default defineConfig({
plugins: [react()],
root: '.',
base: './',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED (latent; still open)base: './' emits relative ./assets/…. HTML served for extensionless nested routes (SPA fallback in explorerMount) resolves assets under the wrong prefix. Contested for v1 (no client router yet; / is fine).

Minimal fix: Use base: '/' for Flatbread-served same-origin, or stop advertising deep-path fallback until client routing exists.


```bash
flatbread start --watch --open
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED (still open) — Skill implies explorer is always at http://localhost:5057/ after flatbread start --watch --open, omitting that missing dist/static soft-fails mount and --open can still open /. Keep .agents copy in sync.

Minimal fix: Add a short monorepo-from-source pnpm build / assets caveat (mirror CONTRIBUTING).

Explorer tsconfig overrode root paths with only @/*, so
@flatbread/effort-graph was invisible during CI typecheck
(before build). Re-declare the monorepo path map, raise lib to
ESNext, and set skipLibCheck so transitive source resolves cleanly.

Change-Id: Ic08896dac672cad66a12ee566014c568af870a7a

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict

REQUEST_CHANGES — consensus HIGH (≥2 perspectives) on open-path/assets mismatch, duplicate assets-unaware resolveOpenPath, sticky --watch explorer mount/flag, under-tested mount contracts, and play:efforts build gap.

Sync @ d1cff0a only restores monorepo path aliases. Prior HIGH items #1#5 remain STILL OPEN. Rename-race soft-skip and tsconfig alias fixes stay FIXED.

Blocking themes (priority)

  1. Preset ≠ mount readinessresolveCliOpenPath / welcome / --open key off matchExplorerPreset alone; mountExplorerIfMatched soft-fails without dist/static/index.html → banner can claim explorer while Apollo owns /.
  2. Duplicate open helpersresolveOpenPath JSDoc says “when mounted” but only checks preset; drifts from mount.
  3. Mount suite gaps — no isolated missing-assets→null, no /graphql+/events coexistence, no extensionless SPA fallback asserts.
  4. Sticky explorer under --watch — one-shot mount + frozen server.explorer; replaceConfig never remounts/clears.
  5. play:efforts / skills — no build gate; skill setup claims :5057/ unconditionally.

Coverage plan (HIGH first)

  1. explorerMount.test.ts — missing index.html in isolated temp → null, no / handler (no shared rename).
  2. explorerMount.test.ts — after mount, /graphql POST + /events SSE still win.
  3. explorerMount.test.ts — extensionless nested path serves SPA HTML.
  4. openPath.test.ts — preset+missing assets → /graphql; assets present → /.
  5. liveServer*.test.tsreplaceConfig clear/remount flips server.explorer; /events while explorer true; missing assets → explorer === false.

Consensus MED (still open)

  • vite base: './' breaks nested SPA fallback asset URLs.
  • Both effort-graph skill setup.md copies omit the assets/build caveat.

Reviewer scoreboard

Reviewer Verdict Signal
correctness-and-contracts REQUEST_CHANGES Strong
test-coverage-robustness REQUEST_CHANGES Strong
cli-and-runtime REQUEST_CHANGES Strong
explorer-package REQUEST_CHANGES Strong
dx-and-examples REQUEST_CHANGES Solid

Perspectives: correctness, coverage, cli-runtime, explorer-package, dx. Models: grok-4.5 (HIGH), composer-2.5 (MED/LOW).

Open in Web View Automation 

Sent by Cursor Automation: Flatbread PR Review

export function resolveCliOpenPath(
content: readonly ContentEntry[] | undefined
): string {
if (content && matchExplorerPreset(content)) return EXPLORER_ENDPOINT;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus)resolveCliOpenPath returns / on matchExplorerPreset only. That diverges from mountExplorerIfMatched, which soft-fails when dist/static/index.html is missing. Welcome/--open then claim explorer while Express never mounted it.

Minimal fix: Share one helper with the mount layer: return / only when preset matches and assets exist; else /graphql.

Comment on lines +74 to +78
let openPath = GRAPHQL_ENDPOINT;
let explorer = false;
try {
const loaded = await loadFlatbreadConfig(process.cwd());
openPath = resolveCliOpenPath(loaded.config?.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — Welcome/--open set openPath/explorer from preset-only resolveCliOpenPath before/without knowing mount succeeded. Fresh clones that skip pnpm build hit the soft-fail path while CLI still opens / and prints explorer URLs.

Minimal fix: Derive both flags from the same mountability predicate as mountExplorerIfMatched (preset + assets).

Comment on lines +154 to +160
if (explorer) {
console.log(
` ${colors.gray('explorer:')} http://${colors.bold(
`localhost:${port}${EXPLORER_ENDPOINT}`
)}`
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH — HTTP welcome prints explorer: whenever open-path resolved to /, even after soft-fail left Apollo owning /.

Minimal fix: Print explorer lines only when assets+preset would mount; otherwise GraphQL-only banner. Same for the HTTPS block below.

Comment on lines +101 to +107
/** Open path for `--open`: explorer root when mounted, else Apollo sandbox. */
export function resolveOpenPath(
content: readonly ContentEntry[] | undefined
): string {
if (content && matchExplorerPreset(content)) return '/';
return GRAPHQL_PATH;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — JSDoc says “when mounted,” but this only calls matchExplorerPreset and ignores the assets gate above. It also duplicates resolveCliOpenPath, so the two can (and do) drift.

Minimal fix: Export canMountExplorer / reuse mountability; make both resolvers and welcome consume it (or fail hard for matched presets without assets).

});

test.serial(
'mounts SPA at / and leaves /graphql for Apollo when assets exist',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (coverage) — Suite claims Apollo retention / SPA mount but never hits /graphql or /events, never exercises extensionless SPA fallback, and never covers missing-assets → null. Prior rename-race is fixed (t.fail instead of hiding assets); coexistence is still unproven.

Minimal fix: Stack Apollo//events handlers after mount and assert they win; add an isolated-temp missing-assets negative (do not rename shared dist/static/index.html).

Comment on lines +114 to +116
// Explorer SPA first so `/` is the visualizer when a preset matches. It must
// `next()` for `/events` and `/graphql` (see explorerMount).
const explorerMount = mountExplorerIfMatched(app, config.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus) — Explorer mounts once at boot. --watch replaceConfig never remounts/clears the SPA, so server.explorer can stick after the Effort Graph preset is removed (and stay false after it is added).

Minimal fix: Remount/tear-down on config reload, or document boot-sticky explorer and add a negative test that reload does not change mount.

port,
reloader,
effortGraph,
explorer: explorerMount !== null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (consensus)explorer: explorerMount !== null is freeze-dried at listen. Under --watch config swap this flag stays wrong even if routes were remounted later.

Minimal fix: Recompute/flip server.explorer when remounting on reload (or expose a live getter).

Comment thread package.json
"typecheck": "pnpm --filter @flatbread/proof --filter @flatbread/explorer typecheck",
"play": "cd examples/nextjs && pnpm dev",
"play:efforts": "cd examples/effort-viz && pnpm dev",
"play:efforts": "pnpm exec flatbread start --watch --open",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (DX)play:efforts runs flatbread start --watch --open with no build precondition. Fresh pnpm install && pnpm play:efforts hits the assets soft-fail while CLI still claims/opens explorer. CONTRIBUTING already says “after pnpm build.”

Minimal fix: preplay:efforts (pnpm build or explorer build) or inline pnpm build && ….

export default defineConfig({
plugins: [react()],
root: '.',
base: './',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MED (latent; still open)base: './' emits relative ./assets/…. HTML served for extensionless nested routes (SPA fallback in explorerMount) resolves assets under the wrong prefix. Contested for v1 (no client router yet; / is fine).

Minimal fix: Use base: '/' for Flatbread-served same-origin, or stop advertising deep-path fallback until client routing exists.

@cursor
cursor Bot enabled auto-merge July 28, 2026 10:27
auto-merge was automatically disabled July 28, 2026 10:28

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants