You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every element of the canonical resource slice exists in packages/fresh and is demonstrated once,
as a frozen init template for the single scaffolded example service — but no re-runnable verb
emits it. A user who adds a resource must hand-transcribe the example: typed route contract, definePage root, route-local (_components)/(_islands)/(_shared) layout, cache-first loader, QueryIsland, deferred partial. The measured consequence is that agents do not transcribe it: the
Wave-6 rickylabs/loom run received the registry, /design, AGENTS.md and WEB-LAYER.md and
still hand-rolled tables, direct service calls and a 676-line island (#1333 body). Generation, not
more prose, is the remaining lever.
Evidence
research/repo-audit/mcp-cli.md §4.2 — element-by-element table: defineRouteContract, withResource, cache-first loader, form slice, stream slice, State extension and router.ts/appRoutes registration are all framework ✅ / generated ❌.
research/repo-audit/web-layer.md §2.1 — verified in-worktree: withResource, withForm, withParams/withPathParams/withSearchParams, withRouteContract, withStreaming, definePartial have zero occurrences in packages/cli/src/kernel/assets/app/.
research/external/eis-chat.md §11 (S1–S12) and §12 — the twelve conventions a
product-grade NetScript app invents in app space because the scaffold does not emit them.
netscript ui:add page <path> --island writes three files, none of which loads data (#1357). netscript service add --with-client writes one app-side module (#1355). Nothing in between
exists: there is no verb that takes a contract procedure and emits a working screen. The reference
shape lives only in packages/cli/src/kernel/assets/app/routes/examples/service/ + (_shared)/service-showcase.ts.template + (_islands)/ServiceShowcaseLab.tsx.template + routes/partials/examples/service-summary.tsx.template, rendered once by init and never again.
Route ids in the one generator are hand-derived dotted strings
(web-scaffold.ts:20) with no link to apps/<app>/.generated/routes.ts, and the generated manifest
is only seeded by init and regenerated by the Vite plugin — there is no generate routes verb, so
a CI/agent flow that never runs Vite has a stale typed route surface
(packages/cli/src/kernel/application/scaffold/writers/app-route-seeds.ts; packages/fresh/src/application/route/manifest.ts:280-292).
Target contract
One composable generator, addressed at an app (#1356), that emits a complete, type-checking,
runnable resource slice and can be re-run:
Optional flags, composable and independently testable:--form (a withForm region with a
Zod schema whose messages are user-facing copy, csrf: true, redirectTo through the typed
route map, spanName, and a component reading firstFieldError), --partial (a deferred withLayer + a routes/partials/... partial whose name is derived, not a matching string
literal), --stream (a @netscript/fresh/streams consumer).
State: when a slice needs request-scoped state, the generator extends the app's utils.tsState type instead of leaving Record<string, never>.
Components: all markup composes app-owned Fresh-UI primitives from components/ui/mod.ts — the generator never emits raw Tailwind for something the registry ships.
Ownership comments: each emitted route-local directory carries a one-line header stating what
belongs in it ((_components) = markup only, (_islands) = hydration only, (_shared) =
loaders/types, (_lib) = route-local pure helpers), so the vocabulary survives the first edit.
Generator semantics match the rest of the CLI:--dry-run, --force, and a
content-compare written/skipped result like generate runtime-schemas
(packages/cli/src/public/features/generate/runtime-schemas/generate-runtime-schemas.ts:44-77)
— not the current hard refuse at web-scaffold.ts:60.
A single documented verb emits the core slice listed in Target contract §1 for a named
contract procedure.
The emitted slice type-checks and lints inside a freshly scaffolded project with no manual
edits.
The emitted page file contains no presentational JSX outside withLayout slots.
The emitted island binds the generated query factory's clientKey, not a string-literal key.
The emitted route registers through router.ts/appRoutes derived from the generated
manifest, not an inline createRouteReference.
--form, --partial and --stream each add exactly their own files and are independently
re-runnable.
The verb supports --dry-run and --force and reports written/skipped on re-run.
A second run with no input change writes zero files and exits 0.
Negative test: generation into a project whose contract lacks the named procedure fails
non-zero with the missing procedure named, and writes no files.
Negative test: a golden test fails if any emitted file contains any, a raw fetch( call,
a hand-written query-key array literal, or manual JSON.parse of a service response.
Not in scope: the missing generate routes verb (mcp-cli.md §4.5 C2) beyond whatever the slice
registration needs; a service-side command slice (T3).
Docs/consumer proof
Adoption is proved by a generated project, not by prose: the scaffold.runtime E2E generates a
slice and type-checks it; the app AGENTS.md/WEB-LAYER.md reference the verb as the first step of
the one-screen path; and a consumer-shaped check re-derives the eis-chat conventions S1–S4/S7 from
generated output rather than from docs/design/BUILD-CONTRACT.md in a downstream repo
(research/external/eis-chat.md §11).
Provenance
Seed run plan-fable5-remediation-roadmap--seed, PR #1347, 2026-08-08. Drafted from research/repo-audit/mcp-cli.md §4.2/§5, research/repo-audit/web-layer.md §2.1, research/external/eis-chat.md §11–12; all repo claims re-verified against worktree fac9e339042c. No GitHub mutation performed.
Filed from planning seed PR #1347 · source Draft-ID T2-01 · live issue #1354.
Summary
Every element of the canonical resource slice exists in
packages/freshand is demonstrated once,as a frozen
inittemplate for the single scaffolded example service — but no re-runnable verbemits it. A user who adds a resource must hand-transcribe the example: typed route contract,
definePageroot, route-local(_components)/(_islands)/(_shared)layout, cache-first loader,QueryIsland, deferred partial. The measured consequence is that agents do not transcribe it: theWave-6
rickylabs/loomrun received the registry,/design,AGENTS.mdandWEB-LAYER.mdandstill hand-rolled tables, direct service calls and a 676-line island (#1333 body). Generation, not
more prose, is the remaining lever.
Evidence
research/repo-audit/mcp-cli.md§4.2 — element-by-element table:defineRouteContract,withResource, cache-first loader, form slice, stream slice,Stateextension androuter.ts/appRoutesregistration are all framework ✅ / generated ❌.research/repo-audit/web-layer.md§2.1 — verified in-worktree:withResource,withForm,withParams/withPathParams/withSearchParams,withRouteContract,withStreaming,definePartialhave zero occurrences inpackages/cli/src/kernel/assets/app/.research/external/eis-chat.md§11 (S1–S12) and §12 — the twelve conventions aproduct-grade NetScript app invents in app space because the scaffold does not emit them.
fac9e339042c: framework surface present —packages/fresh/src/application/route/mod.ts:69(bindRoutePattern),:94(
defineRouteContract); sidecar conventionpackages/fresh/src/application/route/manifest.ts:44(
isRouteContractSidecar),:53(isRouteHelperDirectoryName);packages/fresh/src/application/builders/define-page/builder/state.ts:55(withResource).packages/cli/src/kernel/application/ui/web-scaffold.ts:15-42is theentire page generator (see fix(cli): ui:add page --island emits a useSignal counter and an empty queryLoaders object instead of the advertised data-screen triad #1357);
packages/cli/src/public/features/generate/generate-group.tsregisters only
aspire,runtime-schemas,plugins.convention generation), docs(tutorials): no tutorial demonstrates the page builder — every tutorial underleverages NetScript, and the MCP-served docs will teach the wrong patterns #1208/docs(web-layer): differentiator deep-dives + competitive tutorial benchmark — per-API sub-pages for the page builder (withResource, Partials, …) #1210 (docs for these APIs).
Current surface
netscript ui:add page <path> --islandwrites three files, none of which loads data (#1357).netscript service add --with-clientwrites one app-side module (#1355). Nothing in betweenexists: there is no verb that takes a contract procedure and emits a working screen. The reference
shape lives only in
packages/cli/src/kernel/assets/app/routes/examples/service/+(_shared)/service-showcase.ts.template+(_islands)/ServiceShowcaseLab.tsx.template+routes/partials/examples/service-summary.tsx.template, rendered once byinitand never again.Route ids in the one generator are hand-derived dotted strings
(
web-scaffold.ts:20) with no link toapps/<app>/.generated/routes.ts, and the generated manifestis only seeded by
initand regenerated by the Vite plugin — there is nogenerate routesverb, soa CI/agent flow that never runs Vite has a stale typed route surface
(
packages/cli/src/kernel/application/scaffold/writers/app-route-seeds.ts;packages/fresh/src/application/route/manifest.ts:280-292).Target contract
One composable generator, addressed at an app (#1356), that emits a complete, type-checking,
runnable resource slice and can be re-run:
<route>.route.tssidecar declaring the route contract viadefineRouteContract(path/search schemas typed, notRecord<string, unknown>);index.tsxcontaining a
definePage()root with zero presentational JSX —withRouteContract/withRoute,withResourcefor the shared read,withLayerper region,withLayoutslots,withMeta; route-local(_components)/<resource>-view.tsxholding all markup;(_shared)/<resource>-loaders.tsholding the contract-derived cache-first loader(
createNetScriptQueryClient→fetchQuery→dehydrateQueryClient→cachedAt);(_islands)/<Resource>Island.tsxbindingQueryIsland+useIslandQueryto the generatedquery factory from feat(cli): app-side client/query wiring is a one-shot template with hardcoded names, colliding 'service' cache keys and a no-op invalidation #1355 (
clientKey, not a string literal) and passinginitialDataUpdatedAt(fix(scaffold): the canonical island never passes initialDataUpdatedAt, so the loader's cachedAt is computed, displayed and discarded #1360); registration inrouter.ts/appRoutesderived from thegenerated manifest, not an inline
createRouteReference.--form(awithFormregion with aZod schema whose messages are user-facing copy,
csrf: true,redirectTothrough the typedroute map,
spanName, and a component readingfirstFieldError),--partial(a deferredwithLayer+ aroutes/partials/...partial whose name is derived, not a matching stringliteral),
--stream(a@netscript/fresh/streamsconsumer).utils.tsStatetype instead of leavingRecord<string, never>.components/ui/mod.ts— the generator never emits raw Tailwind for something the registry ships.belongs in it (
(_components)= markup only,(_islands)= hydration only,(_shared)=loaders/types,
(_lib)= route-local pure helpers), so the vocabulary survives the first edit.--dry-run,--force, and acontent-compare
written/skippedresult likegenerate runtime-schemas(
packages/cli/src/public/features/generate/runtime-schemas/generate-runtime-schemas.ts:44-77)— not the current hard refuse at
web-scaffold.ts:60.surfaces, the generator includes them. This clause and only this clause depends on Epic: Typed SDK client contributions — credentials, transport policy, metadata, and cache-safe extensions #1348;
the core generator ships without it and must not be sequenced behind the RFC.
Acceptance
contract procedure.
edits.
withLayoutslots.clientKey, not a string-literal key.router.ts/appRoutesderived from the generatedmanifest, not an inline
createRouteReference.--form,--partialand--streameach add exactly their own files and are independentlyre-runnable.
--dry-runand--forceand reportswritten/skippedon re-run.non-zero with the missing procedure named, and writes no files.
any, a rawfetch(call,a hand-written query-key array literal, or manual
JSON.parseof a service response.rather than write outside
apps/<app>/.deno task e2e:cli run scaffold.runtime --cleanupcovers generating a slice andtype-checking the app that contains it.
Boundaries
verb. Do not restate fix(scaffold/frontend): make the default app an idiomatic eis-chat-grade reference and derive its name from the project #1333's ten boxes here, and do not close fix(scaffold/frontend): make the default app an idiomatic eis-chat-grade reference and derive its name from the project #1333 with this PR.
the consumer side of that seam, gated on Epic: Typed SDK client contributions — credentials, transport policy, metadata, and cache-safe extensions #1348.
emit its own client wiring.
ui:add pageemission; if the owner prefers one command, fix(cli): ui:add page --island emits a useSignal counter and an empty queryLoaders object instead of the advertised data-screen triad #1357becomes this generator's
--minimalmode — that consolidation decision is stated, not assumed.withResource/withForm/partials; this issue ships examples,not the manual.
generate routesverb (mcp-cli.md §4.5 C2) beyond whatever the sliceregistration needs; a service-side command slice (T3).
Docs/consumer proof
Adoption is proved by a generated project, not by prose: the
scaffold.runtimeE2E generates aslice and type-checks it; the app
AGENTS.md/WEB-LAYER.mdreference the verb as the first step ofthe one-screen path; and a consumer-shaped check re-derives the eis-chat conventions S1–S4/S7 from
generated output rather than from
docs/design/BUILD-CONTRACT.mdin a downstream repo(
research/external/eis-chat.md§11).Provenance
Seed run
plan-fable5-remediation-roadmap--seed, PR #1347, 2026-08-08. Drafted fromresearch/repo-audit/mcp-cli.md§4.2/§5,research/repo-audit/web-layer.md§2.1,research/external/eis-chat.md§11–12; all repo claims re-verified against worktreefac9e339042c. No GitHub mutation performed.Filed from planning seed PR #1347 · source Draft-ID T2-01 · live issue #1354.