Skip to content

chore(templates): retire the unreachable feature system - #3939

Merged
kojiwakayama merged 3 commits into
mainfrom
chore/remove-dead-feature-scaffolds
Aug 21, 2026
Merged

kojiwakayama merged 3 commits into
mainfrom
chore/remove-dead-feature-scaffolds

Conversation

@kojiwakayama

@kojiwakayama kojiwakayama commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3797. Closes #3800.

Both issues asked for a decision, not a half-state. This takes the delete branch of each, and the two turn out to be the same decision.

The feature system was dead in two independent ways

Nothing could request a feature. --with/-w was registered as an array flag in cli/shared/args.ts and read by no code. handleInitCommand never touched args.with; InitOptions.features was populated by no non-test caller. Every remaining caller passed features: [] literally — the type checker surfaced three more (cli/app/operations/project-creation.ts, cli/commands/demo/demo.ts, cli/mcp/tools/catalog-tools.ts) the moment the field was removed. Meanwhile templates/integrations/_base/files/SETUP.md shipped veryfront init my-app --with ai … into every integration scaffold, so the one place a user was told to use the flag was advertising something inert.

Nothing could load a feature's files. All six features shipped a files/ tree — 29 files — never scaffolded. feature-loader.ts read them via loadTemplateFromDirectory with an absolute path, but that function resolves its argument as a key into the compressed manifest, and the generator emits no feature keys. Templates migrated to the manifest; features never moved with them.

Why delete rather than finish

The files are not merely unwired — five of six are wrong (per the per-feature assessment in #3797):

feature the deciding fact
ai overwrites agents/assistant.ts on ai-agent, swapping the calculator assistant for a weather one while the shipped evals/assistant.eval.ts still gates on calledTool("calculator")
auth overwrites app/dashboard/page.tsx — saas-starter's product surface
redis is not Redis. lib/redis.ts has no client and no network I/O; its tips tell the user to start a server it never contacts
workflows references six tool ids defined nowhere
blob superseded by veryfront/workflow/blob
mdx clean, but the only salvageable one

And what survived was worth less than the plumbing carrying it. Verified on this branch's base: no feature declares dependencies, configMerge is read by nothing, and with files/ gone the entire system contributed tips plus six envVars — for features no user could select. Wiring --with to reach that would also have newly exposed redis's and blob's tips, which are false.

So the plumbing goes with the files: feature-loader.ts, FeatureName/FeatureConfig/ResolvedFeature, --with/-w, InitOptions.features, and the #3786 detector that existed only to report this condition.

⚠️ Breaking change: MaterializeScaffoldRequest.features

veryfront/scaffold is a public export, so removing the optional features field from MaterializeScaffoldRequest is a deliberate breaking change. Raised in review; recording it rather than leaving it implicit.

Who is affected: JSR/Deno consumers of materializeScaffold only. npm consumers were never affected — build-npm-dnt.ts:306 embeds templates through the compressed manifest and copies no feature.json, so loadFeatureConfig read a path absent from the package. JSR shipped it because the top-level exclude lists templates/files/ and templates/integrations/ but not templates/features/.

What they lose, probed through the public entrypoint on the base commit rather than inferred from types:

features=[]         files=9  tips=0  JWT_SECRET=false REDIS_URL=false
features=["auth"]   files=11 tips=5  JWT_SECRET=true  REDIS_URL=false
features=["redis"]  files=11 tips=4  JWT_SECRET=false REDIS_URL=true
features=["mdx"]    files=9  tips=4  JWT_SECRET=false REDIS_URL=false

envVars, the .env/.env.example pair those envVars cause to be emitted (that is the +2), and tips. No feature files/ were ever scaffolded — consistent with the manifest-key analysis above.

Why it is not preserved behind a deprecated field. What survived is actively misleading, not merely inert. The auth tips it emitted:

Default demo credentials: demo@example.com / password
Auth routes: /login, /signup, /dashboard
API routes: /api/auth/login, /api/auth/signup, /api/auth/logout, /api/auth/me

None of those routes are scaffolded, and it wrote JWT_SECRET into .env.example for auth code that does not exist. redis is the same shape. A deprecated field would keep telling consumers their project has a login page it does not have — strictly worse than the silent no-op #3800 was filed about, and both issues close on "not a half-state".

AGENTS.md permits this: "Preserve public API compatibility unless the task explicitly asks for a breaking change." #3797's acceptance is "the directories deleted and the detector removed"; #3800's is "InitOptions.features is removed".

Migration: drop the field. It has no replacement because it had no working behaviour.

Kept, because they outlive features

  • mergeFiles moves to templates/loader.ts. Integrations and the generated AGENTS.md/.env.example overlay depend on it.
  • withMdxExtension stays, now purely file-driven. The mdx feature was the path that motivated it (fix(npm): make ext-content-mdx an optional peer, not a runtime dependency #3783 review), but the rule outlives that path: a template that ships .mdx gets @veryfront/ext-content-mdx declared whether or not its config remembers to. Its test is retargeted at minimal, the case that exists today, with a guard asserting the template still ships an .mdx file so the test can't go vacuous silently.

Incidental cleanups the deletion forced

  • featureTips → setupTips, which is what it now carries (integration tips only).
  • validateOrThrow's kind/callback indirection collapses into validateIntegrationsOrThrow — it existed to serve two kinds, and one is gone.
  • The tips test now selects an integration instead of a feature, keeping the tips pipeline covered rather than deleting the coverage with the feature.
  • generated-artifact-checks.test.ts no longer creates a templates/features fixture dir the generator no longer reads.
  • deno.json's test:scripts no longer lists the deleted feature-files-detection.test.ts. That task enumerates its files by hand, so the stale path made deno task test:scripts (and verify) fail with a missing-module error before any script test ran. Caught in review.

Verification

All with pinned Deno 2.7.7 (.tool-versions):

  • deno test templates/ cli/shared/ cli/commands/init/ — 69 passed, 0 failed (652 steps), including the scaffold-quality, scaffold-parity and init integration suites.
  • deno task test:scripts — 142 passed (470 steps), 1 failed. That one (npm supply-chain policy … DNT Deno shim) reproduces identically on an untouched baseline worktree, so it is pre-existing.
  • deno task fmt:check, deno lint (5068 files), deno check src/index.ts cli/main.ts — all clean.
  • generate-templates-manifest.ts --check — current. The manifest diff is exactly one line: the corrected SETUP.md command.
  • deno test cli/ tests/docs/guide-content.test.ts — 329 passed, 5 step failures. None are regressions: 3 (deploy, schedule, up) reproduce identically on an unmodified worktree and are network-dependent; the other 2 (demo --auto help, initializeGitRepo) pass in isolation on this branch and are parallel-run cwd interference. The demo.ts diff is a single removed line inside a createProject call and cannot affect --help output.

Deliberately not included

templates/features/ai/files/prompts/assistant.ts is deleted with the rest. #3797 flagged it as the only prompts/ + promptRegistry example anywhere in templates/, worth relocating to templates/files/ai-agent/prompts/. That is a separate, additive change — recovering it from history is a one-liner if wanted, and it should land where the scaffold-quality gate covers it rather than riding along with a deletion.

The feature system was live in the type system and dead everywhere else,
in two independent ways.

**Nothing could request a feature.** `--with`/`-w` was registered as an
array flag in `cli/shared/args.ts` and read by no code:
`handleInitCommand` never touched `args.with`, and `InitOptions.features`
was populated by no non-test caller. Every remaining caller passed
`features: []` literally -- the type checker found three more once the
field was removed. `templates/integrations/_base/files/SETUP.md` shipped
`veryfront init my-app --with ai …` into every integration scaffold, so
the one place a user was told to use the flag was advertising something
inert.

**Nothing could load a feature's files.** All six features shipped a
`files/` tree -- 29 files -- that has never been scaffolded.
`feature-loader.ts` read them through `loadTemplateFromDirectory` with an
absolute path, but that function resolves its argument as a key into the
compressed manifest, and the generator emits no feature keys. Templates
moved to the manifest; features did not move with them.

Deleting rather than finishing, because the files are not merely
unwired:

- `ai` overwrites `agents/assistant.ts` on the ai-agent template, swapping
  the calculator assistant for a weather one while the shipped
  `evals/assistant.eval.ts` still gates on `calledTool("calculator")`.
- `auth` overwrites `app/dashboard/page.tsx`, which is saas-starter's
  product surface.
- `redis` is not Redis: `lib/redis.ts` has no client and no network I/O,
  and its tips instruct the user to start a server it never contacts.
- `workflows` references six tool ids that are defined nowhere.
- `blob` is superseded by `veryfront/workflow/blob`.

What survived was worth less than the plumbing that carried it. No
feature declared dependencies, `configMerge` was read by nothing, and
with `files/` gone the whole system contributed tips plus six envVars --
for features no user could select. So the plumbing goes with them:
`feature-loader.ts`, `FeatureName`/`FeatureConfig`/`ResolvedFeature`, the
`--with` flag, `InitOptions.features`, and the #3786 detector that
existed only to report this condition.

Kept, because they are load-bearing beyond features:

- `mergeFiles` moves to `templates/loader.ts` -- integrations and the
  generated AGENTS.md/.env.example overlay use it.
- `withMdxExtension` stays and is now purely file-driven. The `mdx`
  feature was the path that motivated it, but the rule it enforces
  outlives that path: a template that ships `.mdx` gets the extension
  declared whether or not its config remembers to say so. Its test is
  retargeted at `minimal`, which is that case today.

`featureTips` is renamed `setupTips`, which is what it now carries.
`validateOrThrow`'s kind/callback indirection collapses into
`validateIntegrationsOrThrow` now that only one kind remains.

Closes #3797
Closes #3800
@coderabbitai

coderabbitai Bot commented Aug 21, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@kojiwakayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aec9fe2c-7be3-4ac0-94be-428e92f59459

📥 Commits

Reviewing files that changed from the base of the PR and between 3837403 and 7b25269.

⛔ Files ignored due to path filters (1)
  • templates/manifest.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (61)
  • cli/app/operations/project-creation.ts
  • cli/commands/demo/demo.ts
  • cli/commands/init/init-command.test.ts
  • cli/commands/init/init-command.ts
  • cli/commands/init/types.ts
  • cli/mcp/tools/catalog-tools.ts
  • cli/shared/args.test.ts
  • cli/shared/args.ts
  • cli/shared/project-creation.test.ts
  • cli/shared/project-creation.ts
  • deno.json
  • docs/api-reference/veryfront/scaffold.md
  • scripts/build/feature-files-detection.test.ts
  • scripts/build/generate-templates-manifest.ts
  • scripts/build/generated-artifact-checks.test.ts
  • templates/feature-loader.ts
  • templates/features/ai/feature.json
  • templates/features/ai/files/agents/assistant.ts
  • templates/features/ai/files/app/api/ag-ui/route.ts
  • templates/features/ai/files/app/chat/page.tsx
  • templates/features/ai/files/prompts/assistant.ts
  • templates/features/ai/files/tools/get-weather.ts
  • templates/features/auth/feature.json
  • templates/features/auth/files/app/api/auth/login/route.ts
  • templates/features/auth/files/app/api/auth/logout/route.ts
  • templates/features/auth/files/app/api/auth/me/route.ts
  • templates/features/auth/files/app/api/auth/signup/route.ts
  • templates/features/auth/files/app/dashboard/page.tsx
  • templates/features/auth/files/app/login/page.tsx
  • templates/features/auth/files/app/signup/page.tsx
  • templates/features/auth/files/lib/auth.ts
  • templates/features/auth/files/lib/db.ts
  • templates/features/auth/files/lib/jwt.ts
  • templates/features/auth/jwt.test.ts
  • templates/features/blob/feature.json
  • templates/features/blob/files/app/api/upload/[id]/route.ts
  • templates/features/blob/files/app/api/upload/route.ts
  • templates/features/blob/files/app/upload/page.tsx
  • templates/features/blob/files/lib/storage.ts
  • templates/features/mdx/feature.json
  • templates/features/mdx/files/app/docs/core-concepts/page.mdx
  • templates/features/mdx/files/app/docs/getting-started/page.mdx
  • templates/features/mdx/files/app/docs/page.mdx
  • templates/features/redis/feature.json
  • templates/features/redis/files/app/api/tasks/[id]/route.ts
  • templates/features/redis/files/app/api/tasks/route.ts
  • templates/features/redis/files/lib/redis.ts
  • templates/features/workflows/feature.json
  • templates/features/workflows/files/app/api/workflows/start/route.ts
  • templates/features/workflows/files/app/api/workflows/status/route.ts
  • templates/features/workflows/files/workflows/data-processing.ts
  • templates/features/workflows/files/workflows/index.ts
  • templates/index.test.ts
  • templates/index.ts
  • templates/integrations/_base/files/SETUP.md
  • templates/loader.ts
  • templates/manifest.json
  • templates/scaffold-parity.test.ts
  • templates/scaffold-quality.test.ts
  • templates/types.ts
  • tests/docs/guide-content.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

📦 Client bundle boundary

Entrypoint Modules Source size Server leaks
src/index.client.ts 326 1949 KiB ✅ 0

A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in scripts/lint/client-bundle-baseline.json to burn down.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7dec789267

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build/feature-files-detection.test.ts
Comment thread cli/shared/project-creation.ts
Removing the feature plumbing from cli/shared/project-creation.ts shifts
the source line numbers the generated reference links to. Regenerated
with the pinned toolchain (deno 2.7.7, per .tool-versions) rather than
hand-edited, so the pins match what CI produces.
`test:scripts` enumerates its test files by hand, so deleting
feature-files-detection.test.ts without editing the task left
`deno task test:scripts` (and `verify`, which calls it) failing with a
missing-module error before any script test ran.

Caught in review on #3939.
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Both review findings addressed at 7b25269b9; threads replied to and resolved.

P1 (test:scripts) — real miss, fixed. test:scripts enumerates its files by hand, so the deleted detector test made deno task test:scripts (and verify) fail with a missing-module error before any script test ran. Verified after the fix with pinned Deno 2.7.7: 142 passed / 470 steps, 1 failure that reproduces identically on an untouched baseline. Also swept deno.json, .github/, scripts/ and .husky/ for the other deleted paths — this was the only stale reference.

P2 (MaterializeScaffoldRequest.features) — keeping the removal, and the PR body now carries an explicit Breaking change section rather than leaving it implicit. Worth noting the finding understated it: probing the public entrypoint on the base commit shows it was a behavioural break for JSR consumers, not only a type break — envVars, the .env/.env.example pair, and tips. It is not preserved behind a deprecated field because what survived is actively misleading: the auth tips advertise /login, /signup, /dashboard and demo credentials for routes that were never scaffolded, and write JWT_SECRET for auth code that does not exist. Full evidence in the PR body.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 7b25269b90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Blast-radius check on the breaking change, before merging.

I swept the platform monorepo for consumers of the public veryfront/scaffold subpath. There is exactly one real one — veryfront-api — and it does not use the removed field:

veryfront-api/src/usecases/projects/scaffold-files.ts:110
  const { files } = await materializeScaffold({ template: cliTemplateName, projectName })

grep features across scaffold-files.ts and scaffold-template.ts returns nothing, and its test at scaffold-files.test.ts:46 calls the same two-field shape. So the removal is source-compatible with the only in-org consumer.

veryfront-api also consumes this from npm, where features never worked at all (build-npm-dnt.ts:306 ships templates through the compressed manifest and copies no feature.json), so there is no behavioural change for it either.

Net: the break is real for a hypothetical external JSR consumer that passed features, documented in the PR body with a one-line migration, and inert for every consumer that actually exists.

CI is green across the board on 7b25269b9, both review threads are resolved, and the Codex re-review at that SHA found no major issues. Enqueuing.

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 21, 2026
Merged via the queue into main with commit 3d66c6b Aug 21, 2026
36 checks passed
@kojiwakayama
kojiwakayama deleted the chore/remove-dead-feature-scaffolds branch August 21, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant