@wairon/cli 5.1.1-dev.102 → 5.1.1-dev.104

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.
@@ -105,8 +105,10 @@ schemas:
105
105
  - **designDepth** (project `rules.designDepth`, per-subsystem override, or a pack profile's default): how deep the team COMMITS to designing — `components | interfaces | implementations | narratives` (default `narratives` = full depth). Expectation checks below the depth are gated (nothing deeper is demanded to exist); soundness of whatever IS authored always applies. Ask the user their intended depth at Stage 1 if unclear — do not assume a team that stops at L3 is "incomplete".
106
106
  - **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.
107
107
  - **Subsystem consumers**: an entry may list `consumers: [<subsystem>, …]` — the subsystems the surface is published to. Absent, any subsystem may depend on it; present, only those may, and a dependency from any other subsystem is `CROSS_SUBSYSTEM_UNLISTED_CONSUMER` (an error). Use it for an internal Portal meant for one or two peers, so the boundary says who may call it.
108
- - **Portal mounts (listeners)**: a Portal's routes are its methods' `endpoint` bindings, but something must SERVE the portal. The listener Portal the host starts declares `mounts: [{ portal, prefixes: ["/api"], via? }]` for each portal it routes to; declaring `mounts` — even `mounts: []` — marks a portal as a listener. Every Portal with HTTP endpoints must be mounted by some listener or be one (`UNMOUNTED_PORTAL`), and each mounted endpoint must lie under one of its prefixes (`ENDPOINT_OUTSIDE_MOUNT`; a prefix matches whole path segments, so `/` covers only the root). A single-Portal service: give that Portal `mounts: []`.
108
+ - **Portal mounts (listeners)**: a Portal's routes are its methods' `endpoint` bindings, but something must SERVE the portal: the HTTP listener the host starts (the server process — an Express/Fastify app, a framework's server entry). For an HTTP service, model that listener as a Portal and give it `mounts: [{ portal, prefixes: ["/api"], via? }]`, one entry per portal it routes to; the portals it serves declare no `mounts` of their own. Declare `mounts` ONLY on a listener: the field's presence — even `mounts: []` — says "the host starts this portal directly and nothing else needs to mount it", which exempts it from `UNMOUNTED_PORTAL`. `mounts: []` is therefore right only for a Portal that really IS the whole HTTP server and serves only its own routes; never set it to quiet the warning, and never give it to two HTTP portals of one service — that claims two listeners where there is one server, and hides that nothing mounts them. Every Portal with HTTP endpoints must be mounted by some listener or be one (`UNMOUNTED_PORTAL`), and each mounted endpoint must lie under one of its prefixes (`ENDPOINT_OUTSIDE_MOUNT`; a prefix matches whole path segments, so `/` covers only the root). Mounts judge HTTP only: an MCP, gRPC or CLI portal leaves the field off.
109
109
  - **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.
110
+ - **Declare planned code at design time**: give each implementation the `sourcePath` its code will live at (and a method its own `sourcePath` when its body lives elsewhere). Code linkage — `sourcePath`, `symbol`, `exportedVia`, `simPath`, `injectedParams`, conformance tiers — is not part of the approval, so declaring or changing it never asks for a re-lock. A file not written yet is `SOURCE_FILE_PLANNED` (a notice, never a `--ci` failure) until the component's realization begins; never strip a planned path to quiet anything. A team that wants CI to demand code for every designed implementation sets `rules.conformance.requireCode: true`.
111
+ - **Externals — the pin gates, live drift is visible**: declare a project this one consumes with `sdd_add_external` (alias; source `../sibling`, `hosted:<id>`, `<git url>` or `<git url>#<commit>`; optional `project`, `use`, `description`) instead of editing `.wai/project.yaml`. It refuses in one sentence what cannot be declared, takes the declaration back out when the producer contradicts it (another id, an unexported `use` name), and pins it by default. The owner's gate judges each external against its pin; `sdd_validate_tree` and `sdd_get_status` add ADVISORY live-drift findings (`EXTERNAL_LIVE_INCOMPATIBLE`, `EXTERNAL_DRIFTED`, `EXTERNAL_LIVE_UNCOMPARED`) that never make the tree invalid — adapt the uses, then the human re-pins. `wairon externals status` is the human's opt-in live gate.
110
112
  - **Growing a system — subsystems → parts → projects**: boundaries are earned. Keep subsystems in one folder until a piece needs its own folder or repository; then make it a **part** — `members: { scheduler: services/scheduler }` (or `../admin`, or `git-url#<commit>`), created by `sdd_add_member` (a part by default) or `sdd_externalize_subsystem`. A part's subsystems stay this project's own: local ids, the ordinary subsystem rules, this project's lock. Make a **project** — its own id, exports and lock, referenced as `alias::name` — only when it needs its own team, release, approval or public surface: `sdd_promote_member`, and `sdd_demote_member` to undo it. What a member is follows from its content (an id, an L0 or a lock); `as:` only asserts it.
111
113
  - **Members & the family's shape**: relocate a member with `sdd_move_member`. Every other change of a family's shape is a **family migration**: `sdd_attach_member` (an existing project becomes a member), `sdd_detach_member` / `sdd_adopt_member` (out of the family and back), `sdd_rename_project`, `sdd_rename_member_alias`, `sdd_internalize_member` (a member folded back in, its metadata sent to explicit homes) and `sdd_externalize_subsystem`. Call it with `dryRun: true` first and show the human the plan; applied, it writes every project it touches or none, and it never locks — it names the projects to re-lock.
112
114
  - **Renames keep a trace**: rename with `sdd_rename_component`, `sdd_rename_method` and `sdd_rename_type`, and move methods with `sdd_move_methods` — never by deleting and re-adding a spec or editing an id through `sdd_update_spec`. The tools rewrite every reference, record the old key in the element's rename trace (`previousIds`, a method's `previousNames`; the design export shows it as `formerly`, which is how a generator keeps its user's code across the rename), and keep a published name through `as`. A retired id or method name cannot be taken again (`id-retired`, `name-retired`), and a hand-edited trace that collides is `RENAME_TRACE_CONFLICT`. Never write a trace yourself; unsetting one releases its names at the cost that consumers read a delete plus an add.
@@ -28,7 +28,7 @@ You are the **Delegation Orchestrator**. Your job is to hand scoped work to a fo
28
28
  - **Never reuse a brief across delegations or after a re-lock** — fetch fresh per delegation; the call is cheap and the brief is always current.
29
29
  3. **Spawn a GENERIC subagent from the brief**:
30
30
  - Prompt: `brief.instructions`, plus the concrete task description.
31
- - Write fence: the subagent may only modify files matching `brief.ownedPaths` (within `brief.domainRoot` when set).
31
+ - Write fence: the subagent may only modify files matching `brief.ownedPaths` (within `brief.domainRoot` when set). The fence holds every code location the specs name, existing or planned; a planned file is marked `(planned — create it)` and the subagent creates it there. When the brief names no code location, declare the planned `sourcePath` first (`sdd_update_spec` on the implementation) — code linkage is not part of the approval, so this costs no re-lock — and fetch the brief again.
32
32
  - Required first reading: `brief.readPaths` — the subagent reads these before any edit.
33
33
  - Pass `brief.variantGuidance` along when present, and `brief.typeMapping` — the subagent writes `list<T>`, `T?`, `async T`, `result<T, E>` and an enum by that mapping, never by guess.
34
34
  4. **Apply `brief.budget` when it is present** — constituting the subagent correctly is part of spawning it, not a separate concern. When the brief carries no budget the project has not opted in; spawn as you otherwise would.
@@ -51,7 +51,9 @@ You are the **Spec-to-Code Compiler**. Your job is to generate concrete source c
51
51
  - Confirm the design is approved: `sdd_get_status` reports "Approved: …" and does not list any of this component's specs as changed since the approval. If there is no approval on record, or this component's specs changed after it, stop immediately and ask the human to review the design and run `wairon lock` (never run it yourself).
52
52
  2. **Setup Workspace & Align with Standards**:
53
53
  - Align with the inlined **Core Architecture & Coding Standards** (see below) to ensure naming, narrative coding, and stereotype conventions are respected. Do NOT read these standards from disk; they are already fully specified in your system context.
54
- - Locate the target implementation source file (mapped by `sourcePath` in L4).
54
+ - Locate the target implementation source file (mapped by `sourcePath` in L4). A file the brief marks `(planned — create it)` does not exist yet: create it at exactly that path — the fence already holds it. Until the first file a component names exists, a planned file is only `SOURCE_FILE_PLANNED` (a notice); once one exists, a missing file is `MISSING_SOURCE_FILE` (an error) and a contract method naming no file is `METHOD_SOURCE_PATH_MISSING`.
55
+ - **Code linkage is not approval.** `sourcePath`, `symbol`, `exportedVia`, `simPath`, `injectedParams` and conformance tiers say where the design is realized, not what it is: they are outside the approved digests. When the implementation names no `sourcePath` yet, or names the wrong file, set it with `sdd_update_spec` — it costs no re-lock and never turns the approval stale.
56
+ - **Externals.** A contract you consume from another project is judged against its pin. If `sdd_validate_tree` reports an ADVISORY `EXTERNAL_LIVE_INCOMPATIBLE` (a used member changed, was renamed — the new name is given — or is gone in the live producer), adapt the uses, then ask the human to re-pin (`wairon externals pin <alias>`). Advisory findings never fail the gate; they are not a reason to stop, nor to code against the live producer's new shape without adapting the spec first.
55
57
  3. **Write Tests First (TDD)**:
56
58
  - Create or update the companion test file (e.g. `<component>.test.ts` or similar).
57
59
  - Mock all direct L2 dependencies using the signatures defined in their respective L3 `.interface.yaml` files.
@@ -0,0 +1,83 @@
1
+ # wairon — Architecture
2
+
3
+ > Last updated: 2026-10-05 (for v6.0.0). wairon's own design is specified in its
4
+ > spec tree under `.wai/specs/`; this page is the orientation map, and the tree
5
+ > is authoritative where they differ.
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ wairon is a TypeScript CLI. Its data flow is a single pipeline from the spec tree
12
+ to the artifacts the host AI tool consumes:
13
+
14
+ ```
15
+ .wai/specs/ ──┬─ validate ─▶ conformance result (gate)
16
+ ├─ lock ─────▶ .wai/lock.json (approval) ──▶ lock-check (CI gate)
17
+ ├─ export ───▶ design JSON (wairon-design)
18
+ └─ resolve ──▶ agent topology ──┬─▶ live briefs (sdd_get_agent_brief)
19
+ ├─▶ skills installed
20
+ ├─▶ MCP server (sdd_*)
21
+ └─▶ agent files (opt-in)
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Layers (`src/`)
27
+
28
+ | Directory | Responsibility |
29
+ |-----------|----------------|
30
+ | `cli/` | Commander entrypoint; wires commands |
31
+ | `commands/` | Command implementations, some thin ones in `cli/runner.ts` (init, validate, status, generate, lock, lock-check, doctor, diagram, export, rules, pack, member, project, subsystem, externals, surface, remote, produce, agent, list, show, domains, skills, mcp, execution, update, aliases, `dev`, and the hosting commands `serve` / `host`); `commands/adapters/` holds the forwards into core |
32
+ | `core/` | The engine: `specs` (load/scan the tree), `validation` and `family-validation` (the owner's gate and the family run), `rules/` (the rule registry), `authoring` (the gated spec writes), `agent_resolver` (spec → agents and briefs), `domains`, `skills`, `statehash` (deterministic StateId), `lockfile` and `approval` (the lock record), `externals` / `project-family` (members and pins), `design-export`, `extensions` / `packstore` (packs), `source-analysis` (code↔spec conformance), `diagram` / `canvas`, `provision`, `context`, `templates` |
33
+ | `migrations/` | Family migrations and the upgrade migrations: plan on a private copy, then apply all or nothing through a journaled transaction |
34
+ | `git/` | Git-backed hosted projects (commits only `.wai/`) |
35
+ | `producers/` | Projecting a spec tree to Notion / Miro |
36
+ | `config/` | `loader` (paths, request-scoped project root, project config, topology config, derived registry), defaults |
37
+ | `models/` | Zod schemas: `specs`, `agent`, `domain`/topology, `project`, `template`, `registry` |
38
+ | `exporters/` | Render an agent into a tool-specific file (Claude, Gemini, custom) |
39
+ | `mcp/` | The stdio MCP server (topology + `sdd_*` tools + the `wairon-agent://` brief resource) |
40
+ | `server/` | The hosting server (`sdd_host`): HTTP data plane (`/mcp`), admin control plane, web UI, per-request project scoping, auth, units and permissions, and the credential/project registries |
41
+ | `templates/` | Built-in agent templates + the SDD skill files |
42
+ | `utils/` | Logger, fs, yaml, errors, the AI guide |
43
+
44
+ ---
45
+
46
+ ## Key design points
47
+
48
+ - **Specs are the single source of truth.** `loadRegistry()` always derives the
49
+ agent set from the spec tree via `resolveAgentTopology()`. There is no
50
+ hand-maintained `agents.json`.
51
+ - **Domains are a superset of subsystems.** `resolveDomains()` returns
52
+ subsystem-derived domains (`boundTo` set) plus free-standing domains from
53
+ `.wai/topology.yaml`. A subsystem is a software unit; a domain is an ownership
54
+ scope.
55
+ - **Agents are briefs first.** Each agent's brief is composed live from the
56
+ current tree; writing it as a native subagent file into each target's output
57
+ directory is opt-in (`rules.materializeAgentFiles`). wairon does not do
58
+ per-directory or session-based rendering.
59
+ - **Approval is a file, not a status.** `lock` writes the gate identity and one
60
+ digest per spec to `.wai/lock.json` and never rewrites a spec; `lock-check`
61
+ and `status` compare against it.
62
+ - **Conformance is centralized** in `core/validation.ts`: `validateProject` (the
63
+ owner's gate, one project from its own files) and `validateFamily` (the
64
+ family run in `core/family-validation.ts`: each member's own gate verbatim,
65
+ plus composition and the family checks), reused by both the `validate`
66
+ command and the `sdd_validate_tree` MCP tool.
67
+ `validateAsComplete` runs the same gate at full strictness for `lock`.
68
+ - **Optionally hosted.** `wairon serve` (the `sdd_host` subsystem, in `server/`)
69
+ serves the `sdd_*` tools over streamable HTTP for many fully-isolated projects,
70
+ binding each request to its authenticated project via `AsyncLocalStorage`
71
+ (`runWithProjectRoot`) — reusing the core, validation, and MCP layers unchanged.
72
+ - **Everything is file-based** under `.wai/` — no database, and no daemon for the
73
+ core workflow. `wairon serve` is an **opt-in** hosting daemon (see the
74
+ [hosted server guide](design/hosted-mcp-server.md)) that serves the same
75
+ file-based tools over HTTP.
76
+
77
+ ---
78
+
79
+ ## Stack
80
+
81
+ TypeScript + Node 18+, Commander, Inquirer, Zod, js-yaml, the MCP SDK, Vitest,
82
+ and tsup for bundling. See [docs/standards/](standards/INDEX.md) for the
83
+ architecture standards the SDD component model is built on.