Slice isolation (TWA0009)
Slices are independently removable vertical units. Folders organize humans; the
namespace under SliceRoot is the law. Analyzer TWA0009 is the reactive safety net;
this skill is for correct first placement so greenfield work does not couple product
areas by accident.
Enforcement ships in TimeWarp.Architecture.Analyzers (and monorepo ProjectReference). Do not
re-interpret rules here — mirror the analyzer.
Detection — when to invoke
| Signal |
How to find it |
Product area under features/ |
web-spa/features/…, web-contracts/features/… |
| Namespace under SliceRoot |
…Features.<Id> (or nested …Features.Admin.Roles) |
TWA0009 / CrossSliceReference |
diagnostic text or attribute on a type |
| “Where does this page/state live?” |
any new product capability before scaffolding |
Terms
| Term |
Meaning |
| Slice |
Removable vertical capability; the isolation unit |
| Feature |
Informal product language only — not the analyzer’s unit name |
| Module |
IModule / host DI composition — not a product slice |
Prefer saying slice in diagnostics, opt-outs, and agent prose. Namespace path may still use
…Features.* (familiar VSA / contracts layout).
SliceRoot
| Setting |
Value |
| Default |
{RootNamespace}.Features (from MSBuild RootNamespace) |
| Override |
MSBuild property TimeWarpSliceRoot (must be CompilerVisibleProperty) |
Slice id = path under SliceRoot after stripping structural suffixes Pages, Components,
Application. Nested ids are the full path:
| Namespace |
Slice id / tier |
…Features |
Substrate (bare root) |
…Features.Applications |
Platform id Applications |
…Features.Counters |
Product id Counters |
…Features.Counters.Pages |
Product id Counters (suffix stripped) |
…Features.Admin.Roles |
Product id Admin.Roles |
…Components, layouts, app root types |
Outside SliceRoot |
Folders usually mirror slices (features/counter/ ↔ …Features.Counters) but moving a file
does not change legal dependencies — only the namespace does.
Tiers (dependency rules)
| Tier |
Example |
May reference |
| Outside |
…Components, shell, layouts |
Anything (composition free) |
| Substrate |
bare …Features (BaseComponent, shared base types) |
Outside, substrate, platform — not product without opt-out |
| Platform |
…Features.Applications |
Outside, substrate, platform — not product without opt-out |
| Product |
…Features.Counters, …Features.Admin.Roles |
Outside, substrate, platform, own product id, contracts (other assembly). Not another product id without opt-out |
Forbidden without opt-out (TWA0009):
- product A → product B (A ≠ B)
- platform → product
- substrate → product
Free by design:
- product → platform (
Applications) — e.g. CounterPage → ApplicationState
- any code → other assemblies (contracts are sharing-by-design)
- Outside → product (nav shell, shared components)
Placement matrix
| Artifact |
Where |
| Page, state, actions, slice-local UI |
Inside the product slice namespace |
App chrome / shell / empty MainLayout |
Outside SliceRoot (e.g. …Components) — see blazor-layout |
| Shared UI used by multiple slices |
components/ → namespace outside SliceRoot |
| Shared API shapes |
Contracts project (other assembly; free under TWA0009) |
| Host-wide modal / menu / branding state |
Platform Applications |
Living anchors (timewarp-architecture template):
| Role |
Path |
Namespace |
| Product |
web-spa/features/counter/ |
…Features.Counters |
| Nested product |
web-spa/features/admin/roles/ |
…Features.Admin.Roles |
| Platform |
web-spa/features/application/ |
…Features.Applications |
| Substrate |
web-spa/features/base/base-component.cs |
…Features |
| Outside shell |
web-spa/components/TimeWarpPage.razor |
…Components |
Greenfield scaffold workflow
- Name the slice — plural, domain-oriented (
Clients, Admin.Roles).
- Folder — kebab path under the project’s
features/ tree (match sibling casing).
- Namespace —
{RootNamespace}.Features.{SliceId} (plural segments; nested with .).
- Colocate page, state, actions, and slice-private components in that namespace (pages are
not shared infrastructure — no grab-bag
…Pages for product UI).
- Contracts — same plural feature path under the contracts project (
web-api-contracts
skill). Other assembly → free across SPA slices.
- Wire nav / DI from Outside or platform — not from another product slice.
- Prefer share (Components / contracts) over
[CrossSliceReference].
- Build — fix TWA0009 by relocating code first; mute only for deliberate demo/integration edges.
Share vs opt-out
Prefer share
- Promote shared controls to
…Components.
- Share API shapes via contracts assembly.
- Depend product → platform without attributes when the dependency is real host state.
Opt-out — deliberate edges only
using TimeWarp.Foundation.Features;
[CrossSliceReference(typeof(CounterState), "Living style guide exercises the counter throw-exception pipeline.")]
partial class StyleGuidePage;
Rules:
- Attribute on the referencing type (
AllowMultiple = true for multiple foreign slices).
typeof(T) maps to the target product slice; only edges into that slice are suppressed.
- Reason must be non-empty (human paperwork).
- Partial-safe (semantic attributes).
- Type:
TimeWarp.Foundation.Features.CrossSliceReferenceAttribute (package
TimeWarp.Foundation.Contracts / monorepo foundation-contracts).
Living examples:
- Style guide → Counters:
web-spa/features/style-guide/pages/StyleGuidePage.razor.cs
- Auth pipeline deliberate edges: authentication code-behinds with multiple
[CrossSliceReference(...)] when needed
Limits agents must know
| Limit |
Implication |
| Same assembly only |
Contracts / NuGet metadata types never trip TWA0009 |
| Hand-written C# |
GeneratedCodeAnalysisFlags.None — pure .razor without .razor.cs is not scanned; put deliberate edges on code-behind |
| Folders can lie |
Namespace still owns the type |
| Structural suffixes |
…Counters.Pages is still product id Counters |
Good / bad examples
| Bad |
Why |
Fix |
…Features.Counters type uses WeatherForecastsState |
product → product |
Share via Components/contracts, or reasoned [CrossSliceReference] |
Product page in grab-bag …Pages outside any slice |
not removable with the capability |
Namespace under …Features.<Id> |
Platform type reaches into Counters |
platform ↛ product |
Invert dependency (product → platform) or rare opt-out on platform type |
| Mute TWA0009 globally / without reason |
hides real coupling |
Relocate or scoped opt-out |
| Good |
Why |
CounterPage uses ApplicationState |
product → platform free |
TimeWarpPage in …Components |
Outside composition free |
Admin.Roles only uses own types + contracts + platform |
nested product isolation |
Agent checklist
Related skills and pointers
web-api-contracts — contract placement; contracts assemblies are free under TWA0009; still use plural …Features.* aligned with SPA product slices
blazor-layout — empty layout + shell; chrome outside SliceRoot
blazor-css-strategy — shell/component styling only
agent-context-regions — Purpose/Design on new files (TWA0004)
- AGENTS.md — TWA diagnostic table (row TWA0009)
- HowToRemoveDemoFeatures — delete Counter/EventStream demo slices
- Analyzer (source of truth):
source/analyzers/timewarp-architecture-convention-analyzers/slice-isolation-analyzer.cs
- Opt-out attribute:
source/foundation/foundation-contracts/base/cross-slice-reference-attribute.cs
1---2name: slice-isolation3description: **TIMEWARP SKILL** — product slice placement and TWA0009 isolation (SliceRoot, namespaces, platform Applications, Components/contracts sharing, CrossSliceReference opt-out). Invoke before scaffolding a new feature/slice/page or when fixing TWA0009 / cross-slice references. WHEN: "Add a new clients feature page", "Where does this state live?", "TWA0009 slice references another product slice", "CrossSliceReference", "new product area under features/", greenfield slice scaffolding.4---56# Slice isolation (TWA0009)78Slices are **independently removable vertical units**. Folders organize humans; the9**namespace under SliceRoot is the law**. Analyzer **TWA0009** is the reactive safety net;10this skill is for **correct first placement** so greenfield work does not couple product11areas by accident.1213Enforcement ships in `TimeWarp.Architecture.Analyzers` (and monorepo ProjectReference). Do not14re-interpret rules here — mirror the analyzer.1516## Detection — when to invoke1718| Signal | How to find it |19|--------|----------------|20| Product area under `features/` | `web-spa/features/…`, `web-contracts/features/…` |21| Namespace under SliceRoot | `…Features.<Id>` (or nested `…Features.Admin.Roles`) |22| TWA0009 / `CrossSliceReference` | diagnostic text or attribute on a type |23| “Where does this page/state live?” | any new product capability before scaffolding |2425## Terms2627| Term | Meaning |28|------|---------|29| **Slice** | Removable vertical capability; the isolation unit |30| **Feature** | Informal product language only — not the analyzer’s unit name |31| **Module** | `IModule` / host DI composition — **not** a product slice |3233Prefer saying **slice** in diagnostics, opt-outs, and agent prose. Namespace path may still use34`…Features.*` (familiar VSA / contracts layout).3536## SliceRoot3738| Setting | Value |39|---------|--------|40| Default | `{RootNamespace}.Features` (from MSBuild `RootNamespace`) |41| Override | MSBuild property `TimeWarpSliceRoot` (must be `CompilerVisibleProperty`) |4243**Slice id** = path under SliceRoot after stripping structural suffixes `Pages`, `Components`,44`Application`. Nested ids are the **full path**:4546| Namespace | Slice id / tier |47|-----------|-----------------|48| `…Features` | **Substrate** (bare root) |49| `…Features.Applications` | **Platform** id `Applications` |50| `…Features.Counters` | **Product** id `Counters` |51| `…Features.Counters.Pages` | **Product** id `Counters` (suffix stripped) |52| `…Features.Admin.Roles` | **Product** id `Admin.Roles` |53| `…Components`, layouts, app root types | **Outside** SliceRoot |5455Folders usually mirror slices (`features/counter/` ↔ `…Features.Counters`) but **moving a file56does not change legal dependencies** — only the namespace does.5758## Tiers (dependency rules)5960| Tier | Example | May reference |61|------|---------|---------------|62| **Outside** | `…Components`, shell, layouts | Anything (composition free) |63| **Substrate** | bare `…Features` (`BaseComponent`, shared base types) | Outside, substrate, platform — **not** product without opt-out |64| **Platform** | `…Features.Applications` | Outside, substrate, platform — **not** product without opt-out |65| **Product** | `…Features.Counters`, `…Features.Admin.Roles` | Outside, substrate, platform, **own** product id, contracts (other assembly). **Not** another product id without opt-out |6667**Forbidden without opt-out (TWA0009):**6869- product A → product B (A ≠ B)70- platform → product71- substrate → product7273**Free by design:**7475- product → platform (`Applications`) — e.g. `CounterPage` → `ApplicationState`76- any code → **other assemblies** (contracts are sharing-by-design)77- Outside → product (nav shell, shared components)7879## Placement matrix8081| Artifact | Where |82|----------|--------|83| Page, state, actions, slice-local UI | **Inside** the product slice namespace |84| App chrome / shell / empty `MainLayout` | **Outside** SliceRoot (e.g. `…Components`) — see `blazor-layout` |85| Shared UI used by multiple slices | `components/` → namespace outside SliceRoot |86| Shared API shapes | **Contracts** project (other assembly; free under TWA0009) |87| Host-wide modal / menu / branding state | Platform `Applications` |8889Living anchors (timewarp-architecture template):9091| Role | Path | Namespace |92|------|------|-----------|93| Product | `web-spa/features/counter/` | `…Features.Counters` |94| Nested product | `web-spa/features/admin/roles/` | `…Features.Admin.Roles` |95| Platform | `web-spa/features/application/` | `…Features.Applications` |96| Substrate | `web-spa/features/base/base-component.cs` | `…Features` |97| Outside shell | `web-spa/components/TimeWarpPage.razor` | `…Components` |9899## Greenfield scaffold workflow1001011. **Name the slice** — plural, domain-oriented (`Clients`, `Admin.Roles`).1022. **Folder** — kebab path under the project’s `features/` tree (match sibling casing).1033. **Namespace** — `{RootNamespace}.Features.{SliceId}` (plural segments; nested with `.`).1044. **Colocate** page, state, actions, and slice-private components in that namespace (pages are105 **not** shared infrastructure — no grab-bag `…Pages` for product UI).1065. **Contracts** — same plural feature path under the contracts project (`web-api-contracts`107 skill). Other assembly → free across SPA slices.1086. **Wire nav / DI** from Outside or platform — not from another product slice.1097. **Prefer share** (Components / contracts) over `[CrossSliceReference]`.1108. **Build** — fix TWA0009 by relocating code first; mute only for deliberate demo/integration edges.111112## Share vs opt-out113114### Prefer share115116- Promote shared controls to `…Components`.117- Share API shapes via contracts assembly.118- Depend product → platform without attributes when the dependency is real host state.119120### Opt-out — deliberate edges only121122```csharp123using TimeWarp.Foundation.Features;124125[CrossSliceReference(typeof(CounterState), "Living style guide exercises the counter throw-exception pipeline.")]126partial class StyleGuidePage;127```128129Rules:130131- Attribute on the **referencing** type (`AllowMultiple = true` for multiple foreign slices).132- `typeof(T)` maps to the **target product slice**; only edges into that slice are suppressed.133- Reason must be non-empty (human paperwork).134- Partial-safe (semantic attributes).135- Type: `TimeWarp.Foundation.Features.CrossSliceReferenceAttribute` (package136 `TimeWarp.Foundation.Contracts` / monorepo foundation-contracts).137138Living examples:139140- Style guide → Counters: `web-spa/features/style-guide/pages/StyleGuidePage.razor.cs`141- Auth pipeline deliberate edges: authentication code-behinds with multiple142 `[CrossSliceReference(...)]` when needed143144## Limits agents must know145146| Limit | Implication |147|-------|-------------|148| **Same assembly only** | Contracts / NuGet metadata types never trip TWA0009 |149| **Hand-written C#** | `GeneratedCodeAnalysisFlags.None` — pure `.razor` without `.razor.cs` is not scanned; put deliberate edges on code-behind |150| **Folders can lie** | Namespace still owns the type |151| **Structural suffixes** | `…Counters.Pages` is still product id `Counters` |152153## Good / bad examples154155| Bad | Why | Fix |156|-----|-----|-----|157| `…Features.Counters` type uses `WeatherForecastsState` | product → product | Share via Components/contracts, or reasoned `[CrossSliceReference]` |158| Product page in grab-bag `…Pages` outside any slice | not removable with the capability | Namespace under `…Features.<Id>` |159| Platform type reaches into `Counters` | platform ↛ product | Invert dependency (product → platform) or rare opt-out on platform type |160| Mute TWA0009 globally / without reason | hides real coupling | Relocate or scoped opt-out |161162| Good | Why |163|------|-----|164| `CounterPage` uses `ApplicationState` | product → platform free |165| `TimeWarpPage` in `…Components` | Outside composition free |166| `Admin.Roles` only uses own types + contracts + platform | nested product isolation |167168## Agent checklist169170- [ ] New capability has a slice id and matching `{Root}.Features.{Id}` namespace171- [ ] Page, state, and actions live in that namespace172- [ ] Shell/layout stay outside SliceRoot (or platform only when truly host state)173- [ ] Cross-slice data via Components or contracts first174- [ ] Any remaining edge has `[CrossSliceReference(typeof(T), "reason")]`175- [ ] No silent coupling or unexplained suppressions176177## Related skills and pointers178179- `web-api-contracts` — contract placement; **contracts assemblies are free** under TWA0009; still use plural `…Features.*` aligned with SPA product slices180- `blazor-layout` — empty layout + shell; chrome **outside** SliceRoot181- `blazor-css-strategy` — shell/component styling only182- `agent-context-regions` — Purpose/Design on new files (TWA0004)183- **AGENTS.md** — TWA diagnostic table (row TWA0009)184- **HowToRemoveDemoFeatures** — delete Counter/EventStream demo slices185- **Analyzer (source of truth):** `source/analyzers/timewarp-architecture-convention-analyzers/slice-isolation-analyzer.cs`186- **Opt-out attribute:** `source/foundation/foundation-contracts/base/cross-slice-reference-attribute.cs`