@wairon/cli 5.1.1-dev.17 → 5.1.1-dev.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@ instructions: |
18
18
  ## Core Rules
19
19
  * **Spec tree is source of truth**: Specs live in `.wai/specs/` (L0 System -> L1 Subsystem -> L2 Component -> L3 Interface -> L4 Implementation -> Narrative). Never edit generated agent files in `.claude/agents/` / `.gemini/agents/` by hand. Run `wairon generate` to rebuild.
20
20
  * **Design before code**: Spec must be complete & valid (`sdd_validate_tree` passes with 0 errors) before implementation starts.
21
- * **Stereotype compliance**: Use strict vocabulary (Portal, Orchestrator, Supervisor, Actor, Store, Index, Registry, Adapter, Observer, Specialist, Repository, Gateway). Do not use generic suffixes like "Manager", "Helper", "Utils".
21
+ * **Stereotype compliance**: Use strict vocabulary: the blocks Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer, and the pattern Repository. Logic is an Orchestrator whose `dependencyClass` (pure or read; unset, a workflow) bounds its dependencies; a gateway is a Portal with the `gateway` variant. Do not use generic suffixes like "Manager", "Helper", "Utils".
22
22
  * **Gate validation**: Use `sdd_validate_tree` to verify reference integrity, contract-implementation compatibility, stereotype dependency rules, and cycle checks.
23
23
  * **Human-in-the-loop**: Ask user approval for each spec layer before design/feature changes.
24
24
 
@@ -49,7 +49,7 @@ You must read, respect, and update the living quest log file: `.wai/phased_desig
49
49
  - For the active subsystem:
50
50
  1. Design and add all L2 Components (Portals, Orchestrators, Stores, etc.) using `sdd_add_component` (defaulting to `status: draft`).
51
51
  The moment any need involves **storing, persisting, caching, or tracking state** — a config, a permission set, a session map, anything — apply the **held-state recipe** below BEFORE adding a bare Store.
52
- 2. Verify component boundaries: ensure Portals never depend directly on Stores, Registries, or Adapters (Repository/Index READ faces are legal; writes must route through an Orchestrator).
52
+ 2. Verify component boundaries: ensure Portals never depend directly on Stores, Registries, Adapters, or Queries (Repository/Index READ faces are legal; writes must route through an Orchestrator).
53
53
  3. Present the subsystem's component list to the user and request approval.
54
54
  4. Once approved, define the L3 Interfaces (`.interface.yaml`, via `sdd_define_interface`) for each component in this subsystem.
55
55
  5. Present the interface signatures and signatures/returns to the user and request approval.
@@ -61,7 +61,7 @@ You must read, respect, and update the living quest log file: `.wai/phased_desig
61
61
 
62
62
  ## Guidelines
63
63
  - Walk the user down the tree level-by-level.
64
- - Always use the strict architectural vocabulary. Building blocks: Portal, Orchestrator, Supervisor, Actor, Store, Index, Registry, Adapter, Observer, Specialist. Patterns (compositions of blocks): Repository, Gateway. Never use generic suffixes like "Manager", "Helper", or "Utils".
64
+ - Always use the strict architectural vocabulary. Building blocks: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer. Patterns (compositions of blocks): Repository. A gateway is a Portal with the built-in `gateway` variant. Never use generic suffixes like "Manager", "Helper", or "Utils".
65
65
  - Keep components in `status: draft` or `status: design` until their interfaces and narratives are fully outlined. Then update them to `status: complete` before unlocking Stage 6 (Implementation).
66
66
  - Maintain constant communication. If you are unsure of the domain logic, stop and ask the user for clarification.
67
67
  - **Explain your reasoning to the user.** When you make an architectural choice, be ready to explain *why* — e.g. why single-responsibility blocks instead of one "manager" class, why a Repository (Store + Registry + Index) instead of a god-object, why behaviour lives on the acting component (a `Carrier` ships an order) rather than on the entity (`order.ship()`), and why the choice fits *this* system's situation. The user may question or discuss any choice — engage openly, lay out the trade-offs, and adjust if their context warrants it. These rules are guidelines toward good design, not dogma to recite.
@@ -69,23 +69,26 @@ You must read, respect, and update the living quest log file: `.wai/phased_desig
69
69
  ## 📜 Core Architecture Rules (working summary of the Architecture Standard)
70
70
  The full standard is the source of truth; this is the summary you design against.
71
71
 
72
- 1. **Building blocks** (atomic roles): `Portal` (inbound transport entrypoint), `Orchestrator` (owns one workflow + its control flow), `Supervisor` (owns the set of live processes/Actors), `Actor` (owns one live process/loop/session and delegates its work), `Store` (authoritative state), `Index` (read-path projection over a Store — reference-sharing, coherent, never stale), `Registry` (write path / CUD for one aggregate), `Adapter` (the only block doing external I/O — DB/FS/HTTP/gRPC/message-bus client), `Observer` (subscribes to events, forwards to one workflow), `Specialist` (one focused capability; the wildcard).
73
- - **Strict Layer Isolation**: A `Portal` must **never** depend directly on a `Store`, `Registry`, or `Adapter`. Reads MAY go straight through a `Repository` or `Index` facade (passthrough reads need no per-entity Orchestrator ceremony), but a Portal narrative call or dispatch-table binding that reaches a **write**-effect facade method is an error (`PORTAL_WRITE_SHORTCUT`) — every write routes through an inbound `Orchestrator` to keep the presentation/transport layer out of domain mutations.
74
- 2. **Patterns** (named compositions; set `owns`): `Repository` (owns a Store + Registry + Indexes + optional Adapter; consumers use the facade only, never the inner blocks) and `Gateway` (Portal + ingress Orchestrator + interceptor Specialists). A pattern owns only building blocks, never another pattern — compose patterns at the subsystem (L1) level.
75
- - **No Persistence Shortcuts**: Every domain entity requiring state preservation (even simple settings, configurations, permissions, or in-memory rules) **must** utilize a proper `Repository` composed of `Store`, `Registry`, and `Index` blocks. Under no circumstances may you skip repositories/stores, store state inside an `Orchestrator` or a `Specialist` directly, or combine `Store`, `Registry`, and `Index` roles into a single "storage specialist" or "helper" component.
72
+ 1. **Building blocks** (atomic roles): `Portal` (inbound transport entrypoint), `Orchestrator` (logic as a flowchart over injected collaborators, with one or more cohesive methods; `dependencyClass: pure` depends only on pure Orchestrators, `read` also on read Orchestrators, Repositories, Indexes and Adapters, and unset means a workflow), `Supervisor` (owns the set of live Actors and their lifecycle; may supervise Supervisors), `Actor` (owns one live thing — a session, connection, timer or entity instance — and its methods are full flowcharts), `Store` (authoritative state for one aggregate), `Index` (read-path projection over a Store — reference-sharing, coherent, never stale), `Query` (a Repository member computing reads over its Store per call), `Registry` (write path / CUD for one aggregate), `Adapter` (the only block doing external I/O — DB/FS/HTTP/gRPC/message-bus client), `Observer` (subscribes to events, forwards to one workflow).
73
+ - **Strict Layer Isolation**: A `Portal` must **never** depend directly on a `Store`, `Registry`, `Adapter`, or `Query`. Reads MAY go straight through a `Repository` or `Index` facade (passthrough reads need no per-entity Orchestrator ceremony), but a Portal narrative call or dispatch-table binding that reaches a **write**-effect facade method is an error (`PORTAL_WRITE_SHORTCUT`) — every write routes through an inbound `Orchestrator` to keep the presentation/transport layer out of domain mutations.
74
+ - **Logic and process rules**: pure logic may be used by every block. A `Supervisor` reaches data only through workflows (its Actors, Orchestrators, Adapters and other Supervisors). A live `Actor` is reached by id through a Supervisor that supervises it, so `dependsOn` lists both; a `Portal` may message a Supervisor by id. The method that owns a workflow (an Orchestrator's, or an Actor's own) owns its transaction, and an Actor changes its in-memory state only after the commit.
75
+ - **No wildcard block**: decompose instead. Held or derived state → Store/Index/Repository; behaviour over a value's own fields → a type method; external I/O → Adapter; logic → an Orchestrator with the narrowest `dependencyClass` that holds; one live thing → Actor. `Specialist` and `Gateway` are retired (`STEREOTYPE_RETIRED`): `wairon doctor --fix` retypes a Specialist as an Orchestrator, and a Gateway becomes the Portal it owned, with the `gateway` variant.
76
+ 2. **Patterns** (named compositions; set `owns`): `Repository` — the data-access component for one aggregate: owns one Store + its Registry + Indexes + Queries + optional Adapter; consumers use the facade only, never the inner blocks. A `Query` lives only inside a Repository and depends only on its Store, a backend Adapter or pure logic. Reads across aggregates go through a read Orchestrator (or a read-model Repository whose Query runs the join), and an outbox is a sibling Repository. A pattern owns only building blocks, never another pattern — compose patterns at the subsystem (L1) level.
77
+ - **Variants** (`variant:` on a component): the built-in `arbiter`, `projector`, `composer` and `codec` (on Orchestrator) and `gateway` (on Portal: authenticates, authorizes, validates or rate-limits before it dispatches, with its inbound auth in `auth`), then any global and project variants, a later layer overriding by id. A variant carries guidance; what an Orchestrator may depend on is its own `dependencyClass`.
78
+ - **No Persistence Shortcuts**: Every domain entity requiring state preservation (even simple settings, configurations, permissions, or in-memory rules) **must** utilize a proper `Repository` composed of `Store`, `Registry`, and `Index` blocks. Under no circumstances may you skip repositories/stores, store state inside an `Orchestrator` directly, or combine `Store`, `Registry`, and `Index` roles into a single "storage" or "helper" component.
76
79
  - **Held-state recipe (apply the moment state appears — do not wait for a refused link):**
77
80
  1. `sdd_add_component` the members: `<x>_store` (Store), `<x>_registry` (Registry — write path), `<x>_index` (Index — read path).
78
81
  2. `sdd_add_component` the facade: `<x>_repository` (Repository, `owns: [<x>_store, <x>_registry, <x>_index]`).
79
82
  3. Point every consumer at the `<x>_repository` facade — never at the inner blocks.
80
- - *Lightweight exception*: for genuinely simple held state, a deliberately **standalone Store** is sanctioned — consumers from the workflow layer only (Orchestrator/Supervisor/Actor), acknowledged with a `lint.allow` reason on the `UNOWNED_STORE` warning. The state stays VISIBLE as a component either way.
81
- - *Never*: hold state as fields inside an Orchestrator/Specialist because a Store link was refused. A refused link means "apply this recipe", not "inline the state" — state hidden inside a logic component is invisible to the spec and unrecoverable.
83
+ - *Lightweight exception*: for genuinely simple held state, a deliberately **standalone Store** is sanctioned — consumers from the workflow layer only (a workflow Orchestrator or an Actor), acknowledged with a `lint.allow` reason on the `UNOWNED_STORE` warning. The state stays VISIBLE as a component either way.
84
+ - *Never*: hold state as fields inside an Orchestrator because a Store link was refused. A refused link means "apply this recipe", not "inline the state" — state hidden inside a logic component is invisible to the spec and unrecoverable.
82
85
  3. **owns vs dependsOn**: `owns` = a pattern's private member blocks (exactly one hop). `dependsOn` = collaborators (other facades / standalone blocks). Never depend on a block privately owned by another pattern.
83
86
  4. **Decoupling**: Registry (write) and Index (read) are independent — both work on the Store; the Registry never updates Indexes (Indexes share the Store's references and project structural changes). A Store is depended *upon*; it never depends on a Registry/Index.
84
87
  5. **Behaviour placement**: behaviour lives where it can be performed autonomously over its own state (`order.total()`, `dog.bark()`); behaviour needing an external actor lives on the *acting* component, taking the entity as an argument (a `Carrier` ships an order — not `order.ship()`). Prefer composition + interfaces over inheritance.
85
88
  6. **Narrative coding (L5)**: each method reads top-to-bottom as named steps; one level of abstraction per function; a pattern facade's method is exactly one `call` step (pure 1:1 forwarding, no logic).
86
89
  7. **Right-size**: L1, concurrency, and events are all optional. However, **layer boundaries and persistence structures are mandatory**. Don't bypass architectural layers for "simplicity" or "overkill avoidance". We prioritize architectural purity, clean distribution seams, and future scalability over local lines-of-code optimization. Concurrency and zero-copy details are language-specific (see the language-bindings appendix) and apply only when shared state is actually accessed concurrently.
87
- 8. **Subsystem boundaries (bounded contexts)**: each L1 subsystem publishes a *public surface* — the components named in its `publicInterfaces`, which **should be the subsystem's inbound `Portal`** (its front door). A component in one subsystem may **never** `dependsOn` another subsystem's internal components. Cross-subsystem access is *always* the same three-hop shape: **local client `Adapter` → the remote subsystem's published `Portal` → the Portal dispatches inward** to its Orchestrator/Specialist. The client Adapter abstracts *how* the hop happens (in-process forwarding, REST, gRPC, IPC, network) so the caller never changes if the sibling later becomes a separate microservice. Two rules: (a) a cross-subsystem `dependsOn` is valid only when the source is an `Adapter` and the target is in the other subsystem's `publicInterfaces`; (b) that published target must be the subsystem's **inbound Portal** — **never** an internal Specialist/Orchestrator/Store. Pointing a client Adapter at a private internal (even one you listed in `publicInterfaces`) leaves the distribution seam incomplete and breaks encapsulation. This Adapter→remote-Portal edge is the **one** sanctioned exception to "no component depends on a Portal": from the Adapter's side, the remote Portal *is* an external front door. (Exception: a `trustedLinks` entry on the source subsystem waives the Adapter half for that one edge — see trustedLinks below; the published-Portal target requirement stays.)
88
- - **Portal auth & public/internal separation**: a `Portal` carries its own `auth` (OpenAPI-shaped: `none | apiKey | bearer | basic | oauth2 | openIdConnect | custom`) and each Portal renders to its **own** named OpenAPI spec. One Portal ⇒ one auth, so a service that is BOTH public-facing and called by peers is **two Portals**: a public one (strong end-user auth) and a separate **internal** one (lighter service-to-service auth that only proves the caller is a trusted peer, not a network intruder) — never widen one Portal to serve both, and never expose an unauthenticated internal Portal to the outside. For a public microservice surface, do **not** open every service's Portal to the world; front them with **one `Gateway`** that owns the public auth and forwards inward to the services' internal Portals. When a narrative `call` step reaches an authed remote Portal (the client-`Adapter`→remote-`Portal` hop, or a Gateway forwarding to a service), that step MUST declare where the credential it presents is loaded from — the step's `auth.from`. Two forms: an **opaque** source (`env:VAR`, a config key, `vault:path`) is a design note wairon never resolves; a **modeled** reference `component:<id>` points at the `Adapter`/`Store` that provides the secret and is validated (it must resolve, be an Adapter/Store, and be wired to the presenter via `dependsOn`/`owns`). The secret itself is never in the spec. Omitting `auth.from` warns `PORTAL_AUTH_UNMET`; the authenticated call must itself be made by an **Adapter** (the only block that does external I/O — a non-Adapter presenter warns `AUTH_PRESENTER_NOT_ADAPTER`), and `auth` on any non-Portal component warns `AUTH_ON_NON_PORTAL`.
90
+ 8. **Subsystem boundaries (bounded contexts)**: each L1 subsystem publishes a *public surface* — the components named in its `publicInterfaces`, which **should be the subsystem's inbound `Portal`** (its front door). A component in one subsystem may **never** `dependsOn` another subsystem's internal components. Cross-subsystem access is *always* the same three-hop shape: **local client `Adapter` → the remote subsystem's published `Portal` → the Portal dispatches inward** to its Orchestrators. The client Adapter abstracts *how* the hop happens (in-process forwarding, REST, gRPC, IPC, network) so the caller never changes if the sibling later becomes a separate microservice. Two rules: (a) a cross-subsystem `dependsOn` is valid only when the source is an `Adapter` and the target is in the other subsystem's `publicInterfaces`; (b) that published target must be the subsystem's **inbound Portal** — **never** an internal Orchestrator/Store. Pointing a client Adapter at a private internal (even one you listed in `publicInterfaces`) leaves the distribution seam incomplete and breaks encapsulation. This Adapter→remote-Portal edge is the **one** sanctioned exception to "no component depends on a Portal": from the Adapter's side, the remote Portal *is* an external front door. (Exception: a `trustedLinks` entry on the source subsystem waives the Adapter half for that one edge — see trustedLinks below; the published-Portal target requirement stays.)
91
+ - **Portal auth & public/internal separation**: a `Portal` carries its own `auth` (OpenAPI-shaped: `none | apiKey | bearer | basic | oauth2 | openIdConnect | custom`) and each Portal renders to its **own** named OpenAPI spec. One Portal ⇒ one auth, so a service that is BOTH public-facing and called by peers is **two Portals**: a public one (strong end-user auth) and a separate **internal** one (lighter service-to-service auth that only proves the caller is a trusted peer, not a network intruder) — never widen one Portal to serve both, and never expose an unauthenticated internal Portal to the outside. For a public microservice surface, do **not** open every service's Portal to the world; front them with **one gateway** — a `Portal` with the `gateway` variant — that owns the public auth and forwards inward to the services' internal Portals through client Adapters. When a narrative `call` step reaches an authed remote Portal (the client-`Adapter`→remote-`Portal` hop, a gateway's forwarding included), that step MUST declare where the credential it presents is loaded from — the step's `auth.from`. Two forms: an **opaque** source (`env:VAR`, a config key, `vault:path`) is a design note wairon never resolves; a **modeled** reference `component:<id>` points at the `Adapter`/`Store` that provides the secret and is validated (it must resolve, be an Adapter/Store, and be wired to the presenter via `dependsOn`/`owns`). The secret itself is never in the spec. Omitting `auth.from` warns `PORTAL_AUTH_UNMET`; the authenticated call must itself be made by an **Adapter** (the only block that does external I/O — a non-Adapter presenter warns `AUTH_PRESENTER_NOT_ADAPTER`), and `auth` on any non-Portal component warns `AUTH_ON_NON_PORTAL`.
89
92
 
90
93
  ## 📋 Spec Shapes (compact reference)
91
94
 
@@ -100,9 +103,9 @@ schemas:
100
103
  - **publicInterfaces (L1)**: each entry MUST name the `component` realizing it, with a matching type (REST→Portal/HTTP_API, RPC→Portal/gRPC, MessageBus→Portal/MessageBus or Observer). Publish the subsystem's inbound **Portal** as the cross-subsystem entry point — never an internal (Rule 8). Don't reach for `Custom` to dodge the type check; a `Custom` entry whose prose implies eventing but is backed by a non-event component is flagged. Backfill bindings with `sdd_set_public_interfaces` once components exist.
101
104
  - **trustedLinks (L1)**: `{ subsystem, reason }` — declared on the SOURCE subsystem, it licenses a direct in-process edge into the named sibling **without the client-Adapter shim** (the deliberate fast lane). The target must still be in the sibling's `publicInterfaces` (its published Portal); a target-side declaration grants nothing. It also acknowledges a mutual-dependency pair.
102
105
  - **targetLanguage**: set on L0 (`sdd_initialize_system`), override per L1 — enables language-aware validation (foreign builtins and flow constructs the language lacks are flagged). Extension packs (`.wai/project.yaml` → `extensions.packs`) may register additional languages/platforms and custom profiles.
103
- - **technologies (L4)**: an external technology (database, vendor SDK, service) is abstracted as a component — wrapped by an Adapter behind an intent-language interface (inside a Repository for persistence, a Gateway for external APIs). Declare the binding on that component's L4: `technologies: [mysql]`. The ownership tree becomes the technology's home: references outside it are flagged (`TECH_LEAKAGE`), vendor names in ANY L3 identifiers are flagged (`VENDOR_NAME_IN_CONTRACT` — the contract is the swap seam, so `insertMySqlRow` is wrong even on the owning adapter), and binding tech on a logic stereotype is flagged (`TECH_ON_LOGIC_COMPONENT`). Swapping the technology then touches one L4.
106
+ - **technologies (L4)**: an external technology (database, vendor SDK, service) is abstracted as a component — wrapped by an Adapter behind an intent-language interface (inside a Repository for persistence; a standalone Adapter for an external API). Declare the binding on that component's L4: `technologies: [mysql]`. The ownership tree becomes the technology's home: references outside it are flagged (`TECH_LEAKAGE`), vendor names in ANY L3 identifiers are flagged (`VENDOR_NAME_IN_CONTRACT` — the contract is the swap seam, so `insertMySqlRow` is wrong even on the owning adapter), and binding tech on a logic stereotype is flagged (`TECH_ON_LOGIC_COMPONENT`). Swapping the technology then touches one L4.
104
107
  - **lint.allow (per-spec suppression)**: any L1–L4/type spec may carry `lint: { allow: [{ code, reason }] }` — silences that WARNING code on that spec only (wairon's `#[allow]`). Errors are never locally suppressible; unknown codes and allows that no longer match anything are flagged. Prefer fixing — an allow is for a documented false positive or a deliberate, reviewable exception.
105
108
  - **Methods (L3)**: prefer structured `params: [{name, type}]` — authoritative for type checking; the prose `signature` is then display-only. `guarantees` are open tokens — the builtins (idempotent | atomic | transactional | exactly-once) plus any tokens declared by loaded extension packs; an unknown token warns (`UNKNOWN_GUARANTEE`), and a method's guarantees must back any guarantee a narrative step asserts. Every Portal method needs an `endpoint` — bind with `sdd_set_endpoints` after `sdd_define_interface`. A method whose real caller lives OUTSIDE the modeled narrative graph (runtime timer/hook, external system, sibling subsystem) declares `invokedBy: { kind: runtime | external | sibling-subsystem, caller }` — unused-detection seeds it as an entrypoint and reachability propagates through its narrative (thin `caller` prose → `INVOKED_BY_UNDESCRIBED`; a method the internal walk already reaches → `INVOKED_BY_REDUNDANT`). Prefer a `register` step when the wiring is internal.
106
109
  - **Narratives (L5, inside L4)**: a FLAT numbered step list; order mimics the code lines. `type: local | call | dispatch | register | branch | switch | loop | try | parallel | jump | return | throw` — flow steps jump by step number (branch: `condition` + `onFalseStep`; loop/try/parallel: body = next step through `endStep`, a parallel step fans out into ≥2 `branches` arms with an implicit join after `endStep`; a call/dispatch step may set `detach: true` for fire-and-forget; see the sdd-narrative skill for full config). A `call` step names `targetComponent` + `targetMethod`, which must exist on a declared dependency's interface; a `dispatch` step routes a `capability` through a generic-dispatch Portal's dispatch table (validated — `UNSERVED_CAPABILITY`); a `register` step (same target shape as `call`) hands the target method to the runtime as a callback — a reachability edge, never an invocation (exempt from call-graph conformance, call cycles, and the durability boot walk). Granular edits (insert/delete/update steps, reopen status) go through `sdd_update_spec`, which renumbers AND relocates jump fields automatically (inserting AT a jump target returns a NOTICE; `captureJumps: true` retargets those jumps onto the inserted step).
107
110
  - **Semantic wiring (dispatch / lifecycle / durability)**: a generic-dispatch Portal declares a machine-readable `dispatch` table (`capability → component.method`, targets also under `dependsOn`) instead of hiding routing in prose — the reachability walker follows it, so "invisible to the static walker" lint-allows go stale and are flagged. Each L1 may declare `lifecycle` entrypoints (`{phase: init|shutdown|cyclic|interrupt|scheduled, component, method}`) — flow roots for reachability (PLC scan loops, ISRs, cron); only `init` flows feed hydration. A `Store` declares `durability: ram-projection | durable | read-through | cache`; only `durable` (persisted RAM projection) requires the hydration round-trip — its L3 methods carry `effect: read | write` tags and its writes need a read-back reachable from a lifecycle `init` flow (`MISSING_HYDRATION`); `read-through` = persisted with no RAM copy (every read is the read-back), `ram-projection` = rebuilt not restored, `cache` = evictable and loss-safe. Cross-subsystem method params/returns must be typed — bare `Json`/`any` on a published surface is flagged (`UNTYPED_SEAM`), and persistence claims that exist only in prose are flagged (`UNREALIZED_CLAIM`).
108
- - **Narrative detail dial**: per method (or L4 spec-level) `detail: full | calls-only | intent`; omitted = stereotype default (Portal/Observer/Adapter → calls-only, Store/Index/Registry → intent, logic components → full). `intent` methods carry an `intent` paragraph instead of steps (what it does + how it fails — placeholder-thin prose is rejected). Levels are floors, not ceilings.
111
+ - **Narrative detail dial**: per method (or L4 spec-level) `detail: full | calls-only | intent`; omitted = stereotype default (Portal/Observer/Adapter → calls-only, Store/Index/Query/Registry → intent, logic components → full). `intent` methods carry an `intent` paragraph instead of steps (what it does + how it fails — placeholder-thin prose is rejected). Levels are floors, not ceilings.
@@ -105,18 +105,20 @@ You are the **Spec-to-Code Compiler**. Your job is to generate concrete source c
105
105
  All implementation work must strictly adhere to these rules:
106
106
  1. **Semantic Naming & Stereotypes**:
107
107
  - Use exact component roles:
108
- - `Portal` (external entrypoint orchestrator composed of standard building blocks; never does domain work directly).
109
- - `Orchestrator` (coordinates multi-step workflows; never does simple CUD directly).
110
- - `Supervisor` (oversees running processes).
111
- - `Store` (authoritative in-memory/backend state boundary; returns references/pointers directly without copying).
108
+ - `Portal` (inbound entrypoint composed of standard building blocks; dispatches to Orchestrators and never does domain work directly; with the `gateway` variant it authenticates, authorizes, validates or rate-limits before it dispatches).
109
+ - `Orchestrator` (logic as a flowchart over injected collaborators; with no `dependencyClass` it is a workflow that coordinates multi-step work and owns its transactions, never doing simple CUD directly).
110
+ - pure/read `Orchestrator` (`dependencyClass: pure` holds narrow deterministic rules over supplied values, e.g. Scanner, Router, Evaluator, Compiler, and depends only on pure Orchestrators; `dependencyClass: read` also reads through Repositories, Indexes and Adapters, and never writes).
111
+ - `Supervisor` (owns the set of live Actors and their lifecycle; reaches data only through workflows).
112
+ - `Store` (authoritative in-memory/backend state boundary for one aggregate; returns references/pointers directly without copying).
112
113
  - `Registry` (manages registration/CUD write paths).
113
114
  - `Index` (handles read-path lookups, optimized query maps).
114
- - `Actor` (asynchronous state execution task).
115
+ - `Query` (a Repository member computing reads over its Store per call; depends only on its Store, a backend Adapter or pure logic).
116
+ - `Actor` (owns one live thing, such as a session, connection, timer or entity instance, and its runtime state; its methods are full flowcharts, and it changes that state only after a commit).
117
+ - `Adapter` (the only block doing external I/O).
115
118
  - `Observer` (subscribes to events and forwards them).
116
- - `Specialist` (narrow, functional domain rules e.g., Scanner, Router, Evaluator, Compiler).
117
119
  - **Strict Layer Isolation & No Persistence Shortcuts**:
118
- - A `Portal` must **never** depend directly on a `Store`, `Registry`, or `Adapter`. Passthrough READS may go through a `Repository`/`Index` facade; every WRITE must route through an `Orchestrator` (a Portal narrative call or dispatch-table binding that reaches a write-effect facade method is a `PORTAL_WRITE_SHORTCUT` error).
119
- - Held domain state always lives in a dedicated data component, never as fields inside an `Orchestrator` or `Specialist`. Two sanctioned shapes: the RECOMMENDED `Repository` pattern (owns `Store` + `Registry` + `Index`; consumers depend on the facade), or — for genuinely simple state — a deliberately standalone `Store` (workflow-layer consumers only, acknowledged via `lint.allow` on `UNOWNED_STORE`). Do **not** combine Store/Registry/Index functionality into a single helper/specialist, and never fold state into a consuming component because a link was refused.
120
+ - A `Portal` must **never** depend directly on a `Store`, `Registry`, `Adapter` or `Query`. Passthrough READS may go through a `Repository`/`Index` facade; every WRITE must route through an `Orchestrator` (a Portal narrative call or dispatch-table binding that reaches a write-effect facade method is a `PORTAL_WRITE_SHORTCUT` error).
121
+ - Held domain state always lives in a dedicated data component, never as fields inside an `Orchestrator`. Two sanctioned shapes: the RECOMMENDED `Repository` pattern (owns `Store` + `Registry` + `Index`; consumers depend on the facade), or — for genuinely simple state — a deliberately standalone `Store` (workflow-layer consumers only, acknowledged via `lint.allow` on `UNOWNED_STORE`). Do **not** combine Store/Registry/Index functionality into a single helper component, and never fold state into a consuming component because a link was refused.
120
122
  2. **Narrative coding (Level 5)**:
121
123
  - Every function body must read top-to-bottom as a sequential list of named, readable steps (Narrative Composition).
122
124
  - Maintain one level of abstraction per function. Functions must remain short (~25 lines max).
@@ -21,9 +21,9 @@ You must read, respect, and update `.wai/phased_design.md` (specifically Stage 5
21
21
  1. **Identify Intent**:
22
22
  - Ask the user for the high-level intent, signature, and contract of the method.
23
23
  2. **Choose the detail level FIRST** (the narrative detail dial):
24
- - `full` — a step-by-step narrative, with flow structure where the logic branches. Default for Orchestrators, Supervisors, Actors, Specialists, and patterns.
24
+ - `full` — a step-by-step narrative, with flow structure where the logic branches. Default for Orchestrators, Supervisors, Actors, and patterns.
25
25
  - `calls-only` — only the cross-component `call` choreography. Default for Portals, Observers, and Adapters (boundary pass-throughs: real logic belongs in the Orchestrator they forward to — if a Portal method needs branching, that is a smell).
26
- - `intent` — no steps; instead write an `intent` paragraph stating what the method does and how it fails. Default for Stores, Indexes, and Registries. The validator enforces an intent floor: placeholder-thin prose is rejected.
26
+ - `intent` — no steps; instead write an `intent` paragraph stating what the method does and how it fails. Default for Stores, Indexes, Queries, and Registries. The validator enforces an intent floor: placeholder-thin prose is rejected.
27
27
  - Omit `detail` when the stereotype default already matches; declare it (per method or spec-level) only to override. Levels are floors — extra detail is never penalized.
28
28
  3. **Draft Narrative Steps** (for `full` / `calls-only`):
29
29
  - Narratives are a FLAT ordered list; the order mimics the code lines. Flow structure jumps by step number — blocks are just skipped regions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wairon/cli",
3
- "version": "5.1.1-dev.17",
3
+ "version": "5.1.1-dev.19",
4
4
  "description": "SYW Waffle AIron — CLI for managing AI coding agent topology across projects",
5
5
  "keywords": [
6
6
  "ai",