本文へ移動
cccskills
無料GitHub で公開

crouton-architecture-contract

The crouton mental model for someone who has never seen this repo — what "layer" means (two different things), the two-halves rule, the three app.config registries, team-scoped multi-tenancy, manifest-driven field types, the numbered invariants with the incidents behind them, and the known-weak points. Use when orienting in this codebase for the first time, when a question is shaped like "how does crouton work / why is it built this way / where does X live", when deciding whether a change belongs in a package or a generated layer, when confused by an extends chain or by croutonApps/croutonCollections/croutonLayoutBlocks, or before proposing any structural change. Trigger phrases: "what is a crouton layer", "package vs generated layer", "how does multi-tenancy work here", "why is there a stub component", "where do field types come from", "what invariants must hold".

インストール方法を見る

含まれるファイル(1)

  • SKILL.md15.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Crouton Architecture Contract

The load-bearing mental model of crouton: definitions, design decisions with their WHY, the invariants that must hold, and the honestly-stated weak points. This is theory + contract; the how-to lives in sibling skills.

When to use / when NOT to use

You wantGo to
The mental model, invariants, "why is it like this"this skill
Generate a collection (workflow, MCP tools, gates)crouton skill (.claude/skills/crouton.md)
Schema JSON format, field-type tables, artifact listsibling crouton-generation-reference
Layout engine theory (LayoutTree, viability, placer)sibling crouton-layout-reference; authoring a block → block-authoring skill
Symptom → root cause lookupsibling crouton-diagnostics-index
Cold-start env setup, dist/install trapssibling crouton-build-and-env
Which docs are stale vs canonical (full list)sibling crouton-docs-trust-map
Which gate fires on a changesibling crouton-change-control

Trust order when sources conflict: see sibling crouton-docs-trust-map §1 (it owns the ranked chain; code beats prose for runtime behaviour).

Glossary (repo jargon, first-contact)

TermMeaning here
croutonThree referents: (1) the whole framework; (2) the CLI (packages/crouton-cli, bin crouton) that generates collections; (3) @fyit/crouton, the umbrella Nuxt module
collectionThe unit crouton generates: one entity/table, defined by a schema JSON, generated into a self-contained layer dir. Always team-scoped
layerA Nuxt Layer (a Nuxt app consumed via extends). Two meanings in this repo — see §1
fieldsFileThe per-collection field-definition JSON (schemas/<name>.json) referenced by crouton.config.js
teamThe multi-tenancy unit = a better-auth organization. Every generated API lives under /api/teams/[id]/…
fixtureA throwaway generated app in fixtures/ booted by the Playwright harness in e2e/ to smoke boot/auth/CRUD (see fixtures/CLAUDE.md)
POCA real crouton app in pocs/ incubating a future package; safe-to-fail, no required gates
graduationThe deliberate spec-driven rebuild of a proven POC into real package(s) + app (/graduate skill, epics #916/#992)
gateA hold point where a human signs off before the expensive step (Schema #314 / UI #307 / Test #774 / code review). Resume signal is an lgtm/approve comment only (#572)
stageWhere code lives in the lifecycle, declared in harness.config.mjs (poc/app/package/unstaged). Resolve with node scripts/harness-stages.mjs <path>
manifestA package's crouton.manifest.ts — declares its field types, bundled-ness, reserved names, contributions (§5)

1. "Layer" means two different things

Newcomer trap #1. Both are Nuxt Layers technically, but they play different roles:

  1. Package layer — packages/ holds the crouton-* packages plus the crouton meta package (count: re-verify block). Most are publishable Nuxt Layers consumed as source: packages/crouton-core/package.json has "main": "./nuxt.config.ts" (no build step for the layer itself; dist/ exists only for specific subpath exports — install traps: sibling crouton-build-and-env). Exceptions (check each package.json): crouton and crouton-devtools are Nuxt modules ("main": "./dist/module.mjs"), crouton-cli is the bin CLI, crouton-mcp is the MCP server, crouton-themes has no main at all (subpath-export theme layers only).
  2. Generated per-collection layer — inside a consuming app, layers/<layer>/collections/<collection>/ is itself a nested Nuxt layer with its own nuxt.config.ts. Real tree (fixtures/minimal/layers/main/collections/items/):
layers/main/collections/items/
├── app/components/{_Form.vue, List.vue}
├── app/composables/useMainItems.ts
├── server/api/teams/[id]/main-items/{index.get,index.post,[itemId].patch,[itemId].delete}.ts
├── server/database/{schema.ts, queries.ts}
├── types.ts  nuxt.config.ts  README.md

(Newer generations add tests, seed.json, and app-root aggregates — the full artifact list is crouton-generation-reference's job.)

The extends chain, in a real launched app (apps/velo/nuxt.config.ts):

modules: ['@fyit/crouton'],
extends: [
  '@fyit/crouton-core', '@fyit/crouton-layout', '@fyit/crouton-i18n',
  '@fyit/crouton-assets', '@fyit/crouton-pages', '@fyit/crouton-bookings',
  '@fyit/crouton-email',
  './layers/bookings', './layers/pages',       // ← generated layers
  '@fyit/crouton-maps', './layers/crouton'
]

And @fyit/crouton-core itself extends ['@fyit/crouton-i18n', '@fyit/crouton-auth', '@fyit/crouton-admin'] (its nuxt.config.ts; "Order matters: i18n provides translation system that auth/admin consume") — so auth/admin/i18n arrive through core ("bundled"). Velo lists crouton-i18n explicitly anyway; see invariant 5 for why that pattern is dangerous with auth/admin.

The @fyit/crouton meta-package is a Nuxt MODULE, not a layer ("main": "./dist/module.mjs"), consumed via modules: ['@fyit/crouton'] across apps/fixtures/pocs (count: re-verify block). At runtime it cannot add layers (comment in packages/crouton/src/module.ts: layers must be in extends before modules load) — it only wires runtimeConfig and warns. Its exported getCroutonLayers() extends-builder has zero in-repo callers; apps hand-list extends, kept in sync by the CLI's syncFrameworkPackages. Treat getCroutonLayers() as aspirational (§7).

2. The two-halves rule (pointer)

Canonical: packages/crouton/CLAUDE.md § "Packages vs Generated Layers (IMPORTANT)". In one line: a crouton feature = a package (UI, admin pages, nav, composables, i18n — via extends) + a generated layer (Drizzle schema, API endpoints, configs, types — via the CLI), and they need each other: package without layer → no tables/endpoints (404/500); layer without package → a generic "Collections" sidebar with no dedicated UI. When something "is installed but doesn't work", check which half is missing first.

3. The three registries (all app.config, defu-merged across layers)

RegistryWho writes itWho reads it
croutonCollectionsThe generator upserts one entry per collection into the app's app/app.config.tsCore's shared CRUD components (CroutonCollection, forms, lists) resolve a collection by registry key
croutonAppsEach addon package self-registers in its own app/app.config.ts (e.g. crouton-bookings)Sidebar/nav via useCroutonApps(), and presence detection via hasApp(id)
croutonLayoutBlocksEach package contributes placeable layout blocks (defaults in crouton-layout; bookings adds calendar blocks)The layout engine — sibling crouton-layout-reference

Stub-priority pattern — mechanics + code are owned by root CLAUDE.md § "Optional Cross-Package Components (Stub Pattern)". The WHY this skill adds: optional packages (editor, maps, assets, collab) may or may not be in the extends chain, and Vue has no clean "is this component available?" check (resolveComponent() warns unconditionally when absent; vueApp._context.components is private API). So core ships no-op stubs (packages/crouton-core/app/components/stubs/) at priority: -1; the real addon component at default priority silently wins when present — preventing warning spam + broken renders in apps without the addon. Behavior branching uses hasApp('assets'). Addons registering routes use a parentApp key (type in crouton-core/app/types/app.ts) so defu array-merging doesn't collide nav entries.

4. Team-scoped multi-tenancy

better-auth with the organization plugin; team = organization (canonical package: packages/crouton-auth). The resolver is resolveTeamAndCheckMembership(event) in packages/crouton-auth/server/utils/team.ts — mode-aware per its JSDoc (multi-tenant / single-tenant / personal), returns { team, user, membership }, throws 401/403/404. Every generated endpoint lives at /api/teams/[id]/<layer>-<plural>/ and starts:

import { resolveTeamAndCheckMembership } from '@fyit/crouton-auth/server/utils/team'
const { team } = await resolveTeamAndCheckMembership(event)
// every query takes team.id

Client side: useTeamContext() (packages/crouton-core/app/composables/useTeamContext.ts) reads the [team] route param. There is no opt-out — the generator hardcodes the auth import and passes team.id to every query (invariant 3). Core also wires a #crouton/team-auth nitro alias as a compat path; generated code uses the direct import.

5. Manifest-driven field types

Field types are not hardcoded in the CLI. Each package declares a crouton.manifest.ts (defineCroutonManifest from @fyit/crouton-core/shared/manifest); the CLI's manifest loader merges them into the type mapping. Core's manifest declares the base types (string, text, number, … — exact list in the file) with db/drizzle/zod/tsType/defaultValue/component, plus aliases (integer→number, datetime→date) that resolve to canonical before generators run — the #285 incident is why (datetime once leaked through: raw <UInput> + a text column). The manifest also owns autoGeneratedFields (id, teamId, owner, createdAt, … — never declare these), reserved field/collection names, bundled (read by the meta module), and detects patterns for addon generator contributions. Full tables: sibling crouton-generation-reference. Adding a field type = a manifest change, not a CLI patch.

6. Invariants (compressed to pointers — read the canonical source, don't re-derive)

#InvariantCanonical sourceIncident / why
1Dep direction crouton-layout → crouton-core, never reverse (shared layout types live in core so feature packages contribute blocks dep-free)packages/crouton-layout/CLAUDE.md ("HARD RULE")reverse = cycle
2Optional components via stub-priority, never resolveComponent()root CLAUDE.md § Critical Gotchaswhy: §3
3Every collection team-scoped, no opt-out (§4)the generated handlers themselvestenancy leaks; API tests go red if teamId dropped (#791)
4packages/ is edit-guarded (HARD GATE)root CLAUDE.md § Packages Boundary; hook gate-package-edits.sh; flow → crouton-change-controlevery consumer inherits the change
5Never re-extend bundled auth/admin alongside corepackages/crouton-cli/CLAUDE.mdduplicate layers → SSR $setup.t is not a function
6hub: { db: 'sqlite' }, never hub: { database: true }; NuxtHub = storage only, never deployroot CLAUDE.md § Critical Gotchasbreaks schema.entry resolution + local migrations
7Translatable fields required: false at the root column (real value in translations.{locale}.field; root is cache/fallback)packages/crouton-cli/CLAUDE.md ("Why this matters")NOT NULL root fails translation-only inserts
8Layout is data (a LayoutTree in team-scoped layout_configs), not generated .vuecrouton-layout/server/database/schema/layoutConfigs.ts (code)runtime-editable + diffable for the agent⇄human loop
9Field types / capabilities are manifest-declared (§5) — one source, three consumers (CLI, MCP, meta module)crouton.manifest.ts per packagehardcoded tables drift per consumer (#285)
10Core's ensure-hub-blob module loads before @nuxthub/core; don't reorder core's modulescomment in packages/crouton-core/nuxt.config.tsdb: 'sqlite'-only apps still get blob

(Install/postinstall/typecheck invariants → sibling crouton-build-and-env; deploy invariants → crouton-ci-and-deploy-map.)

7. Known-weak points (plainly)

Weak pointEvidenceStatus
Production-SSR 500 on collection pages — an internal data-fetch loses the auth cookie under nuxt preview, so the entire e2e harness runs against nuxt dev (source of its huge timeouts)e2e/CLAUDE.md; tracked as #246 (per the e2e-smoke skill)unresolved, load-bearing
getCroutonLayers() has zero in-repo callers — the meta module IS consumed, but its headline extends-assembly API is unexercised; apps hand-list extends (CLI-synced)grep in re-verify blockopen question — aspirational or deprecation candidate (owner call)
packages/crouton-core/CLAUDE.md is stale — title claims @fyit/crouton (the meta package), references dead names (nuxt-crouton-i18n, @crouton/auth)the file itselfdon't trust its import examples; full stale list → crouton-docs-trust-map
ISR routeRules contradiction — root CLAUDE.md recommends a wildcard /api/teams/*/... ISR rule that live code comments mark BROKENowner: crouton-diagnostics-index § "The ISR routeRules contradiction"don't add such wildcard rules
CLI mirrors block sizing contracts by hand — crouton-cli/lib/compose-layout.ts "keep in sync" comment (whose own target list has drifted; live default registry is in crouton-layout/app/app.config.ts)the file's commentsilent-drift hazard on minWidth/defaultSize changes
packages/crouton-layout/CLAUDE.md self-contradicts — marks server-side extraction pending while layout_configs schema + API exist on diskfiles existtrust the code

Provenance and maintenance

verified: 2026-07-03

Facts checked against the working tree (velo/core/crouton/auth/layout/cli configs + CLAUDE.mds, fixtures/minimal generated tree, e2e/CLAUDE.md). Issue numbers cited from discovery-report summaries — check the issue if load-bearing. Volatile facts live below, not in the prose:

ls packages | wc -l                                                             # package count (31 at verification)
grep -rln "modules: \['@fyit/crouton'\]" apps fixtures pocs 2>/dev/null | wc -l # meta-module consumers (15 at verification)
grep -n "extends" apps/velo/nuxt.config.ts                                      # extends chain still hand-listed?
grep -rn "getCroutonLayers" apps/*/nuxt.config.ts fixtures/*/nuxt.config.ts pocs/*/nuxt.config.ts  # still 0 callers?
ls packages/crouton-core/app/components/stubs/                                  # stub list (7 at verification)
grep -n "priority" packages/crouton-core/nuxt.config.ts                         # stubs still -1?
grep -n "500 under production SSR" e2e/CLAUDE.md                                # #246 still unresolved?
grep -n "Keep in sync" packages/crouton-cli/lib/compose-layout.ts               # CLI sizing-mirror comment still there?
node scripts/harness-stages.mjs packages/crouton-core                           # packages still edit-guarded/test-first?

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

a11y

無料

Accessibility review for Vue surfaces — the code-cleaning analog of /code-review and /simplify, pointed at WCAG/ARIA. Reviews just your diff (or a package/file), rates findings by severity, and either comments inline on the PR (--comment) or applies the safe fixes for you (--fix). Steers the depth-aware `a11y` subagent. Use when asked to "check accessibility", "a11y this", "audit ARIA/keyboard", or run /a11y.

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

ask-human

無料

Emit a blocking question the owner can read in ~10 seconds and answer in one reply — the scannable, recommendation-first handoff every agent posts when it hits a fork it can't own. Leads with the one decision + a recommendation, carries the 🤖 provenance header, doubles as the

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

audit

無料

Audit packages for documentation completeness, detect drift between code and docs, and maintain documentation quality across the monorepo. Use when checking package docs, running audits, or reviewing documentation health.

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

Author a placeable layout block that looks right at ANY pane size. The one hard rule — size to the PANE with container queries (@container), never the viewport — plus list/form playbooks and the sizing contract (minWidth etc.) the viability metric reads. Use when adding/converting a croutonLayoutBlocks block, or when a block overflows/breaks in a narrow pane.

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

When a bug or regression is reported, the FIRST step — before fixing — is to research how and when it was introduced (git archaeology), then record that finding on the tracking issue/PR. Use the moment a bug, error, broken build, or "this used to work" is reported, before writing a fix. Produces a first-bad-commit (or "not a code regression") note you paste onto the issue.

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

commit

無料

Smart, granular git commits following monorepo conventions. Analyzes changes, filters to session-relevant files, groups by intent, and uses conventional commit format. Use when committing code changes.

日本語の概要は準備中です。原文の説明を表示しています。

FriendlyInternet/nuxt-crouton102026年10月13日 更新

FriendlyInternet のスキルをすべて見る

このスキルの問題を報告する