120 Commits

Author SHA1 Message Date
Minsu fd71da42a8 Merge pull request #14 from Gaic4o/evals/harness-layout
ci: store eval cases as evals/evals.json with per-case assertions
2026-09-17 00:28:03 +09:00
Gaic4o 199ffe4989 evals: split each expected output into assertions a judge grades one at a time
A judge model grades an eval per assertion, and a case whose only
assertion is the whole expected output fails as one result that does not
say which rule broke. Every case now lists its conditions separately: the
placement first, then each thing that must not happen. The validator
requires a non-empty assertions array so a new case cannot skip them, and
the evals README describes how to write one.
2026-09-15 16:06:37 +09:00
Gaic4o 03df8117a6 evals: move the cases into the skill folder as evals/evals.json
The case file used its own schema at the repository root, so nothing
could run it but a person. agent-skills-eval and skill-creator both read
<skill>/evals/evals.json with skill_name, evals[], and expected_output,
so the same file now serves the validator here and a judge-model harness
outside CI. The why, source, and rule fields stay; harnesses ignore them.

The validator looks for evals/evals.json under each skill directory and
checks that skill_name matches the directory. The CI path filter follows
the file, and both READMEs describe the new layout and how to run the
cases with a harness.
2026-09-15 15:24:47 +09:00
Minsu 6720d8d2ca Merge pull request #13 from Stef-Gijsberts/patch-1
Clarify placement of entities/features with single consumers
2026-09-15 12:56:28 +09:00
Stef Gijsberts ac06682d3a Clarify placement of entities with single consumers 2026-09-14 09:51:31 +00:00
Minsu e7eac044ee Merge pull request #12 from Gaic4o/docs/issue-template
docs: replace the four issue forms with one free-form template
2026-09-05 13:28:56 +09:00
Minsu 3e7cd5c4e0 Merge pull request #11 from Gaic4o/ci/reference-routing
ci: decide reference routing in CI instead of the PR checklist
2026-09-05 13:28:48 +09:00
Gaic4o 1f95809d03 docs: replace the four issue forms with one free-form template
Four forms asked a reporter to classify their own issue before writing
it. Two of them were not contributor intake at all: the version bump
form is a maintainer epic, and the enhancement form asked in fields what
a description covers in prose.

Collapse them into one Issue template so anyone can report a problem or
suggest an improvement without picking a category first. The contact
links in config.yml still route questions to Discussions, Telegram, and
fsd.how.

This drops the structured repro fields from the incorrect-guidance form
(agent, prompt, produced output) and the required upstream URL from the
docs-sync form, so those details now depend on the reporter offering
them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 12:37:18 +09:00
Gaic4o b3ea00e5c4 docs: ask contributors to attest they reviewed AI-assisted changes
Whether a human actually read the diff is not something CI can decide,
so it belongs in the checklist rather than the validator.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 23:01:08 +09:00
Gaic4o fbe1af7bdc ci: harden the validator against three review findings
A null evals document crashed on property access instead of being
reported, so the run ended in a stack trace and validateRepository broke
its contract of returning problems. Read it with optional chaining.

Package-scoped ids matched a single character, so "Step 10" matched
nothing at all and its citation went unchecked while "Step 9" was
caught. Accept a letter or a run of digits, on both the citation pattern
and the anchor pattern, or a heading that exists would start failing.

Reference paths were collected for routing from fenced examples too,
which contradicts findRoutingSection already ignoring fences. A path in
an example still has to resolve; it just is not a routing rule.

Also measure the line-limit fixture against the live body length, so the
test keeps pinning the boundary if SKILL.md shrinks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 23:01:08 +09:00
Gaic4o dc8c60ac71 ci: skip fenced lines when locating the routing section
A `# comment` at column 0 inside a fenced example read as the next
heading and cut the section short, and a heading shown inside a fenced
example was picked as the section itself. Either one reported every
reference below it as unrouted, failing a PR that was correct.

Toggle on fences the way the cross-reference check already does, and
cover both shapes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 23:01:08 +09:00
Gaic4o 9ff8d3a407 ci: route reference files from the Conditional references section
The orphan check accepted a reference named anywhere in SKILL.md, so a
file mentioned only in body prose passed while no routing rule reached
it. Scope the mention set to the routing section, and fall back to the
whole-file check for a skill that has no such section.

That prose-only case is what the PR checklist asked a human to catch, so
drop that box along with the one asserting the validator passes locally.
CI decides both now; the README still tells contributors to run it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 23:01:08 +09:00
Minsu d192446f95 Merge pull request #10 from Gaic4o/skills-va
docs: align the skill with the official FSD docs and validate it in CI
0.2.0
2026-09-04 15:07:44 +09:00
Gaic4o 4f8215f049 docs: teach one auth structure in auth-and-api, not three
The file named the same slice three ways. The Auth UI section put the
multi-use login form in features/auth, both dialog trees called it
features/login, and the prose between them said features/auth again. A
reader following the examples gets a different structure each time.

The features/auth tree also held two use cases at once, LoginForm with
RegisterForm and api/{login,register}.ts, which is the bundle 95ab132 split
in layer-structure.md into auth for signing in and register for signing up.
Split it the same way here and point both dialog trees at features/auth.

My review of this PR missed all of it. The sweep searched for the flat path
features/login, and every occurrence was nested inside a tree as
"features/" then an indented "login/", so nothing matched. The sweep now
walks the line after a features/ node.
2026-09-04 14:43:32 +09:00
Gaic4o f3e1449eba ci: lock the auth ownership condition in an eval and free SKILL.md headroom
user-entity-does-not-own-token accepted "either can be right" without
naming what makes an entity the right home. The documentation now requires
an already-established entity that genuinely owns the authentication
state, so the case can pass while the docs regress to "user entity exists,
therefore token entity". Ask for the ownership condition, and say that the
entity existing or profile data being reused is not enough.

SKILL.md was at 498 of the 500-line limit, which leaves no room for the
next sentence anyone writes. Three passages give back seven lines without
losing a rule:

The widgets callout closed with three lines saying the layer is not being
removed and existing projects may keep it. layer-structure.md:155 says the
same and carries the slice rules and an example with it, so one line and a
pointer do here. The paragraph above it listed where each kind of UI block
goes in full sentences; the same routing reads as one.

Rule 4-2 explained why a single segment index can hurt: bundle size,
tree-shaking, dev server startup. The rule is which index to give a folder
and where the boundary then sits, and layer-structure.md is already cited
for the shape.

SKILL.md is now 491 lines.
2026-09-04 14:39:31 +09:00
Gaic4o 43c47b430e docs: decide auth state placement by ownership, not by what exists
Review of PR #10 found examples and summaries that still taught the rules
the branch removes.

The auth section led with shared/auth as the simpler default and then
captioned its entity example "entities layer exists, profile reuse is
real", which is both removed heuristics in one line, ten lines under prose
saying an existing entities layer is not a reason. Make shared/auth the
default for tokens, refresh and expiry handling and the session, and let a
user or session entity own that state only when it is an established
boundary that genuinely owns it. An entities layer existing, a user entity
existing, and profile reuse are none of them reasons to move credentials.

Two places contradicted that and were not in the review. excessive-entities
said tokens stay in shared/auth "either way", which forbids what the
official Auth guide allows and contradicts the anti-pattern six lines below
it in the same file. Its decision tree branched on whether an entities
layer exists and then forbade entity-owned tokens; it now asks whether an
established entity already owns the state rather than merely existing.

Strategy B read "shared domain logic goes to entities" in SKILL.md and
"move that domain-level flow into a domain slice" in cross-import-patterns,
one sentence above a bullet excluding user-flow orchestration. Both now
move a shared domain responsibility into the entity that owns it.

The Quick placement table answered a multi-use product card with entity UI
and no condition; the cell is now conditional on the entity owning it and
the note carries the Section 6 caution. The iteration caption in
excessive-entities still said "extracted only after reuse is real".

README says the skill follows FSD v2.1 without being a verbatim copy: it
picks one guide when two official ones differ, follows the framework where
an integration guide has fallen behind, and folds in recent maintainer
guidance.
2026-09-04 14:37:06 +09:00
Gaic4o b4089ca03a ci: test the validator
Three hundred lines of regex over prose, and the only evidence they caught
anything was a set of breakages applied by hand and reverted. A green run
could not be told apart from a check that matched nothing.

Fifteen tests copy the real repository to a temporary directory, break one
thing, and assert that the validator names it: a dangling section number,
a rule id in a list, a strategy letter, a renamed named-rule heading, a
case rule with a bad id, a bad quotation, or a missing heading, a sibling
reference path, an orphan, the line limit, the description limit, and a
mismatched name. The first test asserts the repository as committed
passes, and one checks that code fences are skipped.

validate.yml runs them after the validator and now also triggers on any
change under .github/scripts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 13:48:00 +09:00
Gaic4o 10c8fc7252 ci: check sibling reference paths and frontmatter limits
The validator only resolved `references/<file>.md` paths written in
SKILL.md. Reference files point at each other in 28 places, and none of
those were checked, so deleting or renaming a reference would break the
SKILL.md route loudly and the sibling routes silently.

Every document is now scanned for reference paths. The frontmatter check
also enforces the Agent Skills limits of 64 characters for `name` and 1024
for `description`; the description is at 906 and the body at 497 of 500
lines, and both are now printed so the headroom is visible on every run.

The validator is reshaped into an exported validateRepository(root) that
returns the problems it found, with the CLI entry point guarded behind an
argv check, so a test can run it against a mutated copy of the repository.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 13:48:00 +09:00
Gaic4o 2b5e5d7227 docs: open SKILL.md with a map of where each question is answered
Section 10 routes to the reference files, but nothing routes within the
body itself, and it sits at the end. A reader who wants to know where code
goes has to find the decision tree first.

Five lines under the header name the four entry points: the tree and table
for placement, Section 4 for violations, Section 7 for tangled slices, and
Section 10 for everything else. The body is at 497 of its 500 lines, so the
next addition has to pay for itself somewhere else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:48:00 +09:00
Gaic4o ea75e1719d docs: give the extraction rule a name and cite it by that name
The three conditions that decide whether code earns a lower layer were the
most-cited passage in the skill and the least anchored: a paragraph inside
Section 1 with no heading of its own. Eight places pointed at it by section
number, so inserting a section ahead of it would have silently redirected
all of them.

The conditions are now a numbered list under "The extraction rule", with
"The six layers" and "The import rule" splitting the rest of Section 1, and
every citation names the rule instead of the number. Reference files carry
the name too, so a reader who opens one alone is not sent back to a section
number to find out what the rule says.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:52:09 +09:00
Gaic4o f902635bab ci: validate cross-references between the skill's documents
Section and rule numbers are how these documents address each other, and
nothing noticed when a renumbering or a renamed heading left one dangling.
evals/README.md said as much about the `rule` field: keep it in sync by
hand. That is a job for a script.

The validator now resolves `Section N`, `Rule N-M`, `Step N`, `Strategy X`,
`Snapshot N`, `Part N`, and `Question N` against the headings and bold
labels of the skill package, and checks each `;`-separated fragment of a
case's `rule` the same way. A named rule cited from more than one file must
be an anchor somewhere, so renaming it breaks the build instead of the
reader.

The existing documents and all 19 cases pass unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 12:51:50 +09:00
Gaic4o e9d9f26705 docs: carry the three conditions into the decision tree and the eval cases
Section 2 is the first thing an agent reads and it was the last place
still asking two of the three conditions. Steps 3 and 4 asked for multiple
places and a stable boundary, and the Golden Rule asked for multiple
places and a clear one. Ask for the change reason too, and let the Golden
Rule point at Section 1 rather than restate half of it.

Step 2 still listed "CRUD operations goes to shared/api" flat. Step 1
filters single-consumer requests before that bullet is reached, but it was
the last of the three flat CRUD classifiers and the single-consumer-crud
eval expects pages/settings/api.

The Steiger description in 5-4 predates the correction in the migration
guide. The rule also flags slices with no references, suggests merging
into the layer above rather than into a page, and exempts pages and
app-only slices.

Rule 4-2's per-component index exception named shared/ui and shared/lib.
The official React Query guide gives shared/api/example its own index, and
state-management.md imports @/shared/api/post, so controller folders
belong in the exception.

Two eval fixes. user-entity-does-not-own-token named the wrong commits and
graded the same question as auth-token differently: one said flatly "not a
user entity" while the other said either can be right. auth-token now
covers what it actually guards, creating an entity to hold the token.
Commit SHAs come out of both why fields, since a squash merge drops them.
2026-09-04 12:39:05 +09:00
Gaic4o 6bb78ec345 docs: say in the README what makes this skill answer differently
The skill description read like any FSD overview, so nothing on the front
page showed the bias the references spend their length on. Say it: start
with app, pages and shared, and open a features or entities boundary only
when a stable shared responsibility earns one. The examples now include
the judgement call that follows from it, whether a rule used on two pages
becomes an entity, which is the question the skill exists to answer
differently.

evals/ was missing from the structure tree while Contributing already
described what the validator checks in it. It sits beside the skill
package rather than inside it, so the tree shows it there.

"The agent reads only SKILL.md by default" describes a runtime guarantee
the skill cannot make. SKILL.md is the entry point and it instructs the
agent when to load a reference; say that instead.

The placement bullet listed pages, features, entities and shared and left
out app, which the skill covers at length through layouts, providers and
routing. The grouping bullet now names slice groups, the term the
reference uses.

Two wording fixes: the validator implements this repository's rules, which
draw in part on the vercel-labs guidance rather than implementing it, and
one checklist line was phrased differently from its neighbours.

Left alone for the third time: the markdown links are well formed. README
contains no nested-bracket link markup at all.
2026-09-04 12:26:33 +09:00
Gaic4o 4ca7dcd1d3 docs: describe the eval fields the way the cases actually use them
The field table said `rule` names a section inside `source`. Five of the
nineteen cases name a rule in a second file as well, because a placement
is often settled by a reference and a SKILL.md rule together. Describe
that, and say what `source` is for: the file to open first on a mismatch.

Add the principle that split phase-out-widgets in two. A prompt checking
several independent placements comes back as one failure that does not say
which rule broke.

"Grading requires a model" reads oddly two lines above "run them by hand".
What needs a model or a person is comparing an answer to `expect`.
2026-09-04 12:25:08 +09:00
Gaic4o 342fbf5825 ci: resync the eval expectations with the ownership rules
new-project-three-pages contradicted Snapshot 0 of the file it cites. It
expected every request in shared/api, and 54daf07 moved the walkthrough's
requests into their pages because each had one consumer. I said then that
this case still read correctly; it did not. Its prompt now says which
request the two pages share, and its expect keeps single-consumer requests
in their page.

plain-reusable-request answered that domain rules promote a request, which
is the classifier 7f37c3e and 0f1fd1f removed from the references: a rule
sends the request to whoever owns the rule, and only an established entity
boundary sends it to an entity.

app-header-placement listed features and entities as sources for the
header's parts without saying those boundaries already exist, and
phase-out-widget-placement asked where a reused action goes without saying
what makes it a feature. business-themed-in-shared excluded business logic
from Shared but not slice-specific behavior.

Two cases added for the two boundaries that moved most while these
references were being aligned. user-entity-does-not-own-token pins that an
entity appearing does not transfer credential ownership, without claiming
tokens can never live there, which the official Auth guide allows.
single-consumer-crud pins that CRUD not belonging in an entity does not
mean it belongs in Shared.

Also drops a "single most reported" claim from a why field that does not
need to be ranked.
2026-09-04 12:23:57 +09:00
Gaic4o 7c8a84a6e8 docs: sort assets by ownership in the table and anti-patterns too
The body had already moved to asking whether several consumers must share
one authoritative copy. The summary table and two anti-patterns were still
sorting by how many places use a file: "Icon reused across the app" and
"Move it only when actual reuse appears". Give them the same question the
decision tree asks.

The opening asked for grouping by use case. A global font, a company logo
and a favicon are placed by who owns their lifecycle and how they have to
be delivered, not by a use case. Ask for the module whose lifecycle the
asset shares.

Three smaller ones. The decision tree offered ui/ or model/ as if those
were the only two homes, when the question is which segment consumes the
asset. "Do not split assets and the components that use them" has real
exceptions, a fixed public URL among them, so name the reason rather than
the rule. And Next.js, Nuxt and Astro are frameworks, not bundlers; the
Vite-specific note about pointing publicDir at a project-local folder said
more about config than about placement.

Left alone again: the Desegmentation link is a single well-formed markdown
link, and the file contains no nested-bracket markup at all.
2026-09-04 12:22:36 +09:00
Gaic4o 0f1fd1f927 docs: keep auth boundaries from crossing layers in the wrong direction
Two dependency hazards this file described without guarding.

features/logout was said to own token cleanup, state cleanup, error
handling and navigation. A feature that navigates imports the router from
app/, which Rule 4-1 forbids. Take navigation out of the feature's job and
show the page awaiting the action and navigating itself.

Automatic logout is triggered by a refresh failure the API client in
shared/api notices first, and that client cannot reach an entity to reset
its state. Say to report the failure back through the same callback,
context or subscription the official guide uses to hand the token down.

Six wording fixes. "Login utilities" was broad enough to pull the whole
login flow into Shared. The file called the same slice features/auth in
one section and features/login in another. The type key principle sent
domain models with business logic to entities without the earned boundary.
Question 1 asked for a headcount when it means ownership, and Question 2's
permission example named entities/user/api without asking who owns the
rule. Nullable<T> was filed under domain-named files.

The Product example inherited ProductDTO and stored isOnSale as a boolean,
teaching transport coupling and a snapshot of a derived value. It now maps
the fields it wants and computes the rule from the price.

Path B stays. GPT-suggested removal of the token from the user entity
would contradict the official Auth guide, which documents "In Entities" as
one of two storage locations and says the store "can contain both the
token and the user object". 3858589 already corrected this repository in
that direction.
2026-09-04 12:20:57 +09:00
Gaic4o cdf71cdfca docs: build the composition examples out of user actions
Strategy C demonstrated composition with features/user-profile,
features/activity-feed and features/comment-list. A panel, a feed and a
list are UI blocks with no user action in them, so under this skill's own
definition none of the three is a feature; page-local UI is where they
would go. The composition patterns were right and the cast was wrong,
which teaches the reader that a feature is whatever block sits on a page.

Basic composition is now a profile page holding a follow button and a
report button, two actions that share a screen and nothing else. Render
props and slots both become manage-wishlist leaving a per-row hole that
the page fills with add-to-cart, which is the case the pattern exists for:
one action's UI has to place another action's control inside itself and
must not import it.

The three features now in the file, follow-user, report-user and
manage-wishlist, join add-to-cart, buy-now and apply-coupon from the
earlier examples, so every slice in the file is named for what a user
does.
2026-09-04 12:18:50 +09:00
Gaic4o 581dc1670c docs: name the cross-import features after the actions they perform
The seven fixes from this review round landed in 3858589. This finishes
the last one, example naming, for the cases where a rename is the whole
fix: Strategy A merged features/profile with features/profile-settings,
and Strategy D had features/profile depend on features/auth. Both are
nouns in a file whose own rule is that a feature is a user interaction.

Strategy C's examples are left for a separate decision. features/
user-profile, features/activity-feed and features/comment-list are not
badly named so much as not features: a panel, a feed and a list are UI
blocks with no user action, so renaming them would hide the question
rather than answer it. Reworking those three composition samples changes
what they demonstrate, which is a bigger edit than this round.
2026-09-04 12:16:43 +09:00
Gaic4o 3858589ebe docs: open cross-imports with the ban, and put @x back at the end
The file opened on "a code smell, not an absolute prohibition", which is
the exception before the rule. Rule 4-3 disallows same-layer cross-imports
by default; A to C remove the dependency and D and @x are the documented
exceptions. Say that first, then keep the point that a deliberate one is
possible.

@x sat second in the entities branch of the decision flow, straight after
merge, which is a short queue for a last resort. Two entities that seem to
need each other may instead be composed by a page, or share something
business-neutral that belongs in shared. Ask both before reaching for @x.

Strategy B pushed "domain flows" into entities, the word the rest of the
skill reserves for what makes a feature, and SKILL.md Section 7 calls the
same strategy "shared domain logic". It also claimed entities hold domain
types and logic only, which layer-structure.md contradicts: an entity may
have ui/ with caution and api/ when it owns the request. Say what actually
moves, an established domain responsibility.

Its example sent session validation to entities/session while two features
named auth and profile consumed it, which leads with the hardest boundary
in the skill. Two features needing one purchase rule shows the same
strategy and separates the entity's rule from each feature's action.

Strategy C said one feature must not import another, full stop, three
paragraphs above the strategy that allows exactly that. State the default
and name where the exception lives.

Two smaller ones. Slices are not deployment units, and a consumer couples
to a public contract rather than to internal implementation, so the code
smell rationale said more than it should. A bidirectional import tells you
the boundaries are wrong, not that merging is the repair.

Also corrects 7f37c3e: it said tokens and session infrastructure stay in
shared/auth "either way", but the official Auth guide accepts an entities
slice for the current user or session, and auth-and-api.md says so.
2026-09-04 12:14:17 +09:00
Gaic4o 7f37c3ed56 docs: place CRUD by the request rule, not at a fixed path
"CRUD does not justify an entity" and "CRUD lives in shared/api" are
different claims, and three passages here ran them together: Section 3,
the decision tree branch, and the anti-pattern. A settings update that one
dashboard page calls is plain CRUD and belongs in that page's api/
segment, which is what Question 1 says and what the Quick placement
table's single-use column shows. Keep the entity claim, and let the
request placement rule answer where it goes instead. SKILL.md 5-2 item 4
stated the same shortcut and moves with it.

The auth anti-pattern still allowed a user entity "when token logic is
genuinely tied to user business logic", which 906aaeb had just ruled out
four sections above by keeping tokens and session state in shared/auth
either way. Remove the exit.

The decision tree's auth branch asked whether profile identity is reused
outside the login flow, turning the prose criterion back into a count.
Ask what the prose asks: is there a stable user-domain responsibility that
needs one shared home.

The tree opened on "a new piece of business logic" and then routed tokens,
login DTOs, response types and CRUD, none of which the skill counts as
business logic.

Section 2's order example said business logic that operates on the DTO
lives in entities/order/model, without the boundary condition the section
above it spends a paragraph establishing.
2026-09-04 12:10:50 +09:00
Gaic4o 1652c11450 docs: make the framework examples agree with the principles above them
Same split as layer-structure had: the principle says configure an alias
only for a layer the project has, and the Next.js and Vite examples then
registered all six, widgets included, in trees whose own comments mark
three of them "when needed". Drop those three from both; the Astro tree
gets the same comments the Next.js tree already had.

React Router's wiring section still said the component and the loader come
from the FSD page's public API. 5284fa0 changed the example so the route
module owns both exports and delegates inward, which is the better shape
and what the two paragraphs under it describe. The sentence was left
behind.

Next.js proxy and instrumentation can sit in src/ when the Next.js app
does, but under the layout this section recommends the routing folders are
at the project root, so both files are too. Say that, since the src/
option is the half a reader remembers, and it never means src/_app/.

Two Shared guards. shared/db holds plain data access; Rule 4-5 does not
relax because code runs on the server. Route Handlers stay adapters and
leave domain rules with the slice that owns them.

The Nuxt heading named a version that reached end of life on 31 July 2026,
which reads as advice to start there. Name the layout instead and say what
happened to the version.

"One @/* entry works everywhere" claimed more than alias syntax can: the
resolver wiring is what differs, which is why the React Router section
needs vite-tsconfig-paths.
2026-09-04 12:08:58 +09:00
Gaic4o 34de2ad9ee docs: name the responsibility the walkthrough's layers are earned by
The opening said a layer is earned by a rule that needs one home. That
fits the entity in Snapshot 2 and not the feature in Snapshot 3, which is
a user action rather than a rule. Ask for a stable responsibility, which
covers both.

The closing summary said a complete action that two pages perform opened
features, putting the count back in the last line after four snapshots
spent removing it. Say what actually earned each layer: a rule that had to
stay consistent across consumers, and an action with one shared behavior.

Snapshot 3 labelled its request "owned by one feature, so it lives here",
which reads as a rule that a request follows whichever slice owns it. It
moves because it is the add-to-cart use case itself; a plain reusable cart
request would have stayed in shared/api. cart-store.ts is renamed for the
same reason, since a file by that name sounds like the cart domain's store
and would teach that any single-consumer store belongs in a feature.

"What did not appear" then justified the missing cart entity by counting
consumers. Say the real reason: nothing here is a reusable cart-domain
responsibility yet, and name the case that would be one.

Snapshot 2 credited the excessive-entities guide with keeping "data
definitions" in shared/api, the same phrase 906aaeb narrowed in that file.
It is the transport type that stays.
2026-09-04 12:06:58 +09:00
Gaic4o 54daf079c3 docs: start the walkthrough's requests in their pages, as Question 1 says
Snapshot 0 put fetchProduct and fetchProducts in shared/api on day one and
justified it with Section 2 Step 2, skipping the question that comes
first. Each request had exactly one consumer, and three places say that
keeps it in the page: Question 1 of the request placement rule, the
single-use column of the Quick placement table, which names this very
file, and the single-page-request eval case. Snapshot 1 then cited
Question 2 for a decision Question 1 had already made.

Give each page its own api/ segment and leave only ProductDTO in
shared/api, which both pages read. When the search page arrives,
fetchProducts gains its second consumer, Question 1 releases it, Question
2 finds no domain rules, and it moves down to shared/api. fetchProduct
stays where it is, because the detail page is still alone with it.

The walkthrough now demonstrates that pages-first governs requests too,
and Snapshot 1 gets a second thing to teach: code can move down a layer
without a layer being opened, which is the distinction the whole file is
about. new-project-three-pages still expects requests in shared/api, and
still should: its prompt gives all three pages product data from the
start.

Also drop the line count from the reason for keeping two copies of the
sale rule. Copies stay local because nothing yet requires the consumers to
agree, not because the rule is short.
2026-09-04 12:06:22 +09:00
Gaic4o 5ee93edfa0 docs: keep the layer-structure examples from teaching past their rules
Three examples were contradicting sentences a few lines above them, which
is the half an agent learns from.

The Shared tree still carried an assets/ segment. 3f5725c removed it from
SKILL.md, asset-handling.md never had one and routes shared presentation
assets to shared/ui, and Section 6 forbids an assets/ segment outright.
This was the last place showing it as part of the default shape.

The alias block listed six layers, widgets included, directly under a
sentence saying to add an entry only for a layer the project has. Show the
three-layer project from Section 5-3 and name the rest as later additions.

Naming conventions asked for names from the business domain while
shared/lib holds debounce.ts and format-date.ts. Same wording as 3f5725c
gave Rule 4-4.

The features example labelled auth "One use case: signing in" next to a
register sibling, which reads as a rule that auth means login. Make both
comments name their use case and stop the slice from defining the word.

The slice keeps its name. features/auth appears in eleven places across
five files, always as the sign-in feature, and SKILL.md's god-slice
anti-pattern uses auth/ as its example of a focused slice next to
password-reset/. Renaming it here alone would break that agreement.
2026-09-04 11:59:47 +09:00
Gaic4o 53a37f5e83 docs: finish taking the counts out of the migration steps
Part 1 opened by saying that code reused across several pages moves to a
layer below, which is the rule Section 1 replaced. The processes step told
teams to create a feature when several pages share workflow state, and its
tree captioned the result "Used in 2+ pages". Step 8 let business logic
stay duplicated when it is "not used in many places". Ask in each case for
a stable responsibility that needs one home.

3f5725c claimed the repository had no count rules left. That grep matched
"2+ places" only, and this file said "2+ pages".

Post-migration verification said no entity or feature may survive that one
page uses, which flattens the insignificant-slice description Step 1 gives
four sections earlier, exceptions and all. Ask instead whether each
single-reference slice still has an intentional boundary.

Step 1 marks a single-reference slice to move back into its consumer, but
Step 2 only offered a page or a widget. An entity's sole consumer can be a
feature. Say that.

The Step 4 tree labelled Shared "only objects that are reused". Shared is
infrastructure that carries no business rules; being reused is not what
puts something there.

Left alone: "the DRY principle holds for business logic" is defined by the
clause after its own colon, "avoid copy-pasting code that must stay in
sync", and that is the rule Snapshot 2 of the walkthrough acts on. The
count in SKILL.md Step 1 runs the safe way, two pages and still keep the
copies.
2026-09-04 11:55:18 +09:00
Gaic4o 94d46e76c8 docs: stop the closing principle from undoing the section above it
The last line of the file sent shared queries to shared/api or
entities/<name>/api "depending on whether the project has an Entities
layer". Fifty lines earlier, c349702 had gated the same choice on whether
an established boundary owns the request and said in as many words that an
existing entities folder is not a reason. The summary is the line an agent
carries away, so it decided the question the section had just settled.

Three more, all in the same direction:

Step 6's two destinations were still described as a business thing and an
action users take, one sentence after the file says a todo noun does not
make an entity. Describe them by responsibility, the way layer-structure.md
now describes the same two layers.

"All API requests for a domain live in one place" is not what the example
shows: getPosts and getDetailPost keep their own files and the factory
wires them to keys. Say what the factory actually gives.

Code generation carried the official guide's comparison as if it were our
own finding, and gave no placement rule beyond a path. Attribute the
comparison and say the thing the section is for, which is that generated
endpoints are transport code and do not move into entities for naming
business resources.

The shared/api type was called a DTO in prose and Todo in code, while the
rest of the skill writes ProductDTO and OrderDto. Rename it and say that
the entity uses the transport type directly unless a rule needs a shape
the backend does not send.

Left alone: "the request carries domain rules" is Question 2's defined
term in auth-and-api.md, and "each request corresponds to a single entity"
is the official guide's condition. The custom API client keeps its example,
since every other section here shows one and SKILL.md routes readers to
this file for exactly these topics.
2026-09-04 11:52:46 +09:00
Gaic4o 3f5725c461 docs: retire the last count rules and the classifiers left in SKILL.md
The layer overview still gated features and entities on "2+ places", eight
lines under the three conditions that replaced it, and it was the last
count rule left in the repository. layer-structure.md already states the
criterion in full for the same two layers. Point the legend at the rule
above it instead. The minimal-layers comments in 5-3 gain the same words
the walkthrough uses.

Rule 4-5's GOOD example moved a reputation calculation straight from
shared/lib to entities/user, which is the "business logic implies an
entity" step 5-2 item 3 warns against two sections later. Show both homes:
the profile page while it owns the rule, the entity once that boundary is
earned. It also sits in model/ now, since business logic is what model/ is
for.

Section 6 sent complex transactional logic to entities. 906aaeb removed
that classifier from excessive-entities.md; a checkout or a cancellation
is a use case and belongs to whoever owns the rule.

Rule 4-4 asked for names from the business domain, but shared/lib holds
debounce.ts and format-date.ts. Ask for what the file is for, which is the
wording layer-structure.md and the migration guide already use.

Section 9 listed a shared assets/ segment while Section 6 forbids an
assets/ segment and asset-handling.md never mentions one, routing shared
presentation assets to shared/ui instead. Drop it and point at the
reference.

The same section barred "entity-specific code" from Shared while the skill
puts ProductDTO in shared/api. Name what is actually barred, the business
rules those layers own and any upward import. layer-structure.md carried
the same sentence and moves with it.
2026-09-04 11:50:16 +09:00
Gaic4o e594dacb0f docs: let the eval README admit that a case can be the thing that is wrong
"Fix the document, not the case" was aimed at the right target, moving the
goalposts to make a model pass, but it forbade more than that. b52ff29
corrected shared-ui-import, which accepted only the fallback import shape
and would have failed an agent giving the documented default. Under this
sentence that fix was out of bounds. Say what actually matters: never edit
expect just to match what the model said.

The validator claim was wider than the validator. It checks that `source`
resolves, not that the passage named in `rule` still exists, so a heading
rename leaves a case pointing at nothing while CI stays green. Say which
half is enforced and that the other half is manual.

The bar for adding a case asked for ambiguity alone. Several cases here do
not clear it and should not have to: entities cannot import app is not
ambiguous, but every Redux tutorial types selectors with RootState, and a
type-based assets folder is the common default in most projects. Admit
regressions and outside conventions as reasons, and keep the part that
rejects cases guarding nothing.

The three cases dropped in 9fe031a fail the wider bar too, so that stays.
2026-09-04 11:48:01 +09:00
Gaic4o b52ff294d6 ci: align the eval cases with the rules the references now state
The references moved to deciding placement by shared responsibility, and
several cases were still asserting the count-based version they replaced.
A suite that enforces the old rule is worse than no suite.

shared-ui-import was wrong, not just dated. It accepted only
@/shared/ui/Button, but Rule 4-2 makes the segment index the default and a
per-component index the fallback for tree-shaking, so it would have failed
an agent that answered @/shared/ui. Accept both and reject only reaching
past the boundary.

new-project-three-pages expected the product DTO and its requests in
shared/api from a prompt that never said the pages fetch anything. Put the
premise in the prompt.

small-project-entities said to add layers "when reuse is real".
top-level-assets-segment still routed by asset type into shared/ui and a
root public/. app-header-placement read as though everything inside the
header belongs to app. same-layer-import answered a yes-or-no question
without a yes or no.

phase-out-widgets checked seven things at once, so a failure said nothing
about which one broke. Split it into whether 2.1 forces the layer out, and
where each widget goes once a team decides to remove it.

Two cases added. similar-product-cards is the negative half of the
extraction rule and nothing covered it: two pages, one component name, and
the answer is still no. single-use-user-action is the same for features,
against the reflex that a user action implies a feature slice.

Left alone: the rule fields keep their section numbers. Those numbers are
part of the heading text and are how the whole skill cross-references
itself, so they move only on a deliberate renumber.
2026-09-04 11:46:12 +09:00
Gaic4o 0ce3ac143c docs: correct the Astro public folder claim and place assets by ownership
Astro: the file said its public path is fixed and cannot be changed. Astro
documents a publicDir option with a default of ./public and an example of
pointing it elsewhere. The official assets guide is where the claim came
from, so record which one this section follows.

app/public/ appeared in the prose and twice in the summary table as a
general option. It only exists where the bundler lets you move the public
directory, which Next.js does not; an agent reading the table would create
one anyway. Keep it as the Vite-shaped config choice it is, and let the
table name the framework's public directory instead of a path. Say too
that framework conventions for favicons and metadata files come first.

The decision tree still sorted assets by how many slices used them. Ask
what the rest of the skill asks: must these consumers share one
authoritative copy? Two hero images that happen to match today and will
change for their own reasons stay where they are.

shared/ui/ was the destination for any shared asset. The official guide
scopes it to reusable icons and images, so scope it to presentation assets
and send the rest to the shared module that owns them.

app/styles/ and app/fonts/ read as required names. layer-structure.md says
App segment names are conventional, so say that here too.

Left alone: the Desegmentation link is valid markdown, and the PDF in
model/ example is the official guide's, kept for the reason it gives,
which is coupling to business logic rather than not being UI.
2026-09-04 11:43:39 +09:00
Gaic4o 906aaebec2 docs: state what earns an entity instead of counting its consumers
The decision tree ended on "reusable domain logic confirmed in 2+
consumers", the anti-pattern list repeated the number, and the iteration
example used it as the caption for the moment of extraction. This is the
last file still deciding entities on a count. Ask for reuse that is real,
has a reason to change of its own, and needs one authoritative copy, which
is what the walkthrough demonstrates and what Section 1 requires.

Four more, all of them classifiers standing in for ownership:

"Complex CRUD" pointed at entities. Complexity is not a layer. A checkout
or a cancellation is a use case and belongs to a feature or the page that
runs it; only a rule the domain owns belongs to the entity.

"Keeping data definitions in shared/api" is broad enough to send every
domain type there, which contradicts the type table in auth-and-api.md.
Narrow it to the transport shapes the sentence is actually about.

A user entity was gated partly on the entities layer already existing.
c349702 said a folder that exists is not a reason to move code into it;
say the same here, and in auth-and-api.md, which carried the same wording.

The thin-client branch said to skip entities outright, harder than both
the prose above it and the official guide, which says "most likely".

Left alone: "the later code moves to entities, the less dangerous the
refactor" is the official guide's sentence with the official guide's own
justification already attached, and entities/order-info is its example
name, not ours.
2026-09-04 11:40:43 +09:00
Gaic4o 5284fa0551 docs: follow the frameworks where the upstream FSD guides have fallen behind
Checked against the framework docs rather than the fsd.how guides, because
this is the one reference whose subject moves.

Next.js: the file said middleware.js and instrumentation.js must be in the
project root and that Next.js will not detect them under src/. Both halves
are now wrong. Next.js 16 deprecated the middleware convention and renamed
it proxy, and both proxy.ts and instrumentation.ts are documented as root
or inside src/ when the app uses src/. The official Next.js guide on
fsd.how still says root only, so record which one this section follows.

Nuxt: the section is a Nuxt 3 layout, and the official guide covers Nuxt 3.
Nuxt 4 moved the default srcDir to app/ and puts pages/ and layouts/ under
it, so its framework app/ collides with the FSD app layer the way Next.js
does, and no official guide covers that yet. Say so, and say that a Nuxt 4
project can keep the Nuxt 3 shape, which Nuxt still auto-detects.

React Router: the route module exported the FSD page directly as its
default, but the framework passes the component its generated
Route.ComponentProps, not the page's own props. Take loaderData in the
wrapper and hand plain props down, which also makes the boundary the
paragraph describes visible. Split that paragraph into what React Router
requires and what this skill recommends, the way the Next.js sections
already separate the two.

Three smaller ones. "Always re-export both the component and metadata"
holds only for pages that have static metadata, and generateMetadata
exists. The custom App was described as exposing its public API "like any
other FSD slice", but App has no slices. And the alias paragraph presented
@/<layer>/* as something FSD requires, with Astro as an exception; it is a
tooling choice either way, so say that and add the rule the rest of the
skill uses, an entry only for a layer that exists.
2026-09-04 11:37:36 +09:00
Gaic4o 95ab132e95 docs: give layer-structure the same criterion the rest of the skill uses
The Features and Entities sections each offered one criterion, a count of
two consumers. This file is loaded on its own, so a reader of those
sections never sees the other two conditions, and the walkthrough keeps a
twice-used component in two pages for exactly the reason the count hides.
State the criterion in full in both sections and in what does not belong
in pages.

The features example gave one auth slice two use cases, a login form and a
register form with an api function each, in a file that elsewhere says
features are split by use case. Splitting into auth and register keeps the
slice name that SKILL.md and auth-and-api.md both use, and shows the split
rather than describing it.

Slice groups picked up two rules they should not own. Siblings needing
each other were sent one layer down, which for entities means domain logic
in shared; cross-import-patterns.md decides that, and grouping changes
nothing about it. Reusable code in a group was sent to shared without
qualification, where the following sentence had already carved out domain
logic.

Grouping advice turned on whether two or three slices would end up in the
group. Say what the number stood for, which is whether the group makes the
layer easier to scan.

The alias block lists six layers including widgets. A project reads it as
a starting config and creates folders for all six, against Section 1's
"do not create empty layer folders". Say to add an entry per layer that
exists.

The page composition example imported an entity as a namespace object and
rendered Product.Card. Every other example in the skill exports named
components, so that one line reads as a recommended entity public API.
2026-09-04 11:33:58 +09:00
Gaic4o 42fab75a0f docs: keep the migration guide from reading as a folder-move script
An agent runs this file as a procedure, so its flat sentences carry more
force here than anywhere else in the skill. Several of them decided
placement on a count or a subject.

Step 1 sorted flagged slices by "reused in 2+ places", and Step 3 kept
them extracted on the same count. Section 1 asks for three conditions, and
Snapshot 1 of the walkthrough keeps a twice-used component in its two
pages because the copies want to differ. Give the audit that third
outcome.

Step 6 sent business nouns to Entities and user actions to Features. That
is the classification the rest of the skill argues against, and the
official step hedges: such a slice *can* move. Say what earns each layer
and keep the hedge.

Step 5 said thunks become the api segment. state-management.md draws a
finer line, and cites this step for half of it: a thunk carrying its own
request belongs in api, and what is left after the request moves out
belongs with the reducer. Two references answering the same question
differently is worse than either answer.

Part 2 Step 2 fills shared/ with everything that does not import pages.
That is a staging move Step 4 undoes, but read on its own it is a
placement rule that would park business logic in Shared. Say which it is.

Steiger's insignificant-slice also flags slices with no references, says
to merge into the layer above rather than into a page, and exempts pages
themselves and slices used only from app. The bullet claimed none of that.

Two smaller ones: Step 2 offered a widget as an inlining target without
saying an existing one, and Step 7 opened by calling modules/ similar to
the Features layer before listing the five places its contents actually
go.
2026-09-04 11:30:51 +09:00
Gaic4o c349702b5e docs: decide the layer by ownership before reaching for Redux or a query key
The file opened by sorting nouns into Entities and verbs into Features,
which is the classification the rest of the skill argues against. Lead
with ownership instead: find the owning slice with Section 2, then put
the Redux code in that slice's model/ segment. The official Step 6 line
follows, with the hedging and the "already reused" precondition it has in
the source, and the features example now states that precondition rather
than opening with a features/ path.

Three corrections underneath it:

The features example said it "consumes the entity's reducer". It imports
setCompleted, an action creator, through the entity's public API. Left
alone, a sentence like that teaches an agent to reach for the reducer.

"The entire Redux slice lives in a single domain-named file" overreached.
Rule 4-4 bans reducers.ts/selectors.ts/thunks.ts, not a second file; a
model that grows can split by domain concern.

The useMutationState example put its save indicator in widgets/, the
layer SKILL.md discourages. Put it in app/ and note the page-local case.

Two upstream guides disagree about entity-local requests, and the file was
quietly carrying the looser one. The React Query guide calls a per-entity
split cleanest once a project has entities and shows CRUD files under
entities/*/api/; excessive-entities excludes CRUD from entities. Question
2 in auth-and-api.md already resolved this for the skill, so gate Option 3
on domain rules rather than on an entities folder existing, and record the
divergence so it does not get "fixed" back.

Mutations: the official text says mixing them with queries is not
recommended. Ours said "Do not". Match the source.
2026-09-04 11:27:20 +09:00
Gaic4o e23889c2e5 docs: say that the walkthrough adds no rules of its own
The file is the one reference built entirely out of worked examples, and
an example is the thing a model is most likely to promote into a rule.
Its four trees are one shop with one sale rule and one cart action; read
as law they would say entities arrive at the third page.

The opening already credits SKILL.md for each decision. Say the other
half too: nothing here is a placement rule that SKILL.md does not carry.

No precedence clause, because a conflict between the two is a defect to
fix rather than one to rank, which is what the eval cases are for.
2026-09-04 11:22:29 +09:00
Gaic4o dc7f32f925 docs: gate the placement table and 5-1 on the conditions, not a consumer count
Section 1 asks for three conditions before extracting. Two passages
answered the question on a consumer count alone, and both read as
triggers rather than gates.

5-1 said to extract the shared model to entities/user/ "when the same
user data is being consumed by another page". Read literally, a second
page reading a response type creates an entity, which the request
placement rule, excessive-entities, and Snapshot 1 of the walkthrough all
refuse. Require the Section 1 conditions alongside the second consumer,
and say that a shared response type is not one of these cases.

The placement table's right column is headed "Confirmed multi-use" and
hands back a path, so an agent with two ProductCards lands on
entities/product/ui. The walkthrough keeps exactly that case in its two
pages because the copies keep drifting apart. Note under the table what
the column assumes; the ProductCard row is not the only one that reads
this way.

The layer overview stays as it is: "only when used in 2+ places" is a
gate, not a trigger, and reads correctly.
2026-09-04 11:22:18 +09:00
Gaic4o 164cdb756d docs: state the extraction condition by change reason, not by usage
The second of the three conditions read "the usages do not always change
together". Under the plain reading, that is the opposite of how the skill
applies it. The walkthrough refuses to extract two ProductCards because
they will "keep drifting apart for their own reasons", meaning the usages
do not change together; it then extracts the sale rule because the copies
"must agree", meaning they do. Both answers are right and only one of
them matches the condition as written.

Elsewhere the skill uses the plain reading and the opposite direction:
Strategy A merges two slices that always change together. So the phrase
carried two meanings a few hundred lines apart.

The condition is really about the code having a change axis of its own.
A page-local card changes when its page changes; the sale rule changes
when marketing redefines a sale, whichever page is on screen. Say that,
and the two snapshots stop contradicting the rule they cite. The
diverged-rule eval case already argued it this way.

Reword it in SKILL.md and in the walkthrough that quotes it, so the two
keep stating the rule the same way.
2026-09-04 11:19:28 +09:00
Gaic4o c43d4088c7 docs: let the walkthrough show the second copy it later argues about
Snapshot 2 turned on a copy of the sale rule in the search page that no
earlier snapshot had shown. It asserted the copy in passing ("copied the
old rule when it copied the card") while the Snapshot 1 tree held only a
card and a page component. The moment the whole file is built around was
the one moment left off the page.

Put `model/is-on-sale.ts` in the Snapshot 1 tree with a paragraph saying
why a second copy of a two-line rule is still not a boundary, and let
Snapshot 2 point back at it. The card came from the home page and the
rule from the product page, so say so rather than implying one move. The
heading drops "nothing new appears", which the added file would have made
false; the layer count is what stays flat.

Snapshot 3 opened the same way, claiming an "Add to cart" button had been
there since Snapshot 0, whose tree shows none and is declared complete.
Give the action a moment between Snapshots 2 and 3 instead of complicating
the first tree, which is meant to stay small.

The intro said two of the four moments create nothing. Only Snapshot 1
does; Snapshot 0 opens three layers, as the table at the bottom already
showed. Say that not every product change earns a layer, which is the
point the sentence was reaching for.
2026-09-04 11:18:59 +09:00