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

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,7 +105,7 @@ 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
110
  - **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
111
  - **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.
@@ -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.