@wairon/cli 5.1.1-dev.11 → 5.1.1-dev.110

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.
Files changed (37) hide show
  1. package/CONTRIBUTING.md +193 -0
  2. package/README.md +154 -41
  3. package/dist/cli/index.js +66474 -29775
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/index.d.ts +43671 -0
  6. package/dist/index.js +53581 -21797
  7. package/dist/index.js.map +1 -1
  8. package/dist/templates/architect.yaml +15 -13
  9. package/dist/templates/domain-owner.yaml +5 -4
  10. package/dist/templates/skills/sdd-architect.md +44 -26
  11. package/dist/templates/skills/sdd-auditor.md +5 -4
  12. package/dist/templates/skills/sdd-delegate.md +43 -4
  13. package/dist/templates/skills/sdd-implement.md +83 -13
  14. package/dist/templates/skills/sdd-narrative.md +7 -4
  15. package/dist/templates/variants/logic-shapes.yaml +59 -0
  16. package/dist/templates/variants/portal-shapes.yaml +13 -0
  17. package/dist/webapp.html +771 -0
  18. package/docs/architecture.md +83 -0
  19. package/docs/cli.md +1208 -0
  20. package/docs/design/approval-baseline.md +257 -0
  21. package/docs/design/connecting-agent-entrypoint.md +167 -0
  22. package/docs/design/execution-budgets.md +199 -0
  23. package/docs/design/pack-scoping.md +581 -0
  24. package/docs/design/supervisor-doctrine.md +110 -0
  25. package/docs/design-export.md +313 -0
  26. package/docs/extending-wairon.md +616 -0
  27. package/docs/network.md +109 -0
  28. package/docs/requirements.md +65 -0
  29. package/docs/roadmap.md +133 -0
  30. package/docs/standards/INDEX.md +23 -0
  31. package/docs/standards/architecture.md +1042 -0
  32. package/docs/standards/language-bindings.md +120 -0
  33. package/docs/templates.md +61 -0
  34. package/docs/vision.md +59 -0
  35. package/docs/wairon-feature-requests-hosted-mcp.md +74 -0
  36. package/package.json +18 -5
  37. package/schemas/design-export-2.json +1186 -0
@@ -0,0 +1,110 @@
1
+ # Supervisor and Actor doctrine: supervision state, shared data, the lifecycle effect
2
+
3
+ ## The problem
4
+
5
+ Two rules left the process layer without an honest shape.
6
+
7
+ - Held state lives in a dedicated data component, but a Supervisor could not
8
+ depend on a Store, Registry, Repository, Index or Query at all
9
+ (`ARCHITECTURE_VIOLATION_SUPERVISOR_DEP`). A Supervisor's own bookkeeping —
10
+ restart counts, the set of live children, the brackets of the runs it starts —
11
+ had nowhere to live.
12
+ - A component depending on a live Actor had to also depend on a Supervisor that
13
+ supervises it (`ACTOR_REACHED_WITHOUT_SUPERVISOR`). Real callers do not go
14
+ through the Supervisor: they look the Actor's live handle up by id in a
15
+ registry the Supervisor keeps. The remedy pushed a ceremonial edge instead of
16
+ the real lookup hop.
17
+
18
+ ## The rules
19
+
20
+ 1. **Supervision state.** A Supervisor may `owns` a Store or Registry that is its
21
+ own: one hop, private, read and written in full. It owns nothing else, and
22
+ nobody else depends on that state.
23
+ 2. **Shared data.** A Supervisor may call a data component it does not own only
24
+ through methods whose declared effect is `read` or `lifecycle`. A write goes
25
+ through a workflow: the Supervisor depends on the Orchestrator that does it.
26
+ Judged from the Supervisor's narrative call steps against the callee's
27
+ declared effect; a callee that declares no effect is refused too.
28
+ 3. **The lifecycle effect.** A third method effect, `lifecycle`: the method
29
+ creates, destroys, or (un)registers an entity's existence or membership and
30
+ never modifies its domain fields. It is closed under composition: a lifecycle
31
+ method calls only read and lifecycle methods besides construction and local
32
+ steps.
33
+ 4. **Supervised reach.** A component depending on a live Actor reaches it through
34
+ its supervision when it also depends on a Supervisor that supervises the
35
+ Actor, or on a Registry such a Supervisor maintains — by owning it, or by
36
+ calling it with lifecycle-effect methods.
37
+
38
+ **Honest limit.** A `read` made on behalf of a request still passes. In practice
39
+ the paired write is refused, which forces the marshalling workflow out, and the
40
+ read moves with it.
41
+
42
+ ## Codes
43
+
44
+ | Code | Severity | Rule | Reported on | Meaning |
45
+ |---|---|---|---|---|
46
+ | `SUPERVISOR_CONTAINMENT` | error | pattern-containment | the Supervisor | it owns something other than a Store or Registry |
47
+ | `SUPERVISION_STATE_INTRUSION` | error | member-visibility | the intruder | "X depends on Y, which Supervisor Z owns as its supervision state" |
48
+ | `SUPERVISOR_WRITE_SHORTCUT` | error | supervisor-shared-data (new) | the Supervisor's implementation | a call to a write-effect or untagged method on data it does not own |
49
+ | `LIFECYCLE_CALLS_WRITE` | error | lifecycle-effect-closure (new) | the implementation | a lifecycle-effect method calls a write-effect method |
50
+ | `ARCHITECTURE_VIOLATION_SUPERVISOR_DEP` | error | entrypoint-dependencies | the Supervisor | kept, narrowed: a Supervisor depending on a presentation block |
51
+ | `ACTOR_REACHED_WITHOUT_SUPERVISOR` | error | entrypoint-dependencies | the caller | kept, widened: Registry reach counts; the remedy names the Registry, or says to model one |
52
+ | `BLOCK_OWNS_MEMBERS` | error | pattern-membership | the block | kept: a Supervisor is exempt |
53
+ | `SHARED_OWNED_MEMBER` | error | pattern-membership | the later claimant | kept: Supervisors claim like patterns |
54
+ | `UNUSED_LINT_ALLOW` | warning | lint-allows | the allowing spec | kept: an allow naming an error now says an error cannot be allowed |
55
+
56
+ The existing readers of `effect` read `lifecycle` as a mutation:
57
+ `PORTAL_WRITE_SHORTCUT` refuses a Portal's direct lifecycle call as it refuses a
58
+ write, and a durable Store needs a hydration read-back for its lifecycle methods
59
+ as for its writes (`MISSING_HYDRATION`). The invariant registry still asks only
60
+ write-effect methods to assert invariants, since a lifecycle method never
61
+ modifies domain fields.
62
+
63
+ Within the brief, three choices were made:
64
+
65
+ - Exclusivity is its own code rather than a reworded `VISIBILITY_VIOLATION`:
66
+ the remedy differs (there is no facade to depend on instead; the state is
67
+ either shared data or reached through the Supervisor).
68
+ - An owned Registry counts as maintained for supervised reach, as decided. A
69
+ caller depending on it is then still an intruder, so that shape reports
70
+ `SUPERVISION_STATE_INTRUSION` once and no actor finding: a lookup Registry
71
+ callers share is shared data the Supervisor keeps through lifecycle calls.
72
+ - The lint-allow audit decides "error" from what the run saw on that spec (a
73
+ finding of the code landed there as an error) or, when nothing fired there,
74
+ from the code's resolved severity (project override, profile, default). The
75
+ validator now hands the rule context each code's default severity.
76
+
77
+ ## How the four motivating cases validate
78
+
79
+ 1. **An orchestrator calls an Actor whose handle it resolves through a router
80
+ that is really a Registry of live handles, mistyped as a Store.** Typed as a
81
+ Store, the caller reaches the Actor through no supervision and
82
+ `ACTOR_REACHED_WITHOUT_SUPERVISOR` fires on the caller. Retyped as a
83
+ Registry whose register/unregister methods are `lifecycle` and whose lookup
84
+ is `read`, with the Supervisor registering each started Actor through a
85
+ lifecycle call, the caller depending on that Registry and the Actor is
86
+ supervised reach, and the Supervisor's calls pass `SUPERVISOR_WRITE_SHORTCUT`.
87
+ A standalone Registry still needs a Store to write to
88
+ (`REGISTRY_WITHOUT_STORE`, a warning) — give the handles a Store, or wrap both
89
+ in a Repository the Supervisor keeps through lifecycle calls.
90
+ 2. **A component that spawns and owns Actors is typed Orchestrator but is a
91
+ fused Supervisor and workflow.** It depends on the Actor with no Supervisor
92
+ anywhere: `ACTOR_REACHED_WITHOUT_SUPERVISOR`, whose remedy says to give the
93
+ Actor a Supervisor that maintains a handle Registry. Split into a Supervisor
94
+ (spawns, restarts, registers handles) and a workflow (looks the Actor up in
95
+ that Registry), the tree validates.
96
+ 3. **A scheduler Supervisor reads guard state and brackets run lifetimes on a
97
+ shared repository.** The edge to the repository is no longer a finding. Its
98
+ calls — read the guard, open a run, count live runs, close the run — are
99
+ `read`, `lifecycle`, `read`, `lifecycle`: quiet. Tagging open or close as
100
+ `write`, or leaving them untagged, fires `SUPERVISOR_WRITE_SHORTCUT`; a
101
+ lifecycle open that also updates the run's fields fires
102
+ `LIFECYCLE_CALLS_WRITE` on the repository's implementation.
103
+ 4. **The same Supervisor reads and writes a shared object heap on behalf of a
104
+ call.** The read passes; the write fires `SUPERVISOR_WRITE_SHORTCUT`. Moving
105
+ the work into a marshalling Orchestrator the Supervisor calls — the read goes
106
+ with it — validates.
107
+
108
+ All four, and a fire and a control for every new or changed code, are fixtures
109
+ in `tests/rules-matrix/families/boundaries-supervision.fixtures.ts`; the
110
+ lint-allow case is in `references-lint-allows.fixtures.ts`.
@@ -0,0 +1,313 @@
1
+ # The design export (`wairon-design`)
2
+
3
+ The design export is one JSON document holding a project's whole design, resolved. It is meant
4
+ for tools that work from a design: code generators, translators to other modelling languages,
5
+ documentation sites and design diff tools. Such a tool reads the export instead of walking
6
+ `.wai/specs`.
7
+
8
+ - **Every element has a key, and every reference is a key.** A consumer never resolves a
9
+ name itself.
10
+ - **Signatures are inlined.** A method that takes its signature from a source carries that
11
+ source's params and returns.
12
+ - **Types are canonical.** Every type position is canonical text plus a parsed expression.
13
+ - **Narratives are flat.** Each method body is a numbered step list, its targets resolved to
14
+ keys.
15
+
16
+ The export is a projection. It holds no information the spec tree does not, and it is never
17
+ read back into a tree.
18
+
19
+ ## Getting one
20
+
21
+ | Where | How |
22
+ |---|---|
23
+ | CLI | `wairon export` prints the JSON to stdout, and nothing else. `wairon export --out design.json` writes the file and reports the path and the approval state. A project with member projects names them (on stderr without `--out`, when it holds no design of its own): they are dependencies, exported from their own roots. See [cli.md](cli.md#wairon-export---out-file). |
24
+ | Library | `const { exportDesign } = require('@wairon/cli')` (or `import` from an ES module), then `exportDesign(outPath?, approval?)`, which returns the document and writes it when `outPath` is given. The package ships TypeScript declarations for the library entry (`dist/index.d.ts`, the `types` field), so `DesignExport` and `exportDesign` are typed. |
25
+ | JSON Schema | `schemas/design-export-2.json`, shipped in the package (`require.resolve('@wairon/cli/schemas/design-export-2.json')`). It is a JSON Schema draft-07 document (it declares `$schema`) and carries no `$id`: reference it by its path in the package. It is generated from the zod schema in `src/models/design-export.ts` at build time, and a test fails if the two drift. |
26
+
27
+ The CLI decides the approval verdict first, the same way `wairon lock-check` does (not strict),
28
+ and stamps it. It also exports a tree that is not approved, stamped with its state.
29
+
30
+ ## Compatibility promise
31
+
32
+ - **`format`** is always `"wairon-design"`.
33
+ - **`formatVersion`** is `MAJOR.MINOR` and is currently `2.0`. 2.0 is the reachability
34
+ model's: `portalType` became `transport`, listener `mounts` are gone, a Portal may carry
35
+ the `invokedBy` entry its verbs inherit, and `invokedBy` kinds are `entry | runtime` with a
36
+ `scope`.
37
+ - **A minor version only adds.** It adds new fields, or new members of a closed set (a
38
+ `role`, an approval state). A consumer must **ignore fields it does not know**. A 1.x
39
+ document validates against `design-export-2.json`.
40
+ - **A major version breaks.** It removes or renames a field, or changes what one means. Every
41
+ major version is named in the CHANGELOG, and wairon emits one major version at a time.
42
+ - **A design change is never a format change.** Two exports of different trees in the same
43
+ format differ only in content.
44
+ - **`generator`** is the wairon version that wrote the document. It is for diagnostics only,
45
+ and consumers must not branch on it.
46
+
47
+ ## Determinism
48
+
49
+ The same tree with the same approval verdict produces **byte-identical** JSON, whatever order the
50
+ spec files sit in on disk.
51
+
52
+ - **Object keys** are sorted (ordinal).
53
+ - **Element lists** are sorted by key: `subsystems`, `components`, `interfaces`,
54
+ `implementations`, `types`, `dependencies` and each export table.
55
+ - **Inner lists keep their declared order**, because that order is part of the design: params,
56
+ fields, enum values, narrative steps, lifecycle roots and `formerly`.
57
+ - **The document has no timestamp.** Equal `source.stateId`s with equal approvals mean equal
58
+ documents, so a consumer can cache on `stateId`.
59
+ - **`source.stateId` is a content id, not the lock's gate identity.** The two are different
60
+ digests by design, and never compare equal:
61
+ - `source.stateId` (`sha256:<digest>`) hashes the parsed spec tree the export was read
62
+ from, every spec the scan loads, timestamps left out and specs in id order. Snapshots and
63
+ archives carry the same id. It changes exactly when the specs change.
64
+ - The lock's `stateId` in `.wai/lock.json` (`sha256+content+doctrine+inputs+members:<digest>`)
65
+ is the gate identity. It hashes this project's own specs together with the governing
66
+ doctrine, the declared inputs, `composition` and each direct member's composition subject,
67
+ so it also moves when a rule, an input or a member's approval moves.
68
+
69
+ To relate an export to an approval, read `source.approval`: it is `locked` exactly when
70
+ that lock record covers this tree.
71
+
72
+ ## Keys
73
+
74
+ | Element | Key |
75
+ |---|---|
76
+ | Subsystem, component, interface, implementation | its id |
77
+ | Type | `subsystem::id` when a subsystem owns it, the bare id when it is system-level |
78
+ | Contract method | `<interface key>.<name>` |
79
+ | Type method | `<type key>.<name>` |
80
+ | Field, param, enum value | its name, within its owner |
81
+ | Narrative step | its `stepNumber`, within its method |
82
+ | Anything in another project | `alias::publicName`: the alias this project declares the project under, and the public name that project's L0 exports it as |
83
+
84
+ A type's key `subsystem::id` uses `::` as a **key separator** inside this document: its first
85
+ segment is one of this document's own `subsystems` keys. A cross-project key's first segment is
86
+ an alias from `dependencies` instead, so the two never collide. Signature text (a method's
87
+ `signature` string) keeps the spec's own spelling, which writes a subsystem-owned type as
88
+ `subsystem.id`; read the structured `params` and `returns`, not the signature text, when you
89
+ need keys.
90
+
91
+ A reference into another project is always written the way a consumer can resolve it: through
92
+ the producer's **public** export table. It is never written with the loader's internal key, so
93
+ the reference stays valid when the producer renames something behind an `as`. A reference that
94
+ binds no exported name stays as written; the validator reports it.
95
+
96
+ ## The document
97
+
98
+ ### Top level (`design_export`)
99
+
100
+ | Field | Meaning |
101
+ |---|---|
102
+ | `format`, `formatVersion`, `generator` | See [Compatibility promise](#compatibility-promise). |
103
+ | `source` | Which tree the document was projected from, and the approval verdict over it. |
104
+ | `project` | The L0 of the exported project. |
105
+ | `dependencies` | The other projects this project depends on. They are listed, never inlined. |
106
+ | `subsystems`, `components`, `interfaces`, `implementations`, `types` | The design itself, one list per level. |
107
+
108
+ ### `source` (`design_source`)
109
+
110
+ | Field | Meaning |
111
+ |---|---|
112
+ | `projectId` | The project's effective id (its system name when it declares none). |
113
+ | `stateId` | `sha256:<digest>`, the tree's content identity. It is the cache key. |
114
+ | `approval` | `locked`, `stale`, `unlocked` or `unjudged`. |
115
+ | `approved` | `true` exactly when `approval` is `locked`. |
116
+
117
+ **What `approved` means.** It is the same verdict `wairon lock-check` and `wairon status` give:
118
+
119
+ - The committed lock record is resolved against the gate identity. That identity is the parsed
120
+ own tree, its governing doctrine, its declared inputs, `composition`, and the approvals of its
121
+ direct members.
122
+ - It is never a weaker per-spec digest comparison.
123
+ - `stale` means a lock exists but the tree moved since. `unlocked` means no lock was ever
124
+ written.
125
+ - `unjudged` means an in-process caller passed no verdict. It claims nothing either way.
126
+
127
+ Validation findings are not part of the document. Run `wairon validate` for those.
128
+
129
+ ### `project` (`design_project`)
130
+
131
+ `name`, `vision`, `boundaries` and `requirements` (as text), `targetLanguage`, and `exports`:
132
+ the resolved L0 export table, as `design_export_entry` rows.
133
+
134
+ ### Export entries (`design_export_entry`)
135
+
136
+ | Field | Meaning |
137
+ |---|---|
138
+ | `publicName` | The name consumers use. |
139
+ | `targetKind` | `component`, `interface` or `type`. |
140
+ | `target` | The canonical target's key. Every re-export has already been followed to that target. |
141
+ | `audience`, `version`, `stability` | As declared. |
142
+
143
+ ### `dependencies` (`design_dependency`)
144
+
145
+ | Field | Meaning |
146
+ |---|---|
147
+ | `alias` | The alias the project uses for the dependency. It is the prefix of every `alias::publicName` that points into it. |
148
+ | `projectId` | The dependency's project id. |
149
+ | `role` | `member` for a project this one contains (an in-tree subdirectory member, a sibling or git member, a legacy mount). `external` for a declared external. A part is not a dependency: its subsystems are this project's own. |
150
+ | `digest` | The content digest pinned in `.wai/externals.lock.yaml`, when the dependency is pinned. It is absent when it is not pinned, or when the lock cannot be read. |
151
+ | `uses` | The public names this project's references reach in the dependency, sorted. |
152
+
153
+ ### `subsystems` (`design_subsystem`)
154
+
155
+ | Field | Meaning |
156
+ |---|---|
157
+ | `key`, `name`, `description`, `status`, `profile`, `targetLanguage` | As declared. |
158
+ | `lifecycle` | The lifecycle roots: `{ phase, component, method }`. These are what the runtime invokes. |
159
+ | `exports` | The resolved L1 export table. |
160
+ | `trustedLinks` | The subsystem keys this subsystem holds a trusted link to. |
161
+ | `ext` | Pack extension data. |
162
+
163
+ ### `components` (`design_component`)
164
+
165
+ | Field | Meaning |
166
+ |---|---|
167
+ | `key`, `name`, `description`, `status`, `subsystem` | As declared. |
168
+ | `stereotype`, `variant`, `dependencyClass`, `durability` | As declared. |
169
+ | `transport`, `abi`, `invokedBy` | On a Portal: its transport, the `abi` of an `InProcess` Portal that declares one, and the entry its verbs inherit. |
170
+ | `owns`, `dependsOn` | Keys. |
171
+ | `emits`, `subscribesTo` | The topics the component publishes to and consumes from. |
172
+ | `auth`, `basePath`, `dispatch` | A Portal's authentication, base path and dispatch table. Dispatch targets are keys. |
173
+ | `patterns`, `externalLinks` | As declared. |
174
+ | `formerly` | The rename trace. See [Renames](#renames-formerly). |
175
+ | `ext` | Pack extension data. |
176
+
177
+ ### `interfaces` (`design_interface`) and methods (`design_method`)
178
+
179
+ An interface carries `key`, `component`, `name`, `description`, `status`, `methods`, `formerly`
180
+ and `ext`. Each method carries:
181
+
182
+ | Field | Meaning |
183
+ |---|---|
184
+ | `key`, `name`, `description` | As declared. |
185
+ | `params`, `returns` | Type refs. A method whose signature comes from a source has its source's params and returns inlined. |
186
+ | `signature` | The derived signature text, for display only. |
187
+ | `signatureType` | The key of the signature type the method takes its signature from, when it names one. A method source is inlined and never named here. |
188
+ | `effect`, `guarantees`, `invokedBy` | As declared. |
189
+ | `endpoint` | The endpoint as declared: `transport` plus that transport's address fields. |
190
+ | `formerly`, `ext` | The method's rename trace (its former `<interface>.<name>` keys), and pack data. |
191
+
192
+ A type's pure methods use the same shape.
193
+
194
+ ### Params, fields and type refs
195
+
196
+ - **`design_param`**: `name`, `type`, `optional` and `description`. `optional` means the
197
+ param may be left out. "May be none" is part of the type, written as `T?`.
198
+ - **`design_field`**: the same fields, plus `key` (`primary`, `unique` or `foreign`) and
199
+ `references`, which is a type key or `<type key>.<field>`.
200
+ - **`design_type_ref`**:
201
+ - `text` is the canonical text in the neutral type grammar.
202
+ - `expression` is the parsed form. It is a tree of `{ form, name?, args }`, where `form` is
203
+ `primitive`, `named`, `list`, `set`, `map`, `optional`, `union`, `async` or `applied`.
204
+ - The `name` of every named member is the **key** of the type it names.
205
+ - When the grammar cannot read a position, `expression` is absent. The text is then opaque:
206
+ the export never invents a shape.
207
+
208
+ ### `implementations` (`design_implementation`) and bodies (`design_method_body`)
209
+
210
+ An implementation carries:
211
+
212
+ - `key`, `contract`, `component`, `name`, `description` and `status`;
213
+ - `technologies`, by name;
214
+ - `sourcePath`, `methods`, `formerly` and `ext`.
215
+
216
+ Each method body carries:
217
+
218
+ | Field | Meaning |
219
+ |---|---|
220
+ | `method` | The contract method's key. |
221
+ | `detail` | `full`, `calls-only` or `intent`, after the stereotype default is applied. |
222
+ | `intent` | The prose that stands in for steps, at `intent` detail. |
223
+ | `calls` | The declared calls, as method keys. |
224
+ | `narrative` | The flat step list as stored. A `call`, `register` or `dispatch` step's target, and a credential source, are keys. Flow steps jump by `stepNumber`. |
225
+
226
+ ### `types` (`design_type`)
227
+
228
+ A type carries:
229
+
230
+ - `key`, `kind`, `name`, `description` and `status`;
231
+ - `fields` and its pure `methods`;
232
+ - a signature type's `params` and `returns`;
233
+ - an enum's `values`, in declared order;
234
+ - a named scalar's `holds`;
235
+ - `invariants`;
236
+ - `componentClass`, a key;
237
+ - `database`, `table` and `linkedEntity`;
238
+ - `formerly` and `ext`.
239
+
240
+ ## What is left out, and why
241
+
242
+ | Left out | Why |
243
+ |---|---|
244
+ | Lint allows, conformance tiers and finding declarations | They are gate configuration, not design. |
245
+ | `symbol` and `exportedVia` | They bind to this repository's code. |
246
+ | `simPath` | It names a test harness. |
247
+ | `createdAt` and `updatedAt` | They would break determinism. |
248
+ | A member project's specs | They are listed as a dependency. Export the member itself for its design. |
249
+
250
+ Pack `ext` data is passed through untouched, under each pack's own key. It is the sanctioned
251
+ extension channel.
252
+
253
+ ## Executable or library: the consumer decides
254
+
255
+ The export has no `kind` that says "program" or "library". Whether a design ships as a
256
+ service, a CLI, a library or a plugin is a packaging decision. The same architecture can be
257
+ packaged in each of those ways, so wairon leaves the decision to the consumer, as it does with
258
+ deployment. The export carries the facts a consumer needs to decide for its own target:
259
+
260
+ - **Lifecycle roots** (`subsystems[].lifecycle`): what the runtime invokes, with the phase
261
+ (`init`, `shutdown`, `cyclic`, `interrupt` or `scheduled`).
262
+ - **Transports and endpoints** (`components[].transport`, `interfaces[].methods[].endpoint`):
263
+ how callers reach each Portal. An `InProcess` Portal is a library and binds no endpoint;
264
+ its `abi` says how a foreign language links it.
265
+ - **Entries** (`components[].invokedBy`, `interfaces[].methods[].invokedBy`): the verbs callers
266
+ outside the design reach, with their `scope`.
267
+ - **The export table** (`project.exports`): what other projects link against.
268
+
269
+ For example, one generator might emit a `main` that runs the `init` roots and serves the HTTP
270
+ portals. Another might emit a library that publishes `project.exports`. Both are correct readings
271
+ of the same export.
272
+
273
+ ## Renames: `formerly`
274
+
275
+ Components, interfaces, implementations and types carry `formerly`: every key they held before,
276
+ oldest first. Contract methods carry their former `<interface>.<name>` keys. The rename tools
277
+ write these traces:
278
+
279
+ | Tool | What it records |
280
+ |---|---|
281
+ | `sdd_rename_component` | The component, and its `i<id>` and `<id>_impl` |
282
+ | `sdd_rename_method` | The method |
283
+ | `sdd_move_methods` | Each method that moved |
284
+ | `sdd_rename_type` | The type |
285
+
286
+ **Retired names cannot be reused.**
287
+
288
+ - A rename tool or a create that would give a new spec an id a trace holds is refused
289
+ (`id-retired`).
290
+ - So is a new method under a name its own contract retired (`name-retired`).
291
+ - A hand edit that reuses one is reported as `RENAME_TRACE_CONFLICT`.
292
+ - Unsetting the trace releases the name. A consumer then sees a delete plus an add.
293
+
294
+ **The consumer's algorithm.** This is how a generator keeps its user's wired code across
295
+ renames:
296
+
297
+ 1. Keep the keys you generated from.
298
+ 2. For a key that is now missing, first resolve its **owner** (subsystem, component, interface
299
+ or type) through `formerly`. Then resolve the **member** by name, or through the member's own
300
+ `formerly`.
301
+ 3. A match is a rename. Anything left unmatched is a deletion. A key present now and absent
302
+ before is an addition.
303
+ 4. Traces accumulate, so this works across chains of renames, and across exports you skipped.
304
+
305
+ **A published name survives an internal rename.** When a renamed component or type backed an
306
+ export entry whose public name came from its id, the rename writes `as: <old name>` on that
307
+ entry. Consumers' pins and `alias::publicName` references keep resolving. A renamed method that
308
+ is published is reported, but not prevented: no mainstream export mechanism aliases a single
309
+ member.
310
+
311
+ **What escapes the trace.** Moving elements between projects (`wairon subsystem externalize`,
312
+ `wairon member internalize`) is a delete in one project and an add in another. No
313
+ single-project trace follows such a move. Renames made before traces existed left none behind.