@wairon/cli 5.1.1-dev.110 → 5.1.1-dev.111
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.
- package/README.md +6 -2
- package/dist/cli/index.js +3777 -2145
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +198 -25
- package/dist/index.js +2706 -1236
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-delegate.md +4 -3
- package/dist/templates/skills/sdd-implement.md +2 -2
- package/dist/templates/variants/portal-shapes.yaml +9 -5
- package/dist/webapp.html +47 -47
- package/docs/cli.md +125 -25
- package/docs/standards/architecture.md +8 -6
- package/package.json +1 -1
package/docs/cli.md
CHANGED
|
@@ -32,6 +32,14 @@ The generated `CLAUDE.md`/`GEMINI.md`, `.claude/` (or `.gemini/`) skills and
|
|
|
32
32
|
`.mcp.json` hold no machine path: commit them and every clone's assistant starts
|
|
33
33
|
with them, or ignore them and run `wairon generate` after a clone — either way
|
|
34
34
|
`wairon generate` / `wairon doctor --fix` refreshes them.
|
|
35
|
+
A root `CLAUDE.md` (and `GEMINI.md`, `.cursorrules`, `.github/copilot-instructions.md`,
|
|
36
|
+
`.codexrules`) is usually your own file: wairon writes only its block between
|
|
37
|
+
`<!-- wairon-root-start -->` and `<!-- wairon-root-end -->` and keeps every other
|
|
38
|
+
byte — `init`, `generate`, `lock` and `doctor --fix` never delete text you wrote
|
|
39
|
+
there. A root file holding the unmarked pointer an earlier release wrote is
|
|
40
|
+
migrated into the marked block, keeping whatever followed it. `.mcp.json` is
|
|
41
|
+
merged: only its `wairon` server entry is wairon's. A folder whose name gives no
|
|
42
|
+
usable project id (`con`, `super`) starts with `<name>-project`, said once.
|
|
35
43
|
wairon writes no `.gitignore` — your toolchain's own entries (`node_modules/`,
|
|
36
44
|
`dist/`, …) are all it needs.
|
|
37
45
|
|
|
@@ -109,6 +117,29 @@ warnings as errors (notices are printed and counted, never fatal).
|
|
|
109
117
|
(wairon's own copy)` on a project with TypeScript 7 or none, `(the project's
|
|
110
118
|
own)` on TypeScript 5 or 6 — or `Code: none read yet` before any file the design
|
|
111
119
|
names exists.
|
|
120
|
+
- **Binding modules.** A consumer whose code reaches another project's library
|
|
121
|
+
through a hand-written binding module (a typed binding to a native addon, a
|
|
122
|
+
client stub) names it on the implementation — `bindings:
|
|
123
|
+
[src/routing/geo_binding.ts]` (code linkage like `sourcePath`: setting it
|
|
124
|
+
never reopens the approval; `sdd_write_narrative` or `sdd_update_spec`).
|
|
125
|
+
`validate` then compares the binding's exported declarations with the pinned
|
|
126
|
+
snapshots of the externals that implementation's component reaches:
|
|
127
|
+
`BINDING_DRIFT` (warning) for a method or function the pin renamed — `"tile_for_coordinate"
|
|
128
|
+
was renamed to "tile_at" in geo::tiles — follow the rename` — or no longer
|
|
129
|
+
exports, a parameter list whose arity or names differ (a parameter rename
|
|
130
|
+
named), and a field the pinned type renamed, does not have or has that the
|
|
131
|
+
binding lacks. A declaration is matched by the `alias::name` its doc comment
|
|
132
|
+
names (`/** geo::tiles */`), else by name (an `Api`/`Port`/`Client` suffix
|
|
133
|
+
ignored), else by the pinned interface most of its methods belong to; names
|
|
134
|
+
compare ignoring case and `_`, and a declaration nothing matches (a loader, an
|
|
135
|
+
aggregate handle) is the binding's own. `BINDING_UNREAD` (notice) says when a
|
|
136
|
+
binding cannot be compared: not written yet, outside the root, not
|
|
137
|
+
TypeScript/JavaScript (the only languages read), or its component reaches
|
|
138
|
+
neither a pinned external nor a member project. Return types are not
|
|
139
|
+
compared. A binding into a **member** project (declared under `members`,
|
|
140
|
+
never pinned) is compared the same way with what the member's L0 exports
|
|
141
|
+
now, so a `rename-param` or `rename-field` in the member reaches its parent's
|
|
142
|
+
binding at `validate` time as `BINDING_DRIFT` with the rename to follow.
|
|
112
143
|
- At a project that declares members, `validate` is the **family run**: every
|
|
113
144
|
member's own gate, and this project's externals composed against their live
|
|
114
145
|
producers. `--no-recursive` runs this project's gate alone; `--family` runs
|
|
@@ -178,17 +209,28 @@ warnings as errors (notices are printed and counted, never fatal).
|
|
|
178
209
|
receiver is typed `any` or `unknown`, cast to either or to an index
|
|
179
210
|
signature, picked by a computed key, or typed nothing, under the name of a
|
|
180
211
|
data component's write (or with a computed key on a receiver that was a data
|
|
181
|
-
component before the cast
|
|
182
|
-
|
|
212
|
+
component before the cast — however the receiver or the key is cast, and
|
|
213
|
+
whether the member is invoked on the spot or taken as a value and invoked
|
|
214
|
+
later), is `PORTAL_CALL_UNRESOLVED` (a warning, so `--ci` fails): give the
|
|
215
|
+
receiver its type. A key the checker types as one literal
|
|
216
|
+
(`const name = 'put'`) names its member by that literal. An Orchestrator (or any component) whose
|
|
183
217
|
code calls a write- or lifecycle-effect method of another component that its
|
|
184
218
|
narrative never claims is `UNDECLARED_WRITE_CALL`, and so is a Portal calling
|
|
185
|
-
a workflow verb its narrative never names whose effect is
|
|
186
|
-
or
|
|
187
|
-
|
|
219
|
+
a workflow verb its narrative never names whose effect is a write — declared,
|
|
220
|
+
or, where the verb declares none, read off its own narrative (a verb whose
|
|
221
|
+
narrative only reads is a read; one nothing settles is reported as an
|
|
222
|
+
unnarrated call whose effect is undeclared); the same unclaimed write through
|
|
223
|
+
a receiver the checker cannot follow is `CALL_ORIGIN_UNRESOLVED` (a warning).
|
|
224
|
+
A narrated call through a port to a collaborator whose code is not written
|
|
225
|
+
yet (no file its implementations name exists) is `CALL_TARGET_PLANNED`, a
|
|
226
|
+
notice like `SOURCE_FILE_PLANNED`, and is judged as usual once that code
|
|
227
|
+
exists. The narrative↔code and
|
|
188
228
|
declared-edge checks read the checker too, so a type-only `ports.ts` barrel,
|
|
189
229
|
`export *`, a port declared in a shared module and a dependency object typed
|
|
190
230
|
inline realize the calls and edges they carry. And a file importing a technology's package
|
|
191
|
-
outside the components that bind it
|
|
231
|
+
outside the components that bind it — itself, or through a helper module no
|
|
232
|
+
component maps (followed transitively; the finding names the path) — is
|
|
233
|
+
`TECH_LEAKAGE_IN_CODE`. A technology's
|
|
192
234
|
packages are its declared tokens (its name, or its `matches`) plus the common
|
|
193
235
|
packages wairon knows for it — see [Technologies and their
|
|
194
236
|
packages](#technologies-and-their-packages). An Adapter's call step to a verb
|
|
@@ -321,10 +363,9 @@ as format 3 in place — before any other repair touches a spec — keeping
|
|
|
321
363
|
but the record still does not match, what moved is the **gate** it was judged
|
|
322
364
|
under — the design rules of a newer wairon release, the project's rule tuning,
|
|
323
365
|
`composition`, the network declaration, consumed contracts or a member's
|
|
324
|
-
approval — never your design: `
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
re-approves the unchanged design. A format-2 record whose specs moved reads stale
|
|
366
|
+
approval — never your design: `doctor --fix` still carries the record into the
|
|
367
|
+
design reading (its claim stays as certified), and the record is then judged as
|
|
368
|
+
any record from before gate parts is when the release changed (below). A format-2 record whose specs moved reads stale
|
|
328
369
|
once; one `wairon lock` clears it for good.
|
|
329
370
|
|
|
330
371
|
It writes **nothing into your spec tree**. The approval is one sha256 per spec
|
|
@@ -386,10 +427,27 @@ signed it off), and the root is judged by whether its pin of the member is still
|
|
|
386
427
|
the member's approved one: after a member re-locks it is not, and the verdict says
|
|
387
428
|
exactly that — no own spec moved, run `wairon lock` here to pin the new approval.
|
|
388
429
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
430
|
+
**Upgrading wairon never stales an approval by itself.** A lock records each
|
|
431
|
+
input of its gate identity on its own (`gateParts`: the design, this release's
|
|
432
|
+
built-in rules, the project's rule tuning, its packs, the network declaration,
|
|
433
|
+
`composition`, consumed contract pins, members' approvals). When only the
|
|
434
|
+
release's built-in rules moved — the approved design and every input the project
|
|
435
|
+
decides are as approved — the approved design is **re-validated under the new
|
|
436
|
+
rules in the same command**: clean at the `--ci` standard (no error, no warning
|
|
437
|
+
but the draft-related ones `--ci` waives; notices never count) the approval
|
|
438
|
+
*carries over* and nothing is written — `lock-check`, `status`, `sdd_get_status`,
|
|
439
|
+
`validate` and the family view all say *approved under <old>, re-validated under
|
|
440
|
+
<new>: still approved*, and one `wairon lock` refreshes the record's release
|
|
441
|
+
stamp (`restamped`) without asking for a re-approval. If the new rules find
|
|
442
|
+
errors or warnings, the approval is stale for exactly those: *the new release
|
|
443
|
+
finds N issue(s) in the approved design: …*. The project's own rule tuning,
|
|
444
|
+
packs, network and pins stay in the identity: changing one still stales the
|
|
445
|
+
approval, and the verdict names the input that moved (*the network declaration*
|
|
446
|
+
after `wairon network declare`). A record from before gate parts (taken by a
|
|
447
|
+
release that did not record them) is judged the same way when another release took it, no own
|
|
448
|
+
spec file moved since and every member pin is unchanged; its own rule tuning,
|
|
449
|
+
packs and pins it can only prove inside its digest, so one `wairon lock` (which
|
|
450
|
+
records the parts) closes that gap.
|
|
393
451
|
|
|
394
452
|
**It is optional by construction.** By default only a moved approval (or a
|
|
395
453
|
member's unapproved changes) refuses, and neither can happen in a project that
|
|
@@ -574,6 +632,37 @@ a `<component>-implementer` per component when
|
|
|
574
632
|
call. `customize` scaffolds the user-owned guidance file `.wai/agents/<id>.md`,
|
|
575
633
|
which later briefs for that agent include.
|
|
576
634
|
|
|
635
|
+
What a brief fences, for an agent that implements components (a component's
|
|
636
|
+
implementer, a subsystem owner):
|
|
637
|
+
|
|
638
|
+
- **Code write fence** (`codeFence`) — exact files, never a folder glob: the
|
|
639
|
+
files the agent's specs alone name — its implementations' and methods'
|
|
640
|
+
`sourcePath`s, `simPath` harnesses and `bindings`, and the files of the types
|
|
641
|
+
it owns (a component's `componentClass` types and the types only its own
|
|
642
|
+
contracts use; an owner's subsystem types). Planned files are marked
|
|
643
|
+
`(planned — create it)`. A file another component's (for an owner, another
|
|
644
|
+
subsystem's) specs also name belongs to no single fence, so sibling fences
|
|
645
|
+
never overlap.
|
|
646
|
+
- **Shared** (`sharedPaths`) — files the agent may touch but does not own: the
|
|
647
|
+
files its specs name that other specs name too (a shared type module,
|
|
648
|
+
created at its planned home exactly as its spec declares it), the setup files
|
|
649
|
+
from the project root down to its code (`package.json` and its lockfile,
|
|
650
|
+
`tsconfig.json`, `Cargo.toml`, `pyproject.toml`, `go.mod`), the package or
|
|
651
|
+
crate roots there (`index.ts`, `lib.rs`, `main.rs`, `mod.rs`, `__init__.py`)
|
|
652
|
+
and up to eight unnamed source files beside its own. The brief states the
|
|
653
|
+
rule: touch a shared file, or one no spec names (a helper, the composition
|
|
654
|
+
root, test setup), only for this component's needs and name it in the
|
|
655
|
+
report; one that would need a responsibility of its own is a design change —
|
|
656
|
+
stop and report it.
|
|
657
|
+
- **Read paths** (`readPaths`) — for an implementer: the system spec, its own
|
|
658
|
+
component, contract and implementation specs, the specs of the types its
|
|
659
|
+
contracts use (and of the types their fields name), its dependencies'
|
|
660
|
+
contracts and the files realizing them, and the pinned snapshot of each
|
|
661
|
+
external it reaches.
|
|
662
|
+
|
|
663
|
+
A consumer's brief also names, under *Externals used*, the binding modules its
|
|
664
|
+
implementations declare (see *Binding modules* under `wairon validate`).
|
|
665
|
+
|
|
577
666
|
### `wairon rules list`
|
|
578
667
|
List the SDD conformance rule registry — every rule group, the issue codes it
|
|
579
668
|
can emit, default severities, and any per-project overrides from
|
|
@@ -754,7 +843,7 @@ promise and how a consumer follows renames).
|
|
|
754
843
|
- A tree without an L0 is refused. The JSON Schema ships in the package as
|
|
755
844
|
`schemas/design-export-1.json`, and the library twin is `exportDesign()`.
|
|
756
845
|
|
|
757
|
-
### `wairon type rename-field <type> <field> <new-name
|
|
846
|
+
### `wairon type rename-field <type> <field> <new-name> [--dry-run] [--search <dirs...>]`
|
|
758
847
|
Renames a field of a type and respells every reference to it — each foreign
|
|
759
848
|
key `references: <type>.<field>` naming it, the type's own included. The field
|
|
760
849
|
keeps its place, type and description; its old name joins the field's rename
|
|
@@ -763,8 +852,14 @@ consumer holding the old name reads a rename, not a removal and an addition.
|
|
|
763
852
|
Traces and prose are never rewritten. It refuses, writing nothing, a type or
|
|
764
853
|
field that does not exist, a name that is not an identifier or that the target
|
|
765
854
|
language reserves for a field (`fn` in Rust), and a name another field of the
|
|
766
|
-
type holds or retired.
|
|
767
|
-
|
|
855
|
+
type holds or retired. It names every export a consumer reaches the field
|
|
856
|
+
through — an exported type that is, or embeds, the renamed type, and an
|
|
857
|
+
exported contract whose methods name one — and every consumer whose specs reach
|
|
858
|
+
one of them (the family in reach, members included): their code reading the
|
|
859
|
+
old name breaks. `--dry-run` prints all of that and writes nothing;
|
|
860
|
+
`--search <dirs...>` also scans these folders for consumer checkouts outside
|
|
861
|
+
the family. The MCP twin is `sdd_rename_field` (with `dryRun` and
|
|
862
|
+
`search`). As with any design edit, the next `wairon lock` approves it.
|
|
768
863
|
|
|
769
864
|
### `wairon method rename <component> <method> <new-name> [--dry-run] [--search <dirs...>] [--no-pin-symbol]`
|
|
770
865
|
Renames a contract method and retargets every reference to it in the bound
|
|
@@ -800,20 +895,25 @@ project's extension point takes its method names from that producer, so its
|
|
|
800
895
|
casing is not judged. The MCP twin is `sdd_rename_method` (with `dryRun`). The
|
|
801
896
|
next `wairon lock` approves the rename.
|
|
802
897
|
|
|
803
|
-
### `wairon method rename-param <component> <method> <param> <new-name
|
|
898
|
+
### `wairon method rename-param <component> <method> <param> <new-name> [--dry-run] [--search <dirs...>]`
|
|
804
899
|
Renames a parameter of a contract method. The parameter moves on every
|
|
805
900
|
interface of the component that declares the method, keeping its place, type,
|
|
806
901
|
description and optionality; its old name joins the parameter's rename trace
|
|
807
902
|
(`previousNames`), which `wairon export` shows as `formerly`, so a generator or
|
|
808
903
|
consumer reads a rename, not a signature change. Each signature is re-derived
|
|
809
904
|
from the params (a method whose `signatureFrom` names this one follows by
|
|
810
|
-
itself), and an HTTP endpoint path placeholder `{old}`
|
|
811
|
-
|
|
812
|
-
|
|
905
|
+
itself), and an HTTP endpoint path placeholder `{old}` (or `/:old`) is
|
|
906
|
+
respelled on the method's own binding and on every method whose
|
|
907
|
+
`signatureFrom` takes its signature — the URL a caller sends is unchanged.
|
|
908
|
+
Prose is never rewritten. It names the exports publishing the method and every
|
|
909
|
+
consumer that calls it there (the family in reach, members included).
|
|
910
|
+
`--dry-run` prints all of that and writes nothing; `--search <dirs...>` also
|
|
911
|
+
scans these folders for consumer checkouts outside the family. It refuses, writing nothing, a component, method or parameter that
|
|
813
912
|
does not exist (or a parameter the method takes from a `signatureFrom`), a name
|
|
814
913
|
that is not an identifier or that the target language reserves for a parameter
|
|
815
914
|
(`class` in TypeScript, `type` in Rust), and a name another parameter of the
|
|
816
|
-
method holds or retired. The MCP twin is `sdd_rename_param
|
|
915
|
+
method holds or retired. The MCP twin is `sdd_rename_param` (with `dryRun`
|
|
916
|
+
and `search`). The next `wairon lock` approves it.
|
|
817
917
|
|
|
818
918
|
### `wairon network flows|policy|diagram|check|why|declare|undeclare`
|
|
819
919
|
Networking derived from the design: the reach the validator already models
|
|
@@ -1072,7 +1172,7 @@ The local server offers 47 tools:
|
|
|
1072
1172
|
| Topology (read) | `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig` |
|
|
1073
1173
|
| Authoring | `sdd_initialize_system`, `sdd_add_subsystem`, `sdd_set_public_interfaces`, `sdd_add_component`, `sdd_define_interface`, `sdd_set_endpoints`, `sdd_write_narrative`, `sdd_add_type`, `sdd_update_spec`, `sdd_delete_spec` |
|
|
1074
1174
|
| Reading and checking | `sdd_get_spec`, `sdd_get_status`, `sdd_validate_tree` |
|
|
1075
|
-
| Renames and moves (in this tree) | `sdd_rename_component`, `sdd_rename_method`, `sdd_rename_param`, `sdd_rename_type`, `sdd_rename_field`, `sdd_move_methods` |
|
|
1175
|
+
| Renames and moves (in this tree) — each with `dryRun` | `sdd_rename_component`, `sdd_rename_method`, `sdd_rename_param`, `sdd_rename_type`, `sdd_rename_field`, `sdd_move_methods` |
|
|
1076
1176
|
| Members and family migrations (each takes `dryRun`) | `sdd_add_member`, `sdd_move_member`, `sdd_externalize_subsystem`, `sdd_promote_member`, `sdd_demote_member`, `sdd_internalize_member`, `sdd_attach_member`, `sdd_detach_member`, `sdd_adopt_member`, `sdd_rename_project`, `sdd_rename_member_alias` |
|
|
1077
1177
|
| Externals | `sdd_add_external`, `sdd_update_external`, `sdd_remove_external`, `sdd_pin_externals`, `sdd_get_externals_status` |
|
|
1078
1178
|
| Public surface (the producer's side) | `sdd_list_consumers`, `sdd_surface_diff` |
|
|
@@ -1194,8 +1294,8 @@ return the impact of every pack they applied in their results.
|
|
|
1194
1294
|
| `wairon externals pin [alias…] [--json]` | Pin declared externals into `.wai/externals/<alias>.yaml` and `.wai/externals.lock.yaml`. The snapshot is rewritten whenever anything it carries moved — not only the signatures the digest covers: a producer that added `abi: c`, changed a transport or a role, or recorded a rename is refreshed by a re-pin. Exits 1 when an alias could not be pinned (unresolved or unreachable — its previous pin stays) |
|
|
1195
1295
|
| `wairon externals status [--json]` | Each pin compared with its live producer per used member — `unchanged`, `changed`, `renamed` (with the new name), `removed`, `unlocked`, `unavailable` — and each external's health (`incompatible`, `not compared`, `drifted`, `ok`); a pinned snapshot that no longer carries what the producer says (a stale `abi`, transport or role) is `drifted`, never `ok`, and names the stale facts. A use the lock does not hold is still compared with the live producer: gone from it, it is `removed` or `renamed`. Git producers are fetched. The opt-in **live** gate: exits 1 when any external is incompatible, 2 when nothing is incompatible but something could not be compared (never a pass), 0 otherwise |
|
|
1196
1296
|
| `wairon externals list [--json]` | The declared externals, how each resolves and what is pinned; a malformed declaration, and an orphaned pin whose declaration is gone, are listed with their problem, never hidden |
|
|
1197
|
-
| `wairon surface export \| import \| list [--audience <level>] [--format native\|openapi] [--portal <id>] [--out <path>] [--source <path>]` | Exchange a public surface document: export this project's (native snapshot or one OpenAPI 3.1 document per portal), import one, or list them. OpenAPI defaults to the `project` audience, so every HTTP Portal of the project's own is described — one its L0 export table never exports included (a wider `--audience` narrows to what the table shares there); the native snapshot defaults to `instance`. In the OpenAPI document a path placeholder — `{name}`, or the Express spelling `:name`, which is rewritten to `{name}` — is a parameter `in: path`, the other params the query of a GET/DELETE or the JSON body of a POST/PUT/PATCH. The Portal's `basePath` is joined into every path (the document carries no `servers` entry: where it is served is deployment, not design). The parameter that carries the credential the Portal's `auth` binds (a bearer `token`, say) is left out of the parameters and the body — `security` describes it — and named under `x-wairon-credential-param` so a wairon reader can restore it. An unknown `--portal` is refused naming the portals the surface renders |
|
|
1198
|
-
| `wairon surface diff [--against <ref\|file>] [--json]` | The public-surface changelog: this project's export table now against the same table at its last **committed** approval (or at a git revision, or in a saved native snapshot from `surface export`) — every exported name and contract method `added`, `removed`, `renamed` (from its rename trace) or `changed` (signature or type shape), and how many a consumer may have to follow. What a producer writes release notes from before it re-locks; `wairon externals consumers --search <dir>` then says who uses what. Read-only. With no approval ever committed it says so (lock and commit first, or name `--against`). `sdd_surface_diff` is the same answer for an assistant |
|
|
1297
|
+
| `wairon surface export \| import \| list [--audience <level>] [--format native\|openapi] [--portal <id>] [--out <path>] [--source <path>]` | Exchange a public surface document: export this project's (native snapshot or one OpenAPI 3.1 document per portal), import one, or list them. OpenAPI defaults to the `project` audience, so every HTTP Portal of the project's own is described — one its L0 export table never exports included (a wider `--audience` narrows to what the table shares there); the native snapshot defaults to `instance`. In the OpenAPI document a path placeholder — `{name}`, or the Express spelling `:name`, which is rewritten to `{name}` — is a parameter `in: path`, the other params the query of a GET/DELETE or the JSON body of a POST/PUT/PATCH. The Portal's `basePath` is joined into every path (the document carries no `servers` entry: where it is served is deployment, not design). The parameter that carries the credential the Portal's `auth` binds (a bearer `token`, say) is left out of the parameters and the body — `security` describes it — and named under `x-wairon-credential-param` so a wairon reader can restore it. Each operation answers the verb's conventional success code — `204` with no content for a method returning nothing, `201` for a POST, else `200` — and a method returning `result<T, E>` also answers a `default` error response carrying E's schema (`surface import` reads both back). A `custom` auth becomes an `apiKey` scheme only when the Portal's `auth.name` (with `auth.in`, default header) names where the credential travels; naming none, it is an `http` scheme `custom` with the design's description — no header name is invented. Two operations of one Portal on the same verb and path (placeholders compared by position) are refused naming both, never dropped (`validate` reports them as `ENDPOINT_ROUTE_DUPLICATE`). Every status line goes to stderr, so `surface export --format openapi > api.json` writes the JSON alone; an export that publishes nothing, or of a design `validate` refuses (its error count and codes), is a warning, never a ✔. An unknown `--portal` is refused naming the portals the surface renders |
|
|
1298
|
+
| `wairon surface diff [--against <ref\|file>] [--json]` | The public-surface changelog: this project's export table now against the same table at its last **committed** approval (or at a git revision, or in a saved native snapshot from `surface export`) — every exported name and contract method `added`, `removed`, `renamed` (from its rename trace) or `changed` (signature or type shape), and how many a consumer may have to follow. What a producer writes release notes from before it re-locks; `wairon externals consumers --search <dir>` then says who uses what. Read-only. With no approval ever committed it says so (lock and commit first, or name `--against`). An L0 export entry added since that publishes nothing (a wildcard over a subsystem that publishes nothing) is listed as such, never read as no change. `--against` takes a native snapshot or a git revision: an OpenAPI document, or a file the native schema does not read, is refused in one line. `sdd_surface_diff` is the same answer for an assistant |
|
|
1199
1299
|
| `wairon produce <notion\|miro> [--page <id>] [--token <token>]` | Project the local spec tree to Notion or Miro (the token comes from `--token`, the environment, else a prompt; nothing is stored) |
|
|
1200
1300
|
|
|
1201
1301
|
## Tooling
|
|
@@ -510,11 +510,13 @@ Repository whose Store it reads (`UNOWNED_QUERY`).
|
|
|
510
510
|
|
|
511
511
|
### gateway: a Portal variant
|
|
512
512
|
> A gateway is a **Portal** with the built-in `gateway` variant: a Portal that
|
|
513
|
-
> **authenticates, authorizes, validates or rate-limits before it dispatches**,
|
|
514
|
-
>
|
|
515
|
-
>
|
|
516
|
-
>
|
|
517
|
-
>
|
|
513
|
+
> **authenticates, authorizes, validates or rate-limits before it dispatches**, and
|
|
514
|
+
> declares its inbound authentication in `auth`. Checking the credential `auth`
|
|
515
|
+
> declares — reading it from the request and verifying it — is the gateway's own
|
|
516
|
+
> admission step and may be a step of its narrative. A check that needs state or
|
|
517
|
+
> policy is logic it calls: Orchestrators it depends on — typically read logic
|
|
518
|
+
> that gathers the caller's grants and pure logic that rules on them — never
|
|
519
|
+
> members it owns. A Portal with no ingress checks needs no variant.
|
|
518
520
|
>
|
|
519
521
|
> Inside a declared **network**, the gateway is also the only Portal that may be
|
|
520
522
|
> entered from outside it, and the only one a modelled call from outside may land
|
|
@@ -576,7 +578,7 @@ can sit in either class, which is why the class is a field and not a variant.
|
|
|
576
578
|
| `projector` | Orchestrator | `pure` when handed its source, `read` when it loads it | source model → self-contained derived view (snapshot, graph, artifact, digest) | ≤1 read facade or parameters-only; recomputed per call, owns nothing, writes nothing. NOT an Index: an Index is a maintained read model over an owned Store (or, exceptionally, over another Index of the same Repository) |
|
|
577
579
|
| `composer` | Orchestrator | `pure` when handed its values, `read` when it loads them | templates + values → authored text/file map | returns content, never writes or executes it; degrades gracefully on missing optional inputs |
|
|
578
580
|
| `codec` | Orchestrator | `pure` | format ↔ format, bidirectional | pure whole-value translation; inbound half validates + safety-checks; both directions in one component so the round-trip stays testable |
|
|
579
|
-
| `gateway` | Portal | — | a Portal that authenticates, authorizes, validates or rate-limits before dispatching |
|
|
581
|
+
| `gateway` | Portal | — | a Portal that authenticates, authorizes, validates or rate-limits before dispatching | checks the credential its `auth` declares itself and calls logic needing state or policy, before it dispatches; writes still route through Orchestrators |
|
|
580
582
|
|
|
581
583
|
A variant is **promoted to a first-class stereotype** only when independent
|
|
582
584
|
projects/packs keep re-registering it, or when its edge rules exceed what guidance
|