@wairon/cli 5.1.1-dev.107 → 5.1.1-dev.109

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.
@@ -0,0 +1,193 @@
1
+ # Contributing to wairon
2
+
3
+ The conventions for making a delegated change — never hand-edit `.wai/`, read
4
+ every write back, the lock is the human's, measure before repairing, prove a
5
+ behaviour by revert without `git checkout` — are **not** in this file. They ship
6
+ with the tool, as the **Working conventions** section of the `sdd-implement`
7
+ skill (`src/templates/skills/sdd-implement.md`, installed as
8
+ `.agents/skills/sdd-implement/SKILL.md` and its siblings). Read them there: they
9
+ are true of any wairon project, and the brief-writing half is in `sdd-delegate`.
10
+
11
+ This file is only what is true of **this repository** and would be wrong to ship
12
+ to a wairon user.
13
+
14
+ ---
15
+
16
+ ## Line endings: CRLF in the working tree, LF in the index
17
+
18
+ Git stores LF and checks out CRLF here. `.gitattributes` pins only `*.sh` to LF
19
+ (a CRLF shebang breaks `#!/usr/bin/env bash` on the Linux CI runners).
20
+
21
+ Most tools that write files emit LF, which turns a three-line change into a
22
+ whole-file diff. After any scripted or tool-driven write, normalise the file back
23
+ and confirm `git diff --stat` shows only the lines you meant.
24
+
25
+ **The rule is preserve what the file has, not "make it CRLF".** The heading above
26
+ describes the common case, not a uniform one: measured across `src/**/*.ts`, 212
27
+ files are CRLF, 52 are LF and 3 are mixed. `install.ps1`, `src/commands/aliases.ts`
28
+ and several tests are LF. Because `core.autocrlf` is true, git normalises on the way
29
+ in and `git status` reads clean either way, so the difference is invisible until a
30
+ scripted edit built for the wrong ending fails to match its anchor — or silently
31
+ rewrites the whole file. Detect each file's endings before editing it and write back
32
+ what was there.
33
+
34
+ Most obvious ways to check for carriage returns lie, including one that wraps the correct check:
35
+
36
+ | Command | What it actually does |
37
+ |---|---|
38
+ | `grep '\r' f` | In a basic regular expression `\r` is the letter `r`. It matches every line containing an `r`, so an LF-only file "has carriage returns". |
39
+ | `grep $'\r' f` | Passes a real CR byte — but grep opens the file in text mode here and strips CR before matching, so it reports **0** on a genuinely CRLF file. |
40
+ | `grep -Uc $'\r' f` | Correct. `-U` suppresses the text conversion, so the CR survives to the match. |
41
+ | `file f` | Correct. Says `with CRLF line terminators`, or says nothing about them. |
42
+ | `x=$(grep -Uc $'\r' f)` | Lies, even though the command inside it is the correct one. Capturing it collapses the lone-CR argument to an empty pattern, which matches every line, so what comes back is the file's **line count** whatever its endings are. Run the check unwrapped, or put the CR in a variable first (`CR=$'\r'; grep -Uc "$CR" f`). |
43
+
44
+ Two traps for scripted edits specifically:
45
+
46
+ - **Never let a normaliser touch text that itself contains `\r` or `\n` escapes.**
47
+ A script that rewrites every line ending to CRLF will also rewrite the escape
48
+ sequence you meant to insert, putting a real carriage return inside a string
49
+ literal — which is an unterminated-string parse error, found at build time
50
+ rather than at write time. Build such text with `String.fromCharCode(13)`.
51
+ - **A quoted heredoc still loses doubled backslashes here.** `<<'EOF'` keeps a lone
52
+ backslash but collapses `\\` to `\`, so `p.replace(/\\/g, "/")` arrives as
53
+ `p.replace(/\/g, "/")` — an unterminated character class, and a syntax error at run
54
+ time. **It does not always fail loudly.** The same collapse inside an anchor string turns
55
+ `\\n` into a real newline, so the text simply does not match and the edit is skipped
56
+ with no error at all — which is how it was found the second time. Write the script with
57
+ the file-writing tool, or avoid doubled backslashes, and check the edit applied.
58
+
59
+ Byte counts show it too: a CRLF file loses exactly one byte per line when it is
60
+ flattened, so `wc -c` before and after a rewrite is evidence.
61
+
62
+ **Four generated files are deliberately LF — do not "fix" them:**
63
+
64
+ ```
65
+ .wai/context/domains.md
66
+ .wai/context/wairon-guide.md
67
+ .wai/docs/topology.md
68
+ .wai/rules/topology.yaml
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Run wairon from this checkout, not the global install
74
+
75
+ ```
76
+ npm run build
77
+ node dist/cli/index.js <command>
78
+ ```
79
+
80
+ A globally installed `wairon` can lag this working tree — including its
81
+ validator, so it can pass a tree the code here rejects, or lock one under an
82
+ older rule set. The MCP server also runs `dist/`, so build *before* reconnecting
83
+ it.
84
+
85
+ ---
86
+
87
+ ## The gates, and today's baseline
88
+
89
+ | Gate | Baseline |
90
+ |---|---|
91
+ | `npx tsc --noEmit` | clean |
92
+ | `npx vitest run` | 289 files / 4755 tests |
93
+ | `npx vitest run --config vitest.e2e.config.ts` | 7 files / 33 tests |
94
+ | `npm run build` | clean |
95
+ | `npm run build:web` | clean (typechecks the SPA) |
96
+ | `node dist/cli/index.js validate` | 0 errors, 0 warnings, 0 notices; register 303 findings / 406 units |
97
+ | `node dist/cli/index.js validate --ci` | passes |
98
+
99
+ The counts are a floor, not a target — they move as the suite grows, so update
100
+ this table when they do. `validate` also prints the **conformance debt register**
101
+ (findings carried under `rules.conformance.carried`); it is informational and
102
+ does not fail the gate, but a number that moved is a result worth reporting.
103
+
104
+ Some tests assert on the text of the shipped skill and agent templates
105
+ (`tests/core/template-vocabulary.test.ts`, `tests/core/skill-composition.test.ts`).
106
+ Changing that text is *supposed* to fail them — that is the assertion doing its
107
+ job. Fix it to match the new text; loosening it to match anything removes the one
108
+ thing keeping the templates and the validator's vocabulary in step.
109
+
110
+ A rule's `summary` is spec data, not a code string. `tests/core/rule-catalog.test.ts`
111
+ proves the registry's codes and the L3 `findings[].summary` values on
112
+ `iheuristic_rules.yaml` (and its siblings) are ONE list, so changing a summary in
113
+ the rule file alone fails the suite. Change it through the authoring tools first and
114
+ let the code string follow. This catches every contributor who touches a rule's
115
+ codes, and the suite is currently the only place that says so.
116
+
117
+ ---
118
+
119
+ ## A stale MCP server
120
+
121
+ When the build on disk changed after the server started — which is exactly what
122
+ `npm run build` does mid-session — every `sdd_*` answer carries
123
+ `staleServer: true` and a `⚠ STALE SERVER` banner, and `sdd_get_status` leads
124
+ with it. What happens to a write depends on what the rebuild changed:
125
+
126
+ - **The spec or tool schemas changed** (the server compares its own schema
127
+ fingerprint with the one the build on disk computes): every spec write, dry
128
+ runs included, is REFUSED and writes nothing, and the answers carry
129
+ `writesRefused: true`. A stale process with old schemas silently drops the
130
+ fields a newer schema introduced, and has — three times.
131
+ - **Only the build changed:** writes still go through, under the warning.
132
+
133
+ Either way the cure is the human reconnecting the server
134
+ (`/mcp reconnect wairon`). So author specs *before* a rebuild that changes a
135
+ schema. For a server old enough not to carry the flag, the tell is the shape of
136
+ the answer: a current `sdd_update_spec` returns a structured change report
137
+ naming what moved, an old one a single sentence.
138
+
139
+ ---
140
+
141
+ ## Narrative jumps: use labels, not hand-counted step numbers
142
+
143
+ Inserting a step into an L5 narrative renumbers every later step, and
144
+ `sdd_update_spec` relocates the jump fields with it. That relocation has gone
145
+ wrong before, and the failure is quiet: a branch keeps a plausible number that
146
+ now points one step past where it meant to land, and nothing but a careful
147
+ re-read catches it.
148
+
149
+ Only STORED jumps relocate. A jump the delta itself writes — on a step it
150
+ inserts, appends or edits — is read in the numbering the whole delta leaves
151
+ behind, after all of its inserts and deletes, so write it as the final
152
+ narrative will number it.
153
+
154
+ Every jump-by-number field has a symbolic twin resolved at write time —
155
+ `label` on the step, and `onTrueLabel` / `onFalseLabel` / `toLabel` / `endLabel`
156
+ on the jump. A labelled jump survives an insert because it never named a number.
157
+ Prefer labels whenever a narrative is likely to grow.
158
+
159
+ When a numeric jump is edited anyway, verify the flow afterwards rather than
160
+ trusting the change report:
161
+
162
+ ```
163
+ awk '/^ - name: METHOD/{f=1} f&&/^ - name: /&&!/METHOD/{exit} f' <impl>.yaml \
164
+ | grep -E 'stepNumber|type:|onTrueStep|onFalseStep|toStep|outcome'
165
+ ```
166
+
167
+ That prints the control flow on its own, which is short enough to read in one
168
+ pass and is where a mis-relocated target is obvious.
169
+
170
+ ---
171
+
172
+ ## Untracked paths are somebody else's work in flight
173
+
174
+ Run `git status` before you start. This repo regularly carries an uncommitted
175
+ design folder under `docs/design/` while an investigation is open (at the time of
176
+ writing, `docs/design/chained-subsystems/`). Do not touch, commit, stash, or
177
+ clean anything untracked that your task did not name.
178
+
179
+ ---
180
+
181
+ ## Where repo-local guidance lives — and where it cannot
182
+
183
+ `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md` at the repo root
184
+ are rewritten wholesale by `wairon generate` (`writeRootGuideDelegator`), and
185
+ `.claude/` is gitignored entirely. Neither survives, so neither is a home for
186
+ anything a future contributor or agent should inherit. That home is **this file**
187
+ — and a brief points at it by name rather than paraphrasing it.
188
+
189
+ ---
190
+
191
+ ## Bugs and questions
192
+
193
+ Actively developed. Open an issue.
package/README.md CHANGED
@@ -223,6 +223,11 @@ git add .wai && git commit -m "Approve the design"
223
223
  # --strict also fails when .wai/lock.json is missing or a member project was
224
224
  # never approved; plain lock-check only fails an approval that no longer matches.
225
225
  wairon lock-check --strict
226
+ npm ci # TS/JS project with code: install the project's dependencies first
227
+ # (pnpm install --frozen-lockfile / yarn install --immutable), so the
228
+ # gate reads your code with YOUR TypeScript (5 or 6); without one —
229
+ # or on TypeScript 7 — it reads with the copy wairon ships. The
230
+ # reusable workflow does this for you.
226
231
  wairon validate --ci # the conformance gate, run at the FAMILY ROOT (the project that
227
232
  # declares the members): there it is the family run, which judges
228
233
  # the network proofs; externals are judged against their pins
@@ -263,13 +268,15 @@ See [docs/cli.md](docs/cli.md). Summary:
263
268
  | `wairon diagram [--all] [--canvas] [--drawio] [--excalidraw] [--sequence <comp:method>]` | Mermaid, interactive canvas, and editable draw.io/Excalidraw exports |
264
269
  | `wairon network flows \| policy \| diagram \| check \| why` | Networking derived from the design: allowed flows, Kubernetes `NetworkPolicy`, a trust-boundary diagram, live-flow checks ([details](docs/network.md)) |
265
270
  | `wairon rules list` | The conformance rule registry (the architecture linter) |
266
- | `wairon pack init \| build \| install \| use \| unuse \| impact \| sync \| bundle \| which \| list \| add \| remove` | Extension packs: injected profiles, language tables, and rules |
271
+ | `wairon pack init \| build \| install \| use \| unuse \| impact \| sync \| bundle \| which <name> \| list \| add \| remove` | Extension packs: injected profiles, language tables, and rules |
267
272
  | `wairon member …` / `wairon subsystem externalize` / `wairon project rename` | Members (parts and projects) and the family migrations |
268
- | `wairon externals add \| pin \| status \| list` | Declare, pin and check the externals a project consumes (`status` is the opt-in live gate) |
273
+ | `wairon externals add \| pin \| status \| list \| remove \| use \| consumers [--search <dirs>]` | Declare, pin and check the externals a project consumes (`status` is the opt-in live gate); from a producer, `consumers --search ..` finds who consumes it, sibling checkouts included |
274
+ | `wairon surface export \| import \| list \| diff [--against <ref\|file>]` | Exchange a public surface (native snapshot or OpenAPI, one document per portal); `diff` is the public-surface changelog since the last committed approval |
275
+ | `wairon method rename-param` / `wairon type rename-field` | Rename a contract parameter or a type field and respell every reference; the old name joins the rename trace |
269
276
  | `wairon domains list \| scan \| add \| remove` | Domains (subsystem-derived + free-standing) |
270
277
  | `wairon skills list \| install` | Manage the SDD skills installed into your tools |
271
278
  | `wairon lock [-y]` | Validate the design as complete and record its approval in `.wai/lock.json`, code findings beside it; no spec file is rewritten |
272
- | `wairon lock-check [--strict]` | Merge gate: is the design in this tree the design that was approved? Importable as a [reusable workflow](https://github.com/SYW-Apps/Waffle-AIron/blob/main/.github/workflows/lock-check.yml) |
279
+ | `wairon lock-check [--strict]` | Merge gate: is the design in this tree the design that was approved? Importable as a [reusable workflow](https://github.com/SYW-Apps/Waffle-AIron/blob/dev/.github/workflows/lock-check.yml) (on `dev` and the dev tags until 6.0.0 ships it on `main`; pin a tag — see [docs/cli.md](docs/cli.md#using-it-in-github-actions)) |
273
280
  | `wairon mcp serve \| install \| status` | The wairon MCP server (`sdd_*` tools) |
274
281
  | `wairon serve [--port] [--data-dir] [--no-auth]` | Self-host: HTTP MCP for many isolated projects + admin plane |
275
282
  | `wairon host unit \| project \| permission \| key \| lock \| doctor \| packs \| git \| producer \| secret \| demo` | Administer the hosting server (units, projects, permissions, keys, the state-scoped lock) |
@@ -287,12 +294,12 @@ See [docs/cli.md](docs/cli.md). Summary:
287
294
  - [Vision](docs/vision.md) — long-term direction
288
295
  - [CLI Reference](docs/cli.md) — all commands and MCP tools
289
296
  - [Design export](docs/design-export.md) — the `wairon export` JSON format for generators and translators
290
- - [Supervisor doctrine](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/supervisor-doctrine.md) — how Supervisors and Actors may depend on data and effects
297
+ - [Supervisor doctrine](docs/design/supervisor-doctrine.md) — how Supervisors and Actors may depend on data and effects
291
298
  - [Hosted server](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/hosted-mcp-server.md) — self-host wairon over HTTP (Docker, auth, sizing)
292
- - [Pack scoping](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/pack-scoping.md) — the pack store, per-project selection, and reproducibility (design)
293
- - [Connecting-agent entrypoint](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/connecting-agent-entrypoint.md) — MCP `instructions`, prompts, and skill composition
294
- - [Execution budgets](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/execution-budgets.md) — what each agent's work costs to do, derived alongside what it owns (design)
295
- - [Approval baselines](https://github.com/SYW-Apps/Waffle-AIron/blob/main/docs/design/approval-baseline.md) — what `lock` approves, and why it stopped rewriting your spec tree (design)
299
+ - [Pack scoping](docs/design/pack-scoping.md) — the pack store, per-project selection, and reproducibility (design)
300
+ - [Connecting-agent entrypoint](docs/design/connecting-agent-entrypoint.md) — MCP `instructions`, prompts, and skill composition
301
+ - [Execution budgets](docs/design/execution-budgets.md) — what each agent's work costs to do, derived alongside what it owns (design)
302
+ - [Approval baselines](docs/design/approval-baseline.md) — what `lock` approves, and why it stopped rewriting your spec tree (design)
296
303
  - [Extending wairon](docs/extending-wairon.md) — extension packs & wrapper products (with a [working example](https://github.com/SYW-Apps/Waffle-AIron/blob/main/examples/wrapper/))
297
304
  - [Templates](docs/templates.md) — agent rendering templates
298
305
  - [Standards](docs/standards/INDEX.md) — the architecture standards the SDD model is built on
@@ -311,7 +318,7 @@ validation), js-yaml, the MCP SDK, and Vitest. Bundled with tsup.
311
318
  Actively developed. For bugs or questions, open an issue.
312
319
 
313
320
  Working on this repo (or delegating work in it) — see
314
- [CONTRIBUTING.md](https://github.com/SYW-Apps/Waffle-AIron/blob/main/CONTRIBUTING.md) for what this checkout does differently: line
321
+ [CONTRIBUTING.md](CONTRIBUTING.md) for what this checkout does differently: line
315
322
  endings, the gate commands and their baselines, and the stale-MCP-server tell.
316
323
  The conventions that hold for *any* wairon project ship in the `sdd-implement`
317
324
  and `sdd-delegate` skills.