@mmerterden/multi-agent-pipeline 13.1.0 → 13.2.0

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/CHANGELOG.md CHANGED
@@ -16,6 +16,58 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [13.2.0] - 2026-07-28
20
+
21
+ The iOS coding standard reaches all three hosts, and works outside the pipeline.
22
+
23
+ The 95-rule registry lived as a pipeline-local routine. Two consequences, both
24
+ measured: only Claude Code could reach it, because `local-only` commands are
25
+ deliberately never synced (verified absent on Copilot CLI and Codex CLI), and it
26
+ applied only when the routine was invoked explicitly - nothing pulled the rules in
27
+ while Swift was being written. The plugin's own `reference/code-style` skill is 154
28
+ lines of prose with zero rule IDs, so the registry was not reachable that way either.
29
+
30
+ ### Added
31
+
32
+ - **`ios-coding-standard` skill**, authored once in `pipeline/skills/shared/external`
33
+ and routed into the iOS stack plugin's `knowledge/` by the existing generator. That
34
+ one source now lands as a skill on Claude Code and Copilot CLI (description-matched,
35
+ so the rules are present while code is being written) and ships inside the plugin for
36
+ Codex. `references/` carries the registry, the teaching doc, a SwiftLint config for
37
+ the mechanically-enforceable subset, and a module-scoped lint runner with a baseline
38
+ mode.
39
+ - **`/multi-agent:ios-coding-standard`** promoted out of `local-only` into the command
40
+ inventory (43 → 44), with its Copilot twin, so the module-wide audit procedure exists
41
+ on every host. It reads its rules from the skill rather than carrying a copy.
42
+
43
+ ### Changed
44
+
45
+ - The registry was generalised before promotion: company hostnames, an internal URL
46
+ scheme, internal model and endpoint names, and repo-specific module paths were
47
+ replaced. `smoke-personal-data.sh` is clean across all six promoted files. Rule
48
+ bodies describe shapes (a mapper doing arithmetic, a logger interpolating a token)
49
+ rather than named modules, so the registry applies unchanged to any SwiftUI codebase;
50
+ a project layers its own vocabulary through a `modules/<Module>.yml` overlay.
51
+ - Four over-long skill descriptions trimmed (`ios-simulator` 839→578,
52
+ `swift-api-design-guidelines` 738→468, `swiftlint` 725→473, and the three new ones).
53
+ The always-on description surface was at 78,079 of 78,000 bytes before this change;
54
+ adding a 44th command and a new skill needed 859 bytes it did not have. Each trim
55
+ removed duplication between a description's "covers" enumeration and its trigger
56
+ clause, which is where the routing value actually is. Surface is now 77,734.
57
+
58
+ ### Notes
59
+
60
+ - **Codex lists plugin skills by name only.** Measured: all 76 plugin-provided skills
61
+ render with an empty description in Codex's skills block, while the 5 system skills
62
+ render theirs. So on Codex a plugin skill is invoked explicitly rather than matched
63
+ on description. That is the reason the registry is authored in `shared/external`
64
+ instead of hand-placed in the plugin: the pipeline install path gives Claude Code and
65
+ Copilot CLI a description-matched skill, and the plugin copy covers Codex.
66
+ - The iOS plugin now declares 143 skills and Codex surfaces about half. That overflow
67
+ predates this change and `tools/validate.py` warns on it; routing `knowledge/` behind
68
+ the plugin's `index` skill is the fix and is separate work.
69
+
70
+
19
71
  ## [13.1.0] - 2026-07-27
20
72
 
21
73
  Six gates, from one branch that spent half its commits on rework.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "13.1.0",
3
+ "version": "13.2.0",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,257 @@
1
+ ---
2
+ description: "Audit an iOS module against the shared coding-standard registry (95 stable-ID rules), produce a remediation plan, then hand off to dev/dev-local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions."
3
+ description-tr: "Bir iOS modulunu paylasilan kodlama-standardi registry'sine (95 sabit-ID kural) gore denetler, duzeltme plani cikarir, sonra dev/dev-local'e devreder. Bir modulde standart gecisi icin, ya da bir review'un gorus yerine kural ID'si istedigi durumda kullan."
4
+ argument-hint: "[module name or path]"
5
+ allowed-tools: Skill, Bash, Read, Edit, Write, AskUserQuestion
6
+ ---
7
+
8
+ # multi-agent ios-coding-standard — Module audit → plan → dev handoff
9
+
10
+ **Input**: $ARGUMENTS — optionally a module name or path. When absent, Phase 1 discovers and asks.
11
+
12
+ This routine is the **procedure**. The rules live in the `ios-coding-standard` skill, whose registry is
13
+ `references/rules.yml` and whose teaching doc is `references/STANDARD.md`. Load that
14
+ skill first; it is installed on every host and carried by the iOS stack plugin, so
15
+ there is one registry rather than a copy per CLI. Never restate a rule here — cite its ID. A rule that is not in the
16
+ registry is not a rule; if the audit needs one, propose it as `status: proposed` and say so.
17
+
18
+ Read-only + planning. This routine never edits source — the dev pipeline does.
19
+
20
+ **Goal.** A developer new to the repo can open any file in the module and understand it without a
21
+ guided tour, and nothing sensitive leaks on the way. Rank every finding by "does fixing this
22
+ shorten the time to first productive PR, or close a real risk?"
23
+
24
+ Render assistant-facing prose in `outputLanguage`. External payloads (branch names, plan file,
25
+ dev task) stay English.
26
+
27
+ ---
28
+
29
+ ## Phase 1 — Discover and pick the module
30
+
31
+ 1. Repo root via `git rev-parse --show-toplevel`.
32
+ 2. **Discover module roots** — any directory with a `Package.swift`, a `Sources/` subtree or an
33
+ `.xcodeproj`. Sweep the container dirs that exist (`Domains/`, `Packages/`, `Modules/`,
34
+ `Features/`, `Core/`, `Common/`, top-level module dirs) one level deep, then two if empty.
35
+ 3. Record per module: name · path · Swift files · lines · governance docs present · SPM targets.
36
+ Keep this **module registry** for the whole run — `MOD-*` greps every import against it.
37
+ Classify each module's role, because the role decides which dependency edges are legal:
38
+ **feature** · **core/common** · **seam** (cross-module contracts/bridges/navigation) ·
39
+ **composition root** (legitimately knows every module).
40
+ 4. If `$ARGUMENTS` resolves to a module, skip the prompt. Otherwise `AskUserQuestion`
41
+ (single-select, `outputLanguage`), documented modules first, each option showing file/line
42
+ counts and `docs: yes/no`. If the list exceeds the question limit, group the smallest into an
43
+ "other" option and ask again — never truncate silently.
44
+ 5. Scope = the whole module's sources across all its targets, unless the user narrows it.
45
+
46
+ ## Phase 2 — Resolve what applies to THIS module
47
+
48
+ ### 2a. The registry is the standard — in-module prose docs are NOT consulted
49
+
50
+ The skill's `references/rules.yml` (+ a project `modules/<Module>.yml` overlay) is the single source of truth. **Do not read, cite, or
51
+ derive rules from a module's own `*-CLAUDE.md`, `docs/` set or any other in-repo prose.** They are
52
+ being retired precisely so there is one place a rule can live; consulting them re-creates the
53
+ split this command exists to remove. If such a file exists and contradicts the registry, that is a
54
+ finding against the file, not against the code.
55
+
56
+ The only in-repo inputs are **code and manifests**: sources, tests, `Package.swift`, `Info.plist`,
57
+ the specs the generator reads, and the module registry from Phase 1.
58
+
59
+ ### 2b. Per-module overlay, then inference
60
+
61
+ 1. **`modules/<Module>.yml`** beside the project's own config carries what code cannot state: the vocabulary
62
+ bindings, the module's role, prohibitions that currently have **zero** instances, deliberate
63
+ carve-outs, name locks, and the real verification command. Registry-backed, so its findings may
64
+ be `blocking`.
65
+ *Why prohibitions need to be written down:* inference reads dominant patterns, and a rule
66
+ obeyed everywhere has no counter-example to infer from. A ban at 100% compliance is invisible
67
+ to a scan — it survives only if the overlay states it.
68
+ 2. **Repo-level rules** (root `CLAUDE.md`, contributing docs) apply to every module.
69
+ 3. **The module's own dominant pattern is its de-facto convention.** Count variants per dimension;
70
+ the minority instances are the finding. Internal consistency beats conformity to a sibling.
71
+ 4. No majority (even split, single instance) → not a finding. Record an Open Question.
72
+
73
+ Findings from step 3 are tagged `inferred` and capped at **suggestion**. Only registry- or
74
+ overlay-backed rules may be `blocking`.
75
+
76
+ **When the overlay is missing**, generate a draft from code evidence, print it for confirmation,
77
+ and mark every slot it could not derive — a frozen UI target, a name lock, a carve-out and an
78
+ operational constraint all look like ordinary code from the outside. Do not guess them; list them
79
+ as Open Questions.
80
+
81
+ ### 2c. Vocabulary
82
+
83
+ Bind each slot to a real symbol by grepping the module, then the reference module. **An unbound
84
+ slot disables its rules** — never invent a name, never import a sibling's.
85
+
86
+ `HandlerName` · `EventParam` · `CoordinatorType` · `ResultType` / `ErrorType` / `ErrorFactory` ·
87
+ `DIResolver` / `DIConfigurator` · `RequestModelSuffix` / `ResponseModelSuffix` / `TransportSuffix` ·
88
+ `MapperShape` · `SafeEnumProtocol` / `UnknownCase` · `CopySurface` · `AnalyticsSurface` ·
89
+ `A11yIdentifierSource` · `GeneratedServiceRoot` · `SharedComponentsDir` / `SharedEntitiesDir` ·
90
+ `MicroComponentLibrary` · `FrozenUITarget` · `DesignTokenNamespaces` · `CalendarDayHelper` ·
91
+ `LoggerAPI` · `FileHeaderShape` · `ScreenRoot` · `ScreenRoleSuffixes` · `AsyncStyle` ·
92
+ `CompositionRoot` · `SeamLayer` · `ModuleEntrySurface` · `ConcurrencyPosture` · `LintToolchain` ·
93
+ `CredentialStore` (the module's Keychain wrapper, if any).
94
+
95
+ Print the resolved table before scanning so a wrong binding is caught early.
96
+
97
+ ### 2d. Sensitive-data inventory — required before any SEC rule runs
98
+
99
+ The `SEC-*` rules are written against the **data classes** in `references/rules.yml →
100
+ sensitive_data_classes`, never against one module's field names. Resolve the module's concrete
101
+ instances of each class: grep entities, request/response models, storage calls and analytics
102
+ events for the values the module actually handles, and map each to its class.
103
+
104
+ Produce the inventory as a table — **class · concrete symbols · where it enters · required
105
+ lifetime (transient / survives-restart / survives-reinstall) · where it is actually stored ·
106
+ where it is logged · where it leaves (network, analytics, pasteboard, another module)**.
107
+
108
+ The **lifetime column is the one that decides SEC-01**, and it is filled by walking
109
+ `references/rules.yml → persistence_decision`, not by looking at what the code currently does. Compare it
110
+ against the storage column and report both mismatches:
111
+ - required transient but persisted anywhere (including the Keychain) → **over-persistence**;
112
+ - required to survive a restart but stored outside the Keychain → **under-protection**.
113
+
114
+ Then generate the SEC lint patterns *from this inventory*, so the checks match this module's real
115
+ vocabulary. A class with no instance here yields no finding; a symbol you cannot classify, or one
116
+ whose required lifetime nobody can state, is an Open Question, not a silent pass.
117
+
118
+ This table is a deliverable in its own right, independent of whether any violation is found.
119
+
120
+ ## Phase 3 — Scan
121
+
122
+ Apply the rules in `references/rules.yml` that survived Phase 2 binding, plus the module's own `validation`
123
+ gate. Tag every finding: **rule ID · severity · source (`registry` / `doc` / `inferred`) ·
124
+ onboarding impact (high/medium/low)**.
125
+
126
+ ### Coverage gate — on disk, not in memory
127
+
128
+ Before scanning, write `.<module>-audit-coverage.tsv` to the scratch dir: one row per target
129
+ (every screen under `ScreenRoot`, every shared component, every entity, every file in every
130
+ target) with columns `path · status · rules-applied`. Mark rows as you go.
131
+
132
+ - Each target is **audited** or **explicitly skipped with a reason** (generated, fixture,
133
+ vendored). No third state.
134
+ - **The run is incomplete while any row is unmarked** — and the file, not a claim, is the proof.
135
+ Chunk the module if it is too large for one pass; the checklist survives a context reset.
136
+ - No sampling. A partial audit reported as complete certifies the unscanned screens as clean.
137
+
138
+ ### Grep hygiene
139
+
140
+ Registry `mechanism` patterns are line-based and match doc comments. Filter
141
+ `^[[:space:]]*//` — BSD grep does not understand `\s`, so a `\s*//` filter silently lets `/// …`
142
+ through — then open each surviving hit and confirm it is real code. Report pre-filter and
143
+ post-verification counts separately; a table padded with comment matches destroys trust.
144
+
145
+ ### Judgement rules need evidence, not assertion
146
+
147
+ For any rule marked `enforcement: judgement`, a finding must carry the measurement its `check`
148
+ names — a reference count, a call-site count, a consumer count, a file list. **No count, no
149
+ finding.** This is what keeps a subjective rule from becoming an opinion.
150
+
151
+ Every judgement rule has a worked ✗/✓ pair in `EXAMPLES.md`, keyed by ID. Cite it in the finding's
152
+ proposed fix rather than re-describing the shape — the developer applying the fix should be
153
+ reading the same picture the audit used.
154
+
155
+ ### Module-specific overrides
156
+
157
+ The module's own docs win over the registry, including where they contradict a sibling module.
158
+ Never apply one module's dialect to another. List every override honoured, so the reader sees why
159
+ a registry rule was not raised.
160
+
161
+ ## Phase 4 — Produce the plan
162
+
163
+ No findings → report compliant and stop. Otherwise write
164
+ `<ModulePath>/ios-coding-standard-plan-<module>.md` (or the repo's scratch/docs dir if the module
165
+ tree must stay clean), containing, in order:
166
+
167
+ 1. **Coverage ledger** — total targets · audited · skipped with reasons. Not 100% accounted for
168
+ means the plan is not deliverable.
169
+ 2. **Summary** — counts by severity and source, resolved vocabulary, branch to create.
170
+ 3. **Sensitive-data inventory** (Phase 2d) — first, because it is the highest-risk content.
171
+ 4. **Screen × role matrix** `[STRUCT-02]` — screens as rows, manifest roles as columns, cells
172
+ `✓ / missing / misplaced`. The structural map a newcomer reads first.
173
+ 5. **Type placement table** `[STRUCT-05]` — type · location · consumer count · correct tier ·
174
+ move required. Paired with the nested-type list `[STRUCT-01]`: owner · nested type · kind ·
175
+ reference count · verdict.
176
+ 6. **Visibility report** `[VIS-01, VIS-02]` — over-exposed declarations per file, non-final
177
+ classes with no subclass.
178
+ 7. **Boundary report** `[MOD-*]` — import graph (allowed vs forbidden, with coupling depth) ·
179
+ manifest graph (declared vs legal for the role, plus dead edges) · **removability delta**
180
+ ("removing X touches N files: …") · inbound test (tests compile with no sibling present).
181
+ 8. **Testability seam report** `[TEST-*]` — types reaching for the environment, logic requiring a
182
+ view to execute, doubles whose signatures have drifted.
183
+ 9. **Change-cost probe** `[FLEX-*]` — pick two plausible upcoming changes (a new variant of an
184
+ existing screen; a new field on a shared entity) and state what each costs in files today.
185
+ 10. **Findings table** — rule ID · `file:line` · current state · proposed fix · severity · source ·
186
+ onboarding impact.
187
+ 11. **Per-file work** — grouped per file, blocking → important → suggestion; within a severity,
188
+ highest onboarding impact first.
189
+ 12. **Behaviour-preservation guard** — splitting files, adding MARKs, extracting extensions and
190
+ moving types must not change behaviour. Anything requiring a behaviour change (a missing
191
+ analytics event, a wrong result type, a timezone fix, a storage migration) goes in a
192
+ **separate group**, reviewed and tested on its own, never inside a mechanical split commit.
193
+ 13. **Tooling delegation & the ratchet** — the section that decides whether any of this survives.
194
+ - Classify each applied rule by its registry `enforcement`. For every `lint` / `format` /
195
+ `scan` rule, state the concrete mechanism and whether that toolchain exists in the repo
196
+ (`LintToolchain`). **When it does not exist, saying so is a finding**, ranked above most
197
+ individual violations it would have caught.
198
+ - Recommend **baseline-and-ratchet, never big-bang**: grandfather existing violations, surface
199
+ only new ones, drain the backlog behind a green build. Order the plan the same way — stop
200
+ the bleeding, then clean up.
201
+ - Custom lint rule identifiers **are** the registry IDs (`sec_01_no_plaintext_persistence`,
202
+ `mod_01_no_sibling_import`) so a violation points straight at the rule entry.
203
+ - **Linting is local-only by decision** — the skill's `references/lint-local.sh` with `references/swiftlint.draft.yml`, config
204
+ and baseline kept outside the repository; no committed config, no build phase, no CI job.
205
+ State this honestly in the plan rather than describing a gate that does not exist: a
206
+ pre-PR habit catches less than a wall, so the residual risk is real and belongs in the
207
+ report. Adopting it project-wide remains an open decision — record it as one so it stays
208
+ visible instead of quietly lapsing.
209
+ - Never imply the audit substitutes for the linter, or the linter for a gate. The audit finds
210
+ the debt once; the linter keeps a developer honest; only a gate stops it returning.
211
+ 14. **Numeric exit criteria** — target and today's measured value side by side: lint violations
212
+ per 1k lines (<1) · public-surface ratio, public declarations ÷ externally consumed symbols
213
+ (≈1) · forbidden cross-module imports (0) · removability delta · files over threshold and
214
+ files >120 lines with no sectioning (0) · screens missing a manifest role (0) · escape-hatch
215
+ count `[CONC-03]` (flat or falling) · exception-marker count (flat or falling) · dead code (0)
216
+ · screens with logic and no mirrored tests (0) · unclassified sensitive symbols (0).
217
+ 15. **Exception ledger** — every `// standard:exception(<ID>)` in the module: rule · reason ·
218
+ expiry · expired yes/no.
219
+ 16. **Open Questions** — Phase 2b dimensions with no majority, and unclassifiable symbols.
220
+ 17. **Suggested commit slicing** — one commit per file-group / concern.
221
+ 18. **Scope guard** — only this module is touched; definition of done is the module's own
222
+ verification path.
223
+
224
+ Then write a **separate one-page onboarding summary** beside the plan, aimed at a developer
225
+ joining next week rather than at whoever does the remediation: the ten always/never lines
226
+ specialised to this module, the screen file manifest, the placement ladder, the sensitive-data
227
+ classes in play, and the verification command. Nobody learns a codebase from a 40-finding audit —
228
+ this page is the actual deliverable, and the audit is how you earn the right to write it
229
+ accurately. Derive it from `references/STANDARD.md § 0`, do not re-invent it.
230
+
231
+ Show a concise version of the plan to the user too.
232
+
233
+ ## Phase 5 — Hand off
234
+
235
+ `AskUserQuestion` (single-select, `outputLanguage`):
236
+
237
+ | Option | What it does |
238
+ |---|---|
239
+ | `/multi-agent:dev-local` | No worktree; branches from the main development branch, fixes on the current checkout |
240
+ | `/multi-agent:dev` | Opens a worktree; Opus fixes on a fresh branch + PR |
241
+
242
+ Invoke the chosen command with the plan file as the task input, branching off the repo's main
243
+ development branch (e.g. `chore/<module>-coding-standard`). The dev pipeline applies the fixes,
244
+ verifies, and commits only when asked. This routine ends at the handoff.
245
+
246
+ ## Notes
247
+
248
+ - **Verification reality is per-module — establish it, do not assume it.** Some targets cannot be
249
+ compiled from the CLI (asset symbol generation, UIKit-dependent packages, a dependency failing
250
+ earlier in the graph); there, verification is Xcode and a manifest dump validates the manifest
251
+ only. Never trust a piped build's exit code — a pipe reports the tail's status. Record the
252
+ module's actual verification command in the plan.
253
+ - One module per run. Several modules means several runs and several plans, never one
254
+ cross-module churn commit.
255
+ - If the audit wants a rule the registry lacks, add it to the skill's `references/rules.yml` as `status: proposed` with a
256
+ rationale and surface it in Open Questions. Do not enforce an unregistered rule.
257
+ - No AI attribution anywhere (code, commits, PRs). Author = git identity.
@@ -59,8 +59,8 @@ Run every step automatically:
59
59
  ```
60
60
  Step 1: PLATFORM Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
61
61
  Step 1.5: DETECT Compare timestamps, find stale targets
62
- Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 43 sub-command skills)
63
- Step 2b: CODEX Claude Code -> Codex CLI (1 router skill + 43 specs as refs + 8 agent TOML)
62
+ Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 44 sub-command skills)
63
+ Step 2b: CODEX Claude Code -> Codex CLI (1 router skill + 44 specs as refs + 8 agent TOML)
64
64
  Step 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
65
65
  Step 3c: PLUGINS pipeline shared/external -> multi-agent-plugins marketplace (rebuild knowledge/,
66
66
  bump changed plugins' patch version, commit + push the plugins repo)
@@ -486,12 +486,12 @@ same 43 specs as reference files rather than as peer skills, via Step 2b - see
486
486
  |-------------|-------------|
487
487
  | `~/.claude/commands/multi-agent/{cmd}/SKILL.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |
488
488
 
489
- **43 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
489
+ **44 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
490
490
 
491
491
  ```
492
492
  analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
493
493
  dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, forget, garbage-collect,
494
- help, issue, jira, kill, language, local,
494
+ help, ios-coding-standard, issue, jira, kill, language, local,
495
495
  local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
496
496
  routines, save, scan, search, setup, stack, status, sync, test, testflight-validation, uninstall, update
497
497
  ```
@@ -6,12 +6,12 @@
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Command Inventory (43 commands)
9
+ ## 1. Command Inventory (44 commands)
10
10
 
11
11
  ```
12
12
  analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
13
13
  dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, forget, garbage-collect,
14
- help, issue, jira, kill, language, local,
14
+ help, ios-coding-standard, issue, jira, kill, language, local,
15
15
  local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
16
16
  routines, save, scan, search, setup, stack, status, sync, test, testflight-validation, uninstall, update
17
17
  ```
@@ -24,7 +24,7 @@ Categories:
24
24
  - **Fast modes** (Init -> Dev(Opus) -> Commit -> Report): `dev`, `dev-autopilot`, `dev-local`, `dev-local-autopilot`
25
25
  - **Tail modes** (run the pipeline tail over already-done local work): `finish`
26
26
  - **Ops commands** (one-shot, no worktree): `status`, `log`, `kill`, `purge`, `uninstall`, `resume`, `review`, `review-jira`, `review-issue`, `analysis`, `analysis-resolve`, `build-optimize`, `channels`, `scan`, `search`, `diff-explain`, `garbage-collect`, `prune-logs`
27
- - **Local audits** (worktree only to build; no commit, push, PR or channels): `design-check`, `testflight-validation`. `testflight-validation` additionally never invokes `altool --upload-app` - a validation run must not be able to ship a build by accident.
27
+ - **Local audits** (worktree only to build; no commit, push, PR or channels): `design-check`, `testflight-validation`, `ios-coding-standard`. `testflight-validation` additionally never invokes `altool --upload-app` - a validation run must not be able to ship a build by accident.
28
28
  - **Meta-ops**: `setup`, `sync`, `update`, `help`, `refactor`, `test`, `stack`, `manual-test`, `language`
29
29
  - **Routines** (user-defined routine registry; the routines they create are local-only and never synced): `save`, `routines`, `forget`
30
30
 
@@ -0,0 +1,258 @@
1
+ ---
2
+ name: multi-agent-ios-coding-standard
3
+ language: en
4
+ description: "Audit an iOS module against the shared coding-standard registry (95 stable-ID rules), produce a remediation plan, then hand off to dev/dev-local. Use for a standards pass on a module, or when a review needs rule IDs rather than opinions."
5
+ user-invocable: true
6
+ argument-hint: "[module name or path]"
7
+ ---
8
+
9
+ # multi-agent ios-coding-standard — Module audit → plan → dev handoff
10
+
11
+ **Input**: $ARGUMENTS — optionally a module name or path. When absent, Phase 1 discovers and asks.
12
+
13
+ This routine is the **procedure**. The rules live in the `ios-coding-standard` skill, whose registry is
14
+ `references/rules.yml` and whose teaching doc is `references/STANDARD.md`. Load that
15
+ skill first; it is installed on every host and carried by the iOS stack plugin, so
16
+ there is one registry rather than a copy per CLI. Never restate a rule here — cite its ID. A rule that is not in the
17
+ registry is not a rule; if the audit needs one, propose it as `status: proposed` and say so.
18
+
19
+ Read-only + planning. This routine never edits source — the dev pipeline does.
20
+
21
+ **Goal.** A developer new to the repo can open any file in the module and understand it without a
22
+ guided tour, and nothing sensitive leaks on the way. Rank every finding by "does fixing this
23
+ shorten the time to first productive PR, or close a real risk?"
24
+
25
+ Render assistant-facing prose in `outputLanguage`. External payloads (branch names, plan file,
26
+ dev task) stay English.
27
+
28
+ ---
29
+
30
+ ## Phase 1 — Discover and pick the module
31
+
32
+ 1. Repo root via `git rev-parse --show-toplevel`.
33
+ 2. **Discover module roots** — any directory with a `Package.swift`, a `Sources/` subtree or an
34
+ `.xcodeproj`. Sweep the container dirs that exist (`Domains/`, `Packages/`, `Modules/`,
35
+ `Features/`, `Core/`, `Common/`, top-level module dirs) one level deep, then two if empty.
36
+ 3. Record per module: name · path · Swift files · lines · governance docs present · SPM targets.
37
+ Keep this **module registry** for the whole run — `MOD-*` greps every import against it.
38
+ Classify each module's role, because the role decides which dependency edges are legal:
39
+ **feature** · **core/common** · **seam** (cross-module contracts/bridges/navigation) ·
40
+ **composition root** (legitimately knows every module).
41
+ 4. If `$ARGUMENTS` resolves to a module, skip the prompt. Otherwise `AskUserQuestion`
42
+ (single-select, `outputLanguage`), documented modules first, each option showing file/line
43
+ counts and `docs: yes/no`. If the list exceeds the question limit, group the smallest into an
44
+ "other" option and ask again — never truncate silently.
45
+ 5. Scope = the whole module's sources across all its targets, unless the user narrows it.
46
+
47
+ ## Phase 2 — Resolve what applies to THIS module
48
+
49
+ ### 2a. The registry is the standard — in-module prose docs are NOT consulted
50
+
51
+ The skill's `references/rules.yml` (+ a project `modules/<Module>.yml` overlay) is the single source of truth. **Do not read, cite, or
52
+ derive rules from a module's own `*-CLAUDE.md`, `docs/` set or any other in-repo prose.** They are
53
+ being retired precisely so there is one place a rule can live; consulting them re-creates the
54
+ split this command exists to remove. If such a file exists and contradicts the registry, that is a
55
+ finding against the file, not against the code.
56
+
57
+ The only in-repo inputs are **code and manifests**: sources, tests, `Package.swift`, `Info.plist`,
58
+ the specs the generator reads, and the module registry from Phase 1.
59
+
60
+ ### 2b. Per-module overlay, then inference
61
+
62
+ 1. **`modules/<Module>.yml`** beside the project's own config carries what code cannot state: the vocabulary
63
+ bindings, the module's role, prohibitions that currently have **zero** instances, deliberate
64
+ carve-outs, name locks, and the real verification command. Registry-backed, so its findings may
65
+ be `blocking`.
66
+ *Why prohibitions need to be written down:* inference reads dominant patterns, and a rule
67
+ obeyed everywhere has no counter-example to infer from. A ban at 100% compliance is invisible
68
+ to a scan — it survives only if the overlay states it.
69
+ 2. **Repo-level rules** (root `CLAUDE.md`, contributing docs) apply to every module.
70
+ 3. **The module's own dominant pattern is its de-facto convention.** Count variants per dimension;
71
+ the minority instances are the finding. Internal consistency beats conformity to a sibling.
72
+ 4. No majority (even split, single instance) → not a finding. Record an Open Question.
73
+
74
+ Findings from step 3 are tagged `inferred` and capped at **suggestion**. Only registry- or
75
+ overlay-backed rules may be `blocking`.
76
+
77
+ **When the overlay is missing**, generate a draft from code evidence, print it for confirmation,
78
+ and mark every slot it could not derive — a frozen UI target, a name lock, a carve-out and an
79
+ operational constraint all look like ordinary code from the outside. Do not guess them; list them
80
+ as Open Questions.
81
+
82
+ ### 2c. Vocabulary
83
+
84
+ Bind each slot to a real symbol by grepping the module, then the reference module. **An unbound
85
+ slot disables its rules** — never invent a name, never import a sibling's.
86
+
87
+ `HandlerName` · `EventParam` · `CoordinatorType` · `ResultType` / `ErrorType` / `ErrorFactory` ·
88
+ `DIResolver` / `DIConfigurator` · `RequestModelSuffix` / `ResponseModelSuffix` / `TransportSuffix` ·
89
+ `MapperShape` · `SafeEnumProtocol` / `UnknownCase` · `CopySurface` · `AnalyticsSurface` ·
90
+ `A11yIdentifierSource` · `GeneratedServiceRoot` · `SharedComponentsDir` / `SharedEntitiesDir` ·
91
+ `MicroComponentLibrary` · `FrozenUITarget` · `DesignTokenNamespaces` · `CalendarDayHelper` ·
92
+ `LoggerAPI` · `FileHeaderShape` · `ScreenRoot` · `ScreenRoleSuffixes` · `AsyncStyle` ·
93
+ `CompositionRoot` · `SeamLayer` · `ModuleEntrySurface` · `ConcurrencyPosture` · `LintToolchain` ·
94
+ `CredentialStore` (the module's Keychain wrapper, if any).
95
+
96
+ Print the resolved table before scanning so a wrong binding is caught early.
97
+
98
+ ### 2d. Sensitive-data inventory — required before any SEC rule runs
99
+
100
+ The `SEC-*` rules are written against the **data classes** in `references/rules.yml →
101
+ sensitive_data_classes`, never against one module's field names. Resolve the module's concrete
102
+ instances of each class: grep entities, request/response models, storage calls and analytics
103
+ events for the values the module actually handles, and map each to its class.
104
+
105
+ Produce the inventory as a table — **class · concrete symbols · where it enters · required
106
+ lifetime (transient / survives-restart / survives-reinstall) · where it is actually stored ·
107
+ where it is logged · where it leaves (network, analytics, pasteboard, another module)**.
108
+
109
+ The **lifetime column is the one that decides SEC-01**, and it is filled by walking
110
+ `references/rules.yml → persistence_decision`, not by looking at what the code currently does. Compare it
111
+ against the storage column and report both mismatches:
112
+ - required transient but persisted anywhere (including the Keychain) → **over-persistence**;
113
+ - required to survive a restart but stored outside the Keychain → **under-protection**.
114
+
115
+ Then generate the SEC lint patterns *from this inventory*, so the checks match this module's real
116
+ vocabulary. A class with no instance here yields no finding; a symbol you cannot classify, or one
117
+ whose required lifetime nobody can state, is an Open Question, not a silent pass.
118
+
119
+ This table is a deliverable in its own right, independent of whether any violation is found.
120
+
121
+ ## Phase 3 — Scan
122
+
123
+ Apply the rules in `references/rules.yml` that survived Phase 2 binding, plus the module's own `validation`
124
+ gate. Tag every finding: **rule ID · severity · source (`registry` / `doc` / `inferred`) ·
125
+ onboarding impact (high/medium/low)**.
126
+
127
+ ### Coverage gate — on disk, not in memory
128
+
129
+ Before scanning, write `.<module>-audit-coverage.tsv` to the scratch dir: one row per target
130
+ (every screen under `ScreenRoot`, every shared component, every entity, every file in every
131
+ target) with columns `path · status · rules-applied`. Mark rows as you go.
132
+
133
+ - Each target is **audited** or **explicitly skipped with a reason** (generated, fixture,
134
+ vendored). No third state.
135
+ - **The run is incomplete while any row is unmarked** — and the file, not a claim, is the proof.
136
+ Chunk the module if it is too large for one pass; the checklist survives a context reset.
137
+ - No sampling. A partial audit reported as complete certifies the unscanned screens as clean.
138
+
139
+ ### Grep hygiene
140
+
141
+ Registry `mechanism` patterns are line-based and match doc comments. Filter
142
+ `^[[:space:]]*//` — BSD grep does not understand `\s`, so a `\s*//` filter silently lets `/// …`
143
+ through — then open each surviving hit and confirm it is real code. Report pre-filter and
144
+ post-verification counts separately; a table padded with comment matches destroys trust.
145
+
146
+ ### Judgement rules need evidence, not assertion
147
+
148
+ For any rule marked `enforcement: judgement`, a finding must carry the measurement its `check`
149
+ names — a reference count, a call-site count, a consumer count, a file list. **No count, no
150
+ finding.** This is what keeps a subjective rule from becoming an opinion.
151
+
152
+ Every judgement rule has a worked ✗/✓ pair in `EXAMPLES.md`, keyed by ID. Cite it in the finding's
153
+ proposed fix rather than re-describing the shape — the developer applying the fix should be
154
+ reading the same picture the audit used.
155
+
156
+ ### Module-specific overrides
157
+
158
+ The module's own docs win over the registry, including where they contradict a sibling module.
159
+ Never apply one module's dialect to another. List every override honoured, so the reader sees why
160
+ a registry rule was not raised.
161
+
162
+ ## Phase 4 — Produce the plan
163
+
164
+ No findings → report compliant and stop. Otherwise write
165
+ `<ModulePath>/ios-coding-standard-plan-<module>.md` (or the repo's scratch/docs dir if the module
166
+ tree must stay clean), containing, in order:
167
+
168
+ 1. **Coverage ledger** — total targets · audited · skipped with reasons. Not 100% accounted for
169
+ means the plan is not deliverable.
170
+ 2. **Summary** — counts by severity and source, resolved vocabulary, branch to create.
171
+ 3. **Sensitive-data inventory** (Phase 2d) — first, because it is the highest-risk content.
172
+ 4. **Screen × role matrix** `[STRUCT-02]` — screens as rows, manifest roles as columns, cells
173
+ `✓ / missing / misplaced`. The structural map a newcomer reads first.
174
+ 5. **Type placement table** `[STRUCT-05]` — type · location · consumer count · correct tier ·
175
+ move required. Paired with the nested-type list `[STRUCT-01]`: owner · nested type · kind ·
176
+ reference count · verdict.
177
+ 6. **Visibility report** `[VIS-01, VIS-02]` — over-exposed declarations per file, non-final
178
+ classes with no subclass.
179
+ 7. **Boundary report** `[MOD-*]` — import graph (allowed vs forbidden, with coupling depth) ·
180
+ manifest graph (declared vs legal for the role, plus dead edges) · **removability delta**
181
+ ("removing X touches N files: …") · inbound test (tests compile with no sibling present).
182
+ 8. **Testability seam report** `[TEST-*]` — types reaching for the environment, logic requiring a
183
+ view to execute, doubles whose signatures have drifted.
184
+ 9. **Change-cost probe** `[FLEX-*]` — pick two plausible upcoming changes (a new variant of an
185
+ existing screen; a new field on a shared entity) and state what each costs in files today.
186
+ 10. **Findings table** — rule ID · `file:line` · current state · proposed fix · severity · source ·
187
+ onboarding impact.
188
+ 11. **Per-file work** — grouped per file, blocking → important → suggestion; within a severity,
189
+ highest onboarding impact first.
190
+ 12. **Behaviour-preservation guard** — splitting files, adding MARKs, extracting extensions and
191
+ moving types must not change behaviour. Anything requiring a behaviour change (a missing
192
+ analytics event, a wrong result type, a timezone fix, a storage migration) goes in a
193
+ **separate group**, reviewed and tested on its own, never inside a mechanical split commit.
194
+ 13. **Tooling delegation & the ratchet** — the section that decides whether any of this survives.
195
+ - Classify each applied rule by its registry `enforcement`. For every `lint` / `format` /
196
+ `scan` rule, state the concrete mechanism and whether that toolchain exists in the repo
197
+ (`LintToolchain`). **When it does not exist, saying so is a finding**, ranked above most
198
+ individual violations it would have caught.
199
+ - Recommend **baseline-and-ratchet, never big-bang**: grandfather existing violations, surface
200
+ only new ones, drain the backlog behind a green build. Order the plan the same way — stop
201
+ the bleeding, then clean up.
202
+ - Custom lint rule identifiers **are** the registry IDs (`sec_01_no_plaintext_persistence`,
203
+ `mod_01_no_sibling_import`) so a violation points straight at the rule entry.
204
+ - **Linting is local-only by decision** — the skill's `references/lint-local.sh` with `references/swiftlint.draft.yml`, config
205
+ and baseline kept outside the repository; no committed config, no build phase, no CI job.
206
+ State this honestly in the plan rather than describing a gate that does not exist: a
207
+ pre-PR habit catches less than a wall, so the residual risk is real and belongs in the
208
+ report. Adopting it project-wide remains an open decision — record it as one so it stays
209
+ visible instead of quietly lapsing.
210
+ - Never imply the audit substitutes for the linter, or the linter for a gate. The audit finds
211
+ the debt once; the linter keeps a developer honest; only a gate stops it returning.
212
+ 14. **Numeric exit criteria** — target and today's measured value side by side: lint violations
213
+ per 1k lines (<1) · public-surface ratio, public declarations ÷ externally consumed symbols
214
+ (≈1) · forbidden cross-module imports (0) · removability delta · files over threshold and
215
+ files >120 lines with no sectioning (0) · screens missing a manifest role (0) · escape-hatch
216
+ count `[CONC-03]` (flat or falling) · exception-marker count (flat or falling) · dead code (0)
217
+ · screens with logic and no mirrored tests (0) · unclassified sensitive symbols (0).
218
+ 15. **Exception ledger** — every `// standard:exception(<ID>)` in the module: rule · reason ·
219
+ expiry · expired yes/no.
220
+ 16. **Open Questions** — Phase 2b dimensions with no majority, and unclassifiable symbols.
221
+ 17. **Suggested commit slicing** — one commit per file-group / concern.
222
+ 18. **Scope guard** — only this module is touched; definition of done is the module's own
223
+ verification path.
224
+
225
+ Then write a **separate one-page onboarding summary** beside the plan, aimed at a developer
226
+ joining next week rather than at whoever does the remediation: the ten always/never lines
227
+ specialised to this module, the screen file manifest, the placement ladder, the sensitive-data
228
+ classes in play, and the verification command. Nobody learns a codebase from a 40-finding audit —
229
+ this page is the actual deliverable, and the audit is how you earn the right to write it
230
+ accurately. Derive it from `references/STANDARD.md § 0`, do not re-invent it.
231
+
232
+ Show a concise version of the plan to the user too.
233
+
234
+ ## Phase 5 — Hand off
235
+
236
+ `AskUserQuestion` (single-select, `outputLanguage`):
237
+
238
+ | Option | What it does |
239
+ |---|---|
240
+ | `/multi-agent:dev-local` | No worktree; branches from the main development branch, fixes on the current checkout |
241
+ | `/multi-agent:dev` | Opens a worktree; Opus fixes on a fresh branch + PR |
242
+
243
+ Invoke the chosen command with the plan file as the task input, branching off the repo's main
244
+ development branch (e.g. `chore/<module>-coding-standard`). The dev pipeline applies the fixes,
245
+ verifies, and commits only when asked. This routine ends at the handoff.
246
+
247
+ ## Notes
248
+
249
+ - **Verification reality is per-module — establish it, do not assume it.** Some targets cannot be
250
+ compiled from the CLI (asset symbol generation, UIKit-dependent packages, a dependency failing
251
+ earlier in the graph); there, verification is Xcode and a manifest dump validates the manifest
252
+ only. Never trust a piped build's exit code — a pipe reports the tail's status. Record the
253
+ module's actual verification command in the plan.
254
+ - One module per run. Several modules means several runs and several plans, never one
255
+ cross-module churn commit.
256
+ - If the audit wants a rule the registry lacks, add it to the skill's `references/rules.yml` as `status: proposed` with a
257
+ rationale and surface it in Open Questions. Do not enforce an unregistered rule.
258
+ - No AI attribution anywhere (code, commits, PRs). Author = git identity.