@mmerterden/multi-agent-pipeline 13.5.0 → 14.0.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 +243 -0
- package/README.md +3 -3
- package/docs/features.md +1 -1
- package/install/_common.mjs +73 -0
- package/install/_mcp-register.mjs +70 -31
- package/install/_plugin-skills.mjs +73 -14
- package/install/claude.mjs +28 -4
- package/install/codex.mjs +33 -2
- package/install/copilot.mjs +145 -9
- package/install/index.mjs +10 -6
- package/install/templates/copilot-instructions.md +1 -1
- package/package.json +1 -1
- package/pipeline/agents/code-reviewer.md +58 -1
- package/pipeline/commands/multi-agent/SKILL.md +7 -5
- package/pipeline/commands/multi-agent/analysis/SKILL.md +7 -7
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +7 -7
- package/pipeline/commands/multi-agent/channels/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/dev/SKILL.md +23 -18
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +19 -13
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +14 -12
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +17 -12
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/resume/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/{finish → ship}/SKILL.md +12 -12
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +5 -2
- package/pipeline/commands/sim-test.md +2 -2
- package/pipeline/lib/credential-store-resolver.sh +16 -0
- package/pipeline/lib/credential-store.sh +47 -4
- package/pipeline/lib/fetch-figma-annotations.sh +26 -28
- package/pipeline/lib/figma-screenshot.sh +28 -39
- package/pipeline/lib/figma-token.sh +63 -0
- package/pipeline/multi-agent-refs/analysis-template.md +1 -1
- package/pipeline/multi-agent-refs/android-guide.md +1 -1
- package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +2 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
- package/pipeline/multi-agent-refs/features/dev-critic.md +2 -2
- package/pipeline/multi-agent-refs/features/model-fallback.md +35 -2
- package/pipeline/multi-agent-refs/features/plan-todos.md +1 -1
- package/pipeline/multi-agent-refs/features/repo-map.md +1 -1
- package/pipeline/multi-agent-refs/features/review-multi-repo.md +3 -3
- package/pipeline/multi-agent-refs/features/shadow-git.md +1 -1
- package/pipeline/multi-agent-refs/features/skill-conformance.md +116 -0
- package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
- package/pipeline/multi-agent-refs/generate-issue.md +1 -1
- package/pipeline/multi-agent-refs/multi-repo-integration-build.md +1 -1
- package/pipeline/multi-agent-refs/phases/log-format.md +4 -4
- package/pipeline/multi-agent-refs/phases/modes.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +13 -11
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +17 -15
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +28 -13
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +90 -58
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-6-commit.md +8 -8
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +8 -8
- package/pipeline/multi-agent-refs/phases.md +13 -13
- package/pipeline/multi-agent-refs/progress-contract.md +2 -2
- package/pipeline/multi-agent-refs/rules.md +7 -5
- package/pipeline/multi-agent-refs/swiftui-guide.md +1 -1
- package/pipeline/multi-agent-refs/tracker-contract.md +16 -15
- package/pipeline/preferences-template.json +7 -1
- package/pipeline/rules/figma-pipeline.md +2 -2
- package/pipeline/schemas/agent-state.schema.json +333 -79
- package/pipeline/schemas/criteria-manifest.schema.json +228 -0
- package/pipeline/schemas/migrations/prefs-2.4.0-to-2.5.0.mjs +64 -0
- package/pipeline/schemas/prefs.schema.json +118 -262
- package/pipeline/schemas/reviewer-output.schema.json +48 -3
- package/pipeline/schemas/token-budget.json +34 -10
- package/pipeline/schemas/triage-output.schema.json +112 -27
- package/pipeline/scripts/cost-table.json +7 -4
- package/pipeline/scripts/gc-worktrees.sh +1 -1
- package/pipeline/scripts/gen-mode-dispatch.mjs +6 -6
- package/pipeline/scripts/match-skills.mjs +37 -4
- package/pipeline/scripts/migrate-prefs.mjs +88 -17
- package/pipeline/scripts/phase-tracker.sh +14 -3
- package/pipeline/scripts/pre-commit-check.sh +49 -2
- package/pipeline/scripts/skill-conformance.mjs +960 -0
- package/pipeline/scripts/smoke-schema-validation.sh +17 -4
- package/pipeline/scripts/uninstall.mjs +35 -9
- package/pipeline/scripts/validate-reviewer.mjs +108 -1
- package/pipeline/skills/.skill-manifest.json +1 -1
- package/pipeline/skills/.skills-index.json +36 -9
- package/pipeline/skills/shared/README.md +15 -12
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/rules.yml +167 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +184 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -10
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +4 -4
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +6 -5
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +7 -6
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +4 -3
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +4 -4
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
- package/pipeline/skills/shared/core/{multi-agent-finish → multi-agent-ship}/SKILL.md +8 -8
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +44 -5
- package/pipeline/skills/shared/external/ios-coding-standard/modules/_TEMPLATE.yml +82 -0
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +169 -10
- package/pipeline/skills/shared/external/ios-coding-standard/references/lint-local.sh +13 -1
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +335 -16
- package/pipeline/skills/skills-index.md +11 -8
|
@@ -1,9 +1,33 @@
|
|
|
1
|
-
version: 1.
|
|
2
|
-
updated: 2026-07-
|
|
1
|
+
version: 1.1.0
|
|
2
|
+
updated: 2026-07-29
|
|
3
3
|
owner: iOS platform
|
|
4
4
|
description: >
|
|
5
|
-
The rule registry. STANDARD.md teaches these to a newcomer with examples;
|
|
6
|
-
against them. IDs are stable and never
|
|
5
|
+
The rule registry. STANDARD.md teaches these to a newcomer with examples; EXAMPLES.md carries a
|
|
6
|
+
worked ✗/✓ pair per judgement rule; SKILL.md audits against them. IDs are stable and never
|
|
7
|
+
renumbered - a rule is retired by status, not deletion.
|
|
8
|
+
|
|
9
|
+
# What these rules may be applied to. A consumer resolves this BEFORE selecting rules,
|
|
10
|
+
# and records how many it dropped and why. Without it every rule below would be applied
|
|
11
|
+
# to every changed file, which on an Objective-C or UIKit diff manufactures findings and
|
|
12
|
+
# buries the real ones - worse than declaring no coverage at all.
|
|
13
|
+
#
|
|
14
|
+
# Per-rule `scope:` narrows this further and never widens it. A rule with no `scope:`
|
|
15
|
+
# inherits the block below.
|
|
16
|
+
scope:
|
|
17
|
+
languages: [swift]
|
|
18
|
+
paths:
|
|
19
|
+
- "**/*.swift"
|
|
20
|
+
excludePaths:
|
|
21
|
+
- "**/*.generated.swift"
|
|
22
|
+
- "**/Generated/**"
|
|
23
|
+
- "**/*.pb.swift"
|
|
24
|
+
# Named so a consumer can report the gap rather than silently covering nothing:
|
|
25
|
+
# these languages appear in iOS repos and this registry does NOT speak for them.
|
|
26
|
+
notCovered:
|
|
27
|
+
objective-c: "*.m / *.mm / *.h - no ObjC rules exist here; report as a coverage gap, never apply Swift rules to them"
|
|
28
|
+
uikit: "UIKit view controllers are Swift, so file-level rules apply, but the UI-* family assumes SwiftUI view construction and is scoped out below"
|
|
29
|
+
c-cpp: "*.c / *.cc / *.hpp - out of scope"
|
|
30
|
+
|
|
7
31
|
severity_levels: [blocking, important, suggestion]
|
|
8
32
|
enforcement_kinds:
|
|
9
33
|
format: the formatter owns it; not reviewed by humans
|
|
@@ -12,6 +36,44 @@ enforcement_kinds:
|
|
|
12
36
|
judgement: requires a human or an audit run
|
|
13
37
|
exception_marker: "// standard:exception(<RULE-ID>) <reason> <expiry:YYYY-MM-DD>"
|
|
14
38
|
|
|
39
|
+
module_overlay_slots:
|
|
40
|
+
description: >
|
|
41
|
+
Some rules govern a CHOICE rather than a defect: two shapes are each internally coherent, the
|
|
42
|
+
cost is only in mixing them, and picking one is the module's call. Writing one of them into the
|
|
43
|
+
shared registry turns every module that chose the other into a hundred findings - which is a
|
|
44
|
+
migration proposal wearing a standards pass. Those rules bind to a slot here instead. The
|
|
45
|
+
module's `modules/<Module>.yml` overlay binds it; an UNBOUND slot disables its rules, and the
|
|
46
|
+
audit says so in the plan rather than defaulting to one dialect silently.
|
|
47
|
+
A slot is only legitimate when both values are genuinely defensible. `UI-04` is the
|
|
48
|
+
counter-example: a module with no copy surface has not chosen a different dialect, it is
|
|
49
|
+
missing the surface, so that rule is not slotted.
|
|
50
|
+
slots:
|
|
51
|
+
- id: ServiceNamingScheme
|
|
52
|
+
governs: [SVC-07]
|
|
53
|
+
values:
|
|
54
|
+
send-path: >
|
|
55
|
+
a method wrapping one service call is named after the endpoint path, prefixed to say a
|
|
56
|
+
request leaves the device. One spelling on both sides of the wire; a grep from the
|
|
57
|
+
endpoint reaches every layer.
|
|
58
|
+
domain-verb: >
|
|
59
|
+
the method is named after what the domain asks for. Reads more naturally at the call site
|
|
60
|
+
and survives an endpoint being renamed, at the cost of the endpoint-to-code grep.
|
|
61
|
+
note: >
|
|
62
|
+
Module-wide, never per screen. Changing the bound value is a rename migration with its own
|
|
63
|
+
ticket, not a review comment.
|
|
64
|
+
- id: NavExitShape
|
|
65
|
+
governs: [NAME-01]
|
|
66
|
+
values:
|
|
67
|
+
coordinator-event: a typed event enum per screen plus a handler alias the coordinator applies.
|
|
68
|
+
output-closure: an output enum per screen delivered through a closure the factory injects.
|
|
69
|
+
note: Both enumerate every exit in one place, which is the property NAME-01 actually protects.
|
|
70
|
+
- id: ComponentsDir
|
|
71
|
+
governs: [READ-04b]
|
|
72
|
+
values:
|
|
73
|
+
Presentation/Components: the conventional spelling.
|
|
74
|
+
Presentation/Subviews: an equally clear alternative in use.
|
|
75
|
+
note: The name is free; using two of them in one module is not.
|
|
76
|
+
|
|
15
77
|
persistence_decision:
|
|
16
78
|
description: >
|
|
17
79
|
Answer this BEFORE reaching for storage. Keychain is the answer to "where does a persisted
|
|
@@ -171,8 +233,11 @@ rules:
|
|
|
171
233
|
check: >
|
|
172
234
|
A `private var x: some View` inside a scene is a component in disguise. If it renders a
|
|
173
235
|
THING - a switcher, a chip row, a banner, a bar, a legend, a card - extract it to its own
|
|
174
|
-
file under the screen's
|
|
236
|
+
file under the screen's component directory (the overlay's `ComponentsDir`, conventionally
|
|
237
|
+
`Presentation/Components/`), as a `struct` that takes DATA and
|
|
175
238
|
CALLBACKS, never the view model, and give it a `#Preview`.
|
|
239
|
+
The directory's NAME is a dialect choice and not a finding on its own; using two names for
|
|
240
|
+
it within one module is.
|
|
176
241
|
Taking data instead of the view model is what makes the preview possible at all: a
|
|
177
242
|
component holding a view model needs the DI container a canvas preview never configures,
|
|
178
243
|
and the workaround (a preview-only fixture type shipped in production sources) is itself
|
|
@@ -201,6 +266,12 @@ rules:
|
|
|
201
266
|
Not this rule: building a design-system `Configuration` value. Moving those to the view
|
|
202
267
|
model drags UI types across the boundary; they belong inside the component that renders
|
|
203
268
|
them (READ-04b), which is where they disappear once the component takes data.
|
|
269
|
+
The view model is where such a rule GOES BY DEFAULT, and for a single-screen rule that is
|
|
270
|
+
the end of it. When the same question turns out to be several screens', SVC-08's ladder
|
|
271
|
+
decides between the entity and a named rule namespace (RULE-01); moving it out of the view
|
|
272
|
+
layer is this rule, choosing its final home is that one.
|
|
273
|
+
Also not this rule: reading the load state's own shape (NAME-06). `state.isLoading` decides
|
|
274
|
+
nothing - it reports what the enum already says.
|
|
204
275
|
|
|
205
276
|
- id: READ-04d
|
|
206
277
|
title: A pure transform is a shared helper, not a method on the view
|
|
@@ -321,11 +392,24 @@ rules:
|
|
|
321
392
|
|
|
322
393
|
# ── NAME ──────────────────────────────────────────────────────────────────
|
|
323
394
|
- id: NAME-01
|
|
324
|
-
title: One name per role -
|
|
395
|
+
title: One name per role - a screen's navigation exit has one spelling module-wide
|
|
325
396
|
severity: blocking
|
|
326
397
|
enforcement: lint
|
|
327
|
-
mechanism:
|
|
398
|
+
mechanism: >
|
|
399
|
+
custom regex scoped to Scene/ViewModel, generated from the module's bound `NavExitShape`:
|
|
400
|
+
any navigation-exit property or parameter whose name does not match the bound spelling.
|
|
328
401
|
rationale: readability
|
|
402
|
+
applies_when: >
|
|
403
|
+
the module's overlay binds `NavExitShape`. Unbound, this rule is DISABLED - a regex for one
|
|
404
|
+
spelling would report every module that chose the other one, which is a dialect difference,
|
|
405
|
+
not a defect.
|
|
406
|
+
check: >
|
|
407
|
+
What this rule protects is that a reader finds a screen's exits under ONE name across the
|
|
408
|
+
module, not that a particular name wins. Both shapes in use are legitimate: a typed
|
|
409
|
+
`CoordinatorEvent` enum with a handler alias, or an `Output` enum with a closure. Each
|
|
410
|
+
enumerates every navigation exit in one place, which is the property that matters.
|
|
411
|
+
The finding is a module that uses both, or a screen whose exits are spread across an enum
|
|
412
|
+
and loose ad-hoc callbacks. Report the spelling per screen and the count of each.
|
|
329
413
|
|
|
330
414
|
- id: NAME-02
|
|
331
415
|
title: Our models use RequestModel/ResponseModel; transport suffixes stop at the data layer
|
|
@@ -349,6 +433,16 @@ rules:
|
|
|
349
433
|
check: >
|
|
350
434
|
An unrecognised server value must land on a known-unknown case rather than failing or
|
|
351
435
|
silently carrying an arbitrary string into the UI.
|
|
436
|
+
**When the generator declares the same value set several times** - one inline enum per
|
|
437
|
+
response payload, because that is what the schema says - do not pick one of them as the
|
|
438
|
+
domain type and do not add a `switch` per mapper. Declare the domain enum once and bridge
|
|
439
|
+
through the shared raw code, in an extension on the domain type
|
|
440
|
+
(`extension X { init?(wireCode: String) }`) that lives beside it. Every mapper then maps
|
|
441
|
+
through one initialiser, the unknown value lands in one place, and adding a case is one edit
|
|
442
|
+
rather than one per payload. The bridge is a lowering, so it stays a mapper concern and does
|
|
443
|
+
not put the generated types in front of the view model (SVC-04).
|
|
444
|
+
Report per value set: how many generated declarations exist, how many hand-written mappings
|
|
445
|
+
of it the module has, and whether the unknown value is handled the same way in each.
|
|
352
446
|
|
|
353
447
|
- id: NAME-05
|
|
354
448
|
title: Screen behaviour is driven by an enum state, not by a spread of booleans
|
|
@@ -372,6 +466,36 @@ rules:
|
|
|
372
466
|
keeps a single source of truth, whereas a stored copy drifts from the inputs it mirrors.
|
|
373
467
|
Two independent booleans that never interact are fine; do not enum-ify for its own sake.
|
|
374
468
|
|
|
469
|
+
- id: NAME-06
|
|
470
|
+
title: The screen's load state is one enum with its payload attached, and the absent case is explained
|
|
471
|
+
severity: important
|
|
472
|
+
enforcement: judgement
|
|
473
|
+
rationale: readability
|
|
474
|
+
check: >
|
|
475
|
+
NAME-05 says a screen's behaviour is an enum rather than a spread of booleans. This rule is
|
|
476
|
+
about the LOAD state specifically - the one every screen with a service call has - and about
|
|
477
|
+
the shape, because the shape is what stops the booleans growing back.
|
|
478
|
+
- **The payload hangs off the case, not beside it.** `case loaded(Content)` where `Content`
|
|
479
|
+
is its own struct. The alternative - an `isLoading` flag next to an optional `data` next to
|
|
480
|
+
an optional `errorMessage` - permits `isLoading == true` with data present and an error
|
|
481
|
+
set, a combination nothing in the type rejects and every reader has to reason about.
|
|
482
|
+
- **The enum answers questions; the view does not destructure it.** Accessors on the enum
|
|
483
|
+
(`var isLoading`, `var <entity>`) keep `if case let` out of the view body. These are not
|
|
484
|
+
business rules (READ-04c) - they are reads of the state's own shape.
|
|
485
|
+
- **A case deliberately NOT modelled is stated in the file, with its reason.** "There is no
|
|
486
|
+
`empty` case: a successful response always represents a populated record; an empty payload
|
|
487
|
+
arrives as a not-found error." Without that line the next reader cannot tell a considered
|
|
488
|
+
omission from an oversight, and adds the case defensively.
|
|
489
|
+
- **A secondary source that may fail is an optional slot inside the payload, not a second
|
|
490
|
+
state.** When a screen's primary data loads but a supporting fetch fails, the failure
|
|
491
|
+
belongs in `Content` as an optional the view simply does not render - not as a screen-level
|
|
492
|
+
error that discards the data that did arrive. Say so at the property: what nil means and
|
|
493
|
+
why it is non-fatal. Promoting a non-critical failure to a screen error is the finding, and
|
|
494
|
+
so is silently defaulting it to an empty value, which makes "absent" and "empty" the same.
|
|
495
|
+
**The measurement:** count the mutually-dependent state properties the view reads to decide
|
|
496
|
+
what to render. Three or more that must be read together, or any pair whose illegal
|
|
497
|
+
combination the type permits, is the finding.
|
|
498
|
+
|
|
375
499
|
- id: STRUCT-07
|
|
376
500
|
title: A scene is one type - no inner view struct wrapping it
|
|
377
501
|
severity: important
|
|
@@ -394,6 +518,17 @@ rules:
|
|
|
394
518
|
`XUseCase.swift` holds the protocol; `XUseCaseLive.swift` sits next to it. Same for a
|
|
395
519
|
repository. A reader opening the protocol sees the contract without scrolling past an
|
|
396
520
|
implementation, and the implementation file is where the collaborators are declared.
|
|
521
|
+
**The split earns its keep when there is an implementation to scroll past.** A repository, or
|
|
522
|
+
any implementation with a body - request construction, mapping, cache reads, error
|
|
523
|
+
projection - is split: that is the case the rule is for. A use case that is a pure
|
|
524
|
+
pass-through, whose whole body forwards one call to one collaborator, may keep the protocol,
|
|
525
|
+
the live type and its null object (TEST-08) in one file: there is no contract to scroll past,
|
|
526
|
+
and splitting a dozen of those produces three dozen files whose names differ by a suffix.
|
|
527
|
+
Decide by whether the implementation has anything a reader must skip, never by the layer's
|
|
528
|
+
name - and apply one answer across the module, because the cost of this rule is a reader
|
|
529
|
+
guessing which file a type is in.
|
|
530
|
+
A pass-through use case is worth a second look for a different reason: SVC-01 asks what it
|
|
531
|
+
adds over calling the repository directly. That is a separate finding from this one.
|
|
397
532
|
|
|
398
533
|
- id: STRUCT-06
|
|
399
534
|
title: A validation rule is a shared type until measurement says otherwise
|
|
@@ -450,10 +585,29 @@ rules:
|
|
|
450
585
|
rationale: flexibility
|
|
451
586
|
|
|
452
587
|
- id: SVC-05
|
|
453
|
-
title: One
|
|
588
|
+
title: One failure vocabulary per module, built through one factory - outcomes may be screen-scoped
|
|
454
589
|
severity: blocking
|
|
455
590
|
enforcement: judgement
|
|
456
591
|
rationale: testability
|
|
592
|
+
check: >
|
|
593
|
+
Two things used to be one rule here, and conflating them pushed modules toward the wrong fix
|
|
594
|
+
in both directions. Separate them:
|
|
595
|
+
**The failure vocabulary is module-scoped.** The set of ways a call can fail - offline,
|
|
596
|
+
timeout, unauthorised, not-found, conflict, server, unknown - is one type the whole module
|
|
597
|
+
shares, produced by one factory that projects the transport error onto it. This is the
|
|
598
|
+
blocking half. The tell that it was skipped: the same offline / timeout / server triple
|
|
599
|
+
hand-written into a dozen screen-local types, each classifying the transport error again, so
|
|
600
|
+
a new failure kind means a dozen edits and the classifications drift apart. Count the
|
|
601
|
+
declarations of the same failure kind across the module; more than one home for the same
|
|
602
|
+
concept is the finding.
|
|
603
|
+
**The outcome type may be screen-scoped.** SVC-09 allows a screen to declare its own closed
|
|
604
|
+
outcome enum, whose success cases carry that screen's destinations. That is not a second
|
|
605
|
+
error family and does not violate this rule - provided the failure cases PROJECT FROM the
|
|
606
|
+
module vocabulary rather than redeclaring it. A screen-scoped outcome that reaches for the
|
|
607
|
+
transport error directly, or invents its own failure spelling, is the violation; one that
|
|
608
|
+
names the shared kinds is correct.
|
|
609
|
+
The distinction in one line: **how a call can fail is a module fact; what a screen does next
|
|
610
|
+
is a screen fact.**
|
|
457
611
|
|
|
458
612
|
- id: SVC-06
|
|
459
613
|
title: Spec discipline - nothing hand-written around the generator, no endpoint in two specs
|
|
@@ -467,6 +621,12 @@ rules:
|
|
|
467
621
|
enforcement: lint
|
|
468
622
|
mechanism: custom regex - non-`send` funcs in repository protocols surface for an explicit marker
|
|
469
623
|
rationale: readability
|
|
624
|
+
applies_when: >
|
|
625
|
+
the module's overlay binds `ServiceNamingScheme: send-path`. Unbound, or bound to
|
|
626
|
+
`domain-verb`, this rule is DISABLED for the module - see module_overlay_slots. The scheme is
|
|
627
|
+
a module-wide decision because its whole value is one spelling on both sides of the wire;
|
|
628
|
+
what the rule then enforces is internal consistency with the declared scheme, never
|
|
629
|
+
conformity to a sibling module's choice.
|
|
470
630
|
check: >
|
|
471
631
|
`send` + the endpoint path, segments in their own order, camel-cased, path parameters
|
|
472
632
|
dropped: `check-open-status` → `sendCheckOpenStatus`, `order/items/save` →
|
|
@@ -505,10 +665,12 @@ rules:
|
|
|
505
665
|
mechanism: custom regex - arithmetic and conditional expressions inside Mapper paths
|
|
506
666
|
rationale: testability
|
|
507
667
|
check: >
|
|
508
|
-
There are exactly
|
|
509
|
-
|
|
510
|
-
`// MARK: - Derived`), when more than one screen
|
|
511
|
-
|
|
668
|
+
There are exactly three homes for a business rule, and RULE-01 carries the ladder that
|
|
669
|
+
picks between them: the **view model** that owns the behaviour; a **computed property on
|
|
670
|
+
the struct the rule is about** (under a `// MARK: - Derived`), when more than one screen
|
|
671
|
+
asks the same question of data the struct already holds; or a **named rule namespace**
|
|
672
|
+
(RULE-01), when several screens share a rule that is screen policy rather than entity
|
|
673
|
+
state. Every other layer is transport. A mapper, a `UseCaseLive`, a repository, a DTO mirror, a
|
|
512
674
|
coordinator, a data source - each moves values between two shapes and decides nothing. A
|
|
513
675
|
`UseCaseLive` in particular reads as a tempting home because it already knows the domain
|
|
514
676
|
vocabulary: it does not get one. Its body is call → map → return, plus cache read/write;
|
|
@@ -556,6 +718,9 @@ rules:
|
|
|
556
718
|
produce, because the state is computed from the same inputs in both.
|
|
557
719
|
- **In the view model** - when the question is that one screen's, or when the answer needs
|
|
558
720
|
anything the entity does not hold (localized copy, a feature flag, live user state).
|
|
721
|
+
- **In a named rule namespace** (RULE-01) - when several screens share the rule but it is
|
|
722
|
+
screen policy rather than entity state, so putting it on the entity would make a data
|
|
723
|
+
carrier answer a question about presentation. RULE-01 carries the selection ladder.
|
|
559
724
|
Never in the mapper, and never as a stored field the mapper fills, because a stored field
|
|
560
725
|
is indistinguishable from a wire value at every call site that reads it.
|
|
561
726
|
|
|
@@ -568,6 +733,75 @@ rules:
|
|
|
568
733
|
emits or drops a field, an aggregation whose result is a screen state
|
|
569
734
|
(`contains { ... } ? .invalid : .valid`), and any localized string.
|
|
570
735
|
|
|
736
|
+
- id: SVC-09
|
|
737
|
+
title: A service outcome is a closed enum - a failure is a value, not control flow
|
|
738
|
+
severity: important
|
|
739
|
+
enforcement: judgement
|
|
740
|
+
rationale: testability
|
|
741
|
+
check: >
|
|
742
|
+
A boundary method returns ONE type that enumerates every outcome the caller must handle,
|
|
743
|
+
and the caller `switch`es over it exhaustively. The compiler then answers "is any outcome
|
|
744
|
+
unhandled?", which no `catch` can. SVC-01 already bans `throws` alongside a result family;
|
|
745
|
+
this rule says what the family looks like.
|
|
746
|
+
Three properties make it work, and each is a separate finding when missing:
|
|
747
|
+
- **Total.** Success, business failure, and every transport failure the screen renders
|
|
748
|
+
differently are cases of the same type. A method that returns a result AND throws has two
|
|
749
|
+
outcome channels, so no `switch` is exhaustive.
|
|
750
|
+
- **Projected, not leaked.** The transport error family (the networking package's own error
|
|
751
|
+
type) is projected onto the screen's outcome inside the data layer, through one shared
|
|
752
|
+
projection helper rather than a per-repository `switch`. The transport type never appears
|
|
753
|
+
in a Domain or Presentation signature (FLEX-05). A repository that returns the transport
|
|
754
|
+
error verbatim has moved the classification into every caller.
|
|
755
|
+
- **Named after the outcome, not the payload.** A success case may carry a DESTINATION
|
|
756
|
+
rather than data - a redirect the backend chose, resolved from the wire value into a case
|
|
757
|
+
(NAME-04) so no raw string reaches the view model. That is the shape's main advantage over
|
|
758
|
+
a bare `Result<Payload, Error>`, and it is why the two coexist.
|
|
759
|
+
Where the error spine lives is SVC-05's question, not this one: the outcome enum is
|
|
760
|
+
screen-scoped, the failure vocabulary it projects from is module-scoped. Duplicating the
|
|
761
|
+
same offline / timeout / server triple into each screen's enum is the SVC-05 finding.
|
|
762
|
+
**The measurement, for a finding to be one:** per boundary method, the outcome-channel count
|
|
763
|
+
(a result type plus a `throws` is two), the number of call sites that re-classify a transport
|
|
764
|
+
error, and whether any non-exhaustive `switch` over the result exists. No count, no finding.
|
|
765
|
+
|
|
766
|
+
# ── RULE - business-rule surface ──────────────────────────────────────────
|
|
767
|
+
- id: RULE-01
|
|
768
|
+
title: A shared business rule lives in a named rule namespace, as a pure static, traceable to its source
|
|
769
|
+
severity: important
|
|
770
|
+
enforcement: judgement
|
|
771
|
+
rationale: testability
|
|
772
|
+
check: >
|
|
773
|
+
SVC-08 names three homes for a business rule. The ladder that picks between them, cheapest
|
|
774
|
+
rung first:
|
|
775
|
+
1. **One screen asks it** -> the view model. Stop here. Do not create a namespace for one
|
|
776
|
+
caller; a namespace with a single consumer is over-hoisting (STRUCT-05).
|
|
777
|
+
2. **Several screens ask it, of data the entity already carries** -> a computed property on
|
|
778
|
+
the entity, under `// MARK: - Derived`. This is also what keeps test doubles honest: a
|
|
779
|
+
double can no longer hand-set a state the real payload could not produce.
|
|
780
|
+
3. **Several screens ask it, but the answer is screen POLICY rather than entity state** ->
|
|
781
|
+
a named rule namespace. Putting it on the entity would make a data carrier answer a
|
|
782
|
+
question about presentation; leaving it in one view model hides it from the others.
|
|
783
|
+
The namespace's shape, and each property is what makes it auditable:
|
|
784
|
+
- A **caseless enum** (never a struct with an initialiser, never a class), so it cannot be
|
|
785
|
+
instantiated or hold state.
|
|
786
|
+
- **Static, pure functions.** Inputs in, value out. No stored state, no I/O, no logging, no
|
|
787
|
+
localized copy. A rule that needs the environment takes it as a parameter - a clock as a
|
|
788
|
+
defaulted parameter is the accepted seam, and it is what keeps TEST-01 satisfied without
|
|
789
|
+
an injected container for a value transform.
|
|
790
|
+
- It lives in the module's **domain layer**, beside the entities it reads, not beside the
|
|
791
|
+
scene that needed it first.
|
|
792
|
+
- It is **not** a formatter, a mapper, or a validator. A value transform is READ-04d, a
|
|
793
|
+
shape lowering is SVC-08, a form rule is STRUCT-06. A namespace that accumulates all four
|
|
794
|
+
is a Utils bucket with a better name (STRUCT-04).
|
|
795
|
+
**Traceability is part of the rule, not a nicety.** Each function names the requirement it
|
|
796
|
+
enforces - the spec clause, business-rule ID, or ticket - in its doc comment, and the test
|
|
797
|
+
that covers it repeats that same token in its name. One grep then reaches requirement, code
|
|
798
|
+
and test. This is also the rule's own measurement: a namespace whose functions cite nothing
|
|
799
|
+
cannot be checked against anything, and a cited rule with no test of the same token is an
|
|
800
|
+
untested requirement, which is the finding.
|
|
801
|
+
**What the doc comment is for.** Recording the decision - which input the rule keys off and
|
|
802
|
+
why the obvious alternative was rejected - is exactly the case READ-02 protects; it is not
|
|
803
|
+
trimmed. This rule does not require such a comment, it requires the citation.
|
|
804
|
+
|
|
571
805
|
- id: PLAT-01
|
|
572
806
|
title: Platform affordances go through the app's own wrapper, not the OS API
|
|
573
807
|
severity: blocking
|
|
@@ -704,12 +938,16 @@ rules:
|
|
|
704
938
|
|
|
705
939
|
# ── UI ────────────────────────────────────────────────────────────────────
|
|
706
940
|
- id: UI-01
|
|
941
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
942
|
+
scope_reason: SwiftUI view composition - a UIKit view controller hierarchy expresses reuse differently
|
|
707
943
|
title: A screen's composite view is never consumed by another screen
|
|
708
944
|
severity: blocking
|
|
709
945
|
enforcement: judgement
|
|
710
946
|
rationale: flexibility
|
|
711
947
|
|
|
712
948
|
- id: UI-02
|
|
949
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
950
|
+
scope_reason: SwiftUI view construction
|
|
713
951
|
title: No new component is added to a UI target the module marks as frozen
|
|
714
952
|
severity: important
|
|
715
953
|
enforcement: lint
|
|
@@ -717,12 +955,16 @@ rules:
|
|
|
717
955
|
rationale: flexibility
|
|
718
956
|
|
|
719
957
|
- id: UI-03
|
|
958
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
959
|
+
scope_reason: SwiftUI view construction
|
|
720
960
|
title: Every screen with user actions has its analytics surface; no direct generated-event calls
|
|
721
961
|
severity: important
|
|
722
962
|
enforcement: judgement
|
|
723
963
|
rationale: readability
|
|
724
964
|
|
|
725
965
|
- id: UI-04
|
|
966
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
967
|
+
scope_reason: SwiftUI copy surface (LocalizedText); UIKit modules localise through a different seam
|
|
726
968
|
title: All user copy goes through the screen's copy surface
|
|
727
969
|
severity: important
|
|
728
970
|
enforcement: lint
|
|
@@ -844,11 +1086,24 @@ rules:
|
|
|
844
1086
|
rationale: testability
|
|
845
1087
|
|
|
846
1088
|
- id: TEST-04
|
|
847
|
-
title: Test doubles follow one named taxonomy - stub, spy, fake, mock
|
|
1089
|
+
title: Test doubles follow one named taxonomy - stub, spy, fake, mock, builder
|
|
848
1090
|
severity: suggestion
|
|
849
1091
|
enforcement: judgement
|
|
850
1092
|
rationale: testability
|
|
851
|
-
check:
|
|
1093
|
+
check: >
|
|
1094
|
+
One kind per file, name states the kind, signature parity with the real type (SVC-02). All of
|
|
1095
|
+
them live in the test target (TEST-08).
|
|
1096
|
+
**The builder is the fifth kind and the one most often missing.** Where tests need many
|
|
1097
|
+
near-identical entities differing in one field, the alternative to a builder is a wall of
|
|
1098
|
+
full initialiser calls - so a test's actual subject drowns in fifty lines of scaffolding, and
|
|
1099
|
+
a new field on the entity breaks every test that never cared about it. A builder is a base
|
|
1100
|
+
value plus chainable single-field overrides returning a copy
|
|
1101
|
+
(`.<baseCase>().with<Field>(...)`), so each test states ONLY what it varies and reads as its
|
|
1102
|
+
own precondition. Two properties keep it honest: it produces the real type (never a
|
|
1103
|
+
parallel test-only mirror, which drifts), and each override sets one field, so a reader can
|
|
1104
|
+
tell what a test depends on from its chain.
|
|
1105
|
+
The finding is not "no builder exists" - it is a test file where the setup of a case is
|
|
1106
|
+
longer than its assertion, repeated. Report the ratio, not the absence.
|
|
852
1107
|
|
|
853
1108
|
- id: TEST-05
|
|
854
1109
|
title: Async behaviour is testable - no unstructured task in logic, no sleep, injected clock
|
|
@@ -870,6 +1125,41 @@ rules:
|
|
|
870
1125
|
enforcement: scan
|
|
871
1126
|
rationale: testability
|
|
872
1127
|
|
|
1128
|
+
- id: TEST-08
|
|
1129
|
+
title: A null object may ship in production sources; a test double may not
|
|
1130
|
+
severity: important
|
|
1131
|
+
enforcement: lint
|
|
1132
|
+
mechanism: >
|
|
1133
|
+
custom regex - a type whose name marks it as a double (Mock / Stub / Spy / Fake / Fixture
|
|
1134
|
+
prefix or suffix) declared under the module's production source root rather than its test
|
|
1135
|
+
target. Whether a given production type is a null object or a disguised double is
|
|
1136
|
+
JUDGEMENT: the name is the signal a regex can see, the payload is not.
|
|
1137
|
+
rationale: testability
|
|
1138
|
+
check: >
|
|
1139
|
+
Two kinds of type conform to a protocol without doing the real work, and only one of them
|
|
1140
|
+
belongs in production sources.
|
|
1141
|
+
**A null object does.** It satisfies the protocol by doing nothing and returning the empty
|
|
1142
|
+
answer - no event sent, `nil` returned, an empty list. It is the honest default for a
|
|
1143
|
+
dependency the caller has legitimately not wired: a defaulted initialiser parameter, a
|
|
1144
|
+
canvas preview, a screen whose optional slot is not configured yet. It carries no data, so
|
|
1145
|
+
it cannot drift from the real payload, and it makes the seam visible in the DI graph instead
|
|
1146
|
+
of forcing an optional dependency on every call site. Name it so the reader knows what it is
|
|
1147
|
+
(a `Noop`-style prefix, consistently across the module) and let it be found by grep.
|
|
1148
|
+
**A test double does not.** A type that carries canned payloads - a mock repository
|
|
1149
|
+
returning a hand-built response, a fixture of sample records - ships that data to users,
|
|
1150
|
+
grows in the store binary, and can be resolved by accident because nothing but the name
|
|
1151
|
+
says it is not real. It belongs in the test target, where the compiler keeps it out of the
|
|
1152
|
+
app. Where a canned payload is genuinely needed AT RUNTIME (a demo build, an offline
|
|
1153
|
+
preview mode), that is a debug affordance and SEC-09 governs it: the question is what
|
|
1154
|
+
removes it from the store build, and "it is only referenced by the mock path" is not an
|
|
1155
|
+
answer.
|
|
1156
|
+
Distinguish by payload, never by name: a `Mock` that returns nothing is a null object with
|
|
1157
|
+
the wrong name (rename it), and a `Noop` that returns three sample bookings is a double in
|
|
1158
|
+
the wrong target (move it).
|
|
1159
|
+
**The measurement:** per module, the count of double-named types under the production source
|
|
1160
|
+
root, split into carries-payload and returns-empty. The first number is the finding; the
|
|
1161
|
+
second is a naming fix.
|
|
1162
|
+
|
|
873
1163
|
# ── FLEX - intra-module flexibility ───────────────────────────────────────
|
|
874
1164
|
- id: FLEX-01
|
|
875
1165
|
title: Layers meet through protocols; no concrete cross-layer type in a signature
|
|
@@ -965,11 +1255,30 @@ rules:
|
|
|
965
1255
|
pasteboard writes explicit and expiring; nothing sensitive cached to disk by default.
|
|
966
1256
|
|
|
967
1257
|
- id: SEC-06
|
|
968
|
-
title: Analytics and crash payloads are redacted
|
|
1258
|
+
title: Analytics and crash payloads are redacted, through a named helper, with a test that proves it
|
|
969
1259
|
severity: blocking
|
|
970
1260
|
enforcement: judgement
|
|
971
1261
|
rationale: security
|
|
972
|
-
check:
|
|
1262
|
+
check: >
|
|
1263
|
+
Cross-check every analytics event's parameter list, user properties, breadcrumbs and
|
|
1264
|
+
non-fatal payloads against the module's sensitive-data inventory. A class marked
|
|
1265
|
+
`loggable: never` must not appear at all; one marked `hashed-or-truncated-only` appears only
|
|
1266
|
+
in its reduced form.
|
|
1267
|
+
Redaction that lives at the call site is not redaction - it is a habit, and it fails on the
|
|
1268
|
+
event somebody adds next month. Three properties, each a separate finding:
|
|
1269
|
+
- **One named helper does the reduction.** A hashing or truncating function with a name,
|
|
1270
|
+
callable from anywhere the value is emitted, so there is one implementation to review and
|
|
1271
|
+
one thing to grep. Not an inline expression repeated per event.
|
|
1272
|
+
- **The event surface carries only the reduced form.** The typed event (UI-03) declares the
|
|
1273
|
+
parameter as the hash or the truncation, never the raw value with a comment asking callers
|
|
1274
|
+
to reduce it first. A parameter that CAN hold the raw value eventually does.
|
|
1275
|
+
- **The redaction has a test.** At minimum: the output is deterministic for the same input,
|
|
1276
|
+
differs for different inputs, and does not contain the input. A redaction nobody tested is
|
|
1277
|
+
a claim; these three assertions are cheap and turn it into a property. This is the rule's
|
|
1278
|
+
measurement - an untested reduction helper is the finding even when the code is correct.
|
|
1279
|
+
Truncation needs one extra check that hashing does not: that what remains cannot identify
|
|
1280
|
+
the subject on its own, and cannot be joined against another field in the same event to do
|
|
1281
|
+
so. Two separately-harmless truncations in one payload are not harmless.
|
|
973
1282
|
|
|
974
1283
|
- id: SEC-07
|
|
975
1284
|
title: Permissions are least-privilege with accurate purpose strings
|
|
@@ -1103,6 +1412,8 @@ rules:
|
|
|
1103
1412
|
|
|
1104
1413
|
# ── A11Y ──────────────────────────────────────────────────────────────────
|
|
1105
1414
|
- id: A11Y-01
|
|
1415
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
1416
|
+
scope_reason: SwiftUI accessibility modifiers
|
|
1106
1417
|
title: An identifier from the shared source on every interactive element
|
|
1107
1418
|
severity: important
|
|
1108
1419
|
enforcement: lint
|
|
@@ -1110,18 +1421,24 @@ rules:
|
|
|
1110
1421
|
rationale: testability
|
|
1111
1422
|
|
|
1112
1423
|
- id: A11Y-02
|
|
1424
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
1425
|
+
scope_reason: SwiftUI accessibility modifiers
|
|
1113
1426
|
title: Localized VoiceOver label, plus a hint where the action is not obvious
|
|
1114
1427
|
severity: important
|
|
1115
1428
|
enforcement: judgement
|
|
1116
1429
|
rationale: accessibility
|
|
1117
1430
|
|
|
1118
1431
|
- id: A11Y-03
|
|
1432
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
1433
|
+
scope_reason: SwiftUI accessibility modifiers
|
|
1119
1434
|
title: Minimum 44x44 tap target; grouped content exposes one meaningful element
|
|
1120
1435
|
severity: important
|
|
1121
1436
|
enforcement: judgement
|
|
1122
1437
|
rationale: accessibility
|
|
1123
1438
|
|
|
1124
1439
|
- id: A11Y-04
|
|
1440
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
1441
|
+
scope_reason: SwiftUI accessibility modifiers
|
|
1125
1442
|
title: Dynamic Type does not break layout at the largest accessibility sizes
|
|
1126
1443
|
severity: important
|
|
1127
1444
|
enforcement: judgement
|
|
@@ -1129,6 +1446,8 @@ rules:
|
|
|
1129
1446
|
check: No fixed-height container holding scalable text.
|
|
1130
1447
|
|
|
1131
1448
|
- id: A11Y-05
|
|
1449
|
+
scope: { frameworks: [swiftui], paths: ["**/*View.swift", "**/*Screen.swift", "**/*Scene.swift", "**/*Cell.swift", "**/*Configuration.swift", "**/*+Modifiers.swift"] }
|
|
1450
|
+
scope_reason: SwiftUI accessibility modifiers
|
|
1132
1451
|
title: RTL mirrors correctly; no leading/trailing hardcoded as left/right
|
|
1133
1452
|
severity: important
|
|
1134
1453
|
enforcement: lint
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Auto-generated by `pipeline/scripts/build-skills-index.mjs` - do not hand-edit.
|
|
4
4
|
> Regenerate with `node pipeline/scripts/build-skills-index.mjs`.
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**196 skills** across 2 groups.
|
|
7
7
|
|
|
8
8
|
| Group | Name | Platform | Description |
|
|
9
9
|
|-------|------|----------|-------------|
|
|
@@ -75,12 +75,13 @@
|
|
|
75
75
|
| external | `html-semantic` | - | Semantic HTML5: elements, forms with validation and accessibility, SEO best practices, structured data, Open Graph, Twitter Cards. Use when |
|
|
76
76
|
| external | `humanizer` | - | \| |
|
|
77
77
|
| external | `ios-accessibility` | - | Implements, reviews, or improves accessibility in iOS/macOS apps with SwiftUI, UIKit, and AppKit. Use when adding VoiceOver, Voice Control, |
|
|
78
|
+
| external | `ios-coding-standard` | - | The iOS coding-standard rule registry: 99 stable-ID rules across readability, security, service layer, business rules, concurrency, testing, |
|
|
78
79
|
| external | `ios-debugger-agent` | - | Debug the current iOS project on a booted simulator with XcodeBuildMCP. Use when an iOS bug has to be reproduced and diagnosed on a booted s |
|
|
79
80
|
| external | `ios-developer` | - | Develop native iOS applications with Swift/SwiftUI. Masters iOS 18, SwiftUI, UIKit integration, Core Data, networking, and App Store optimiz |
|
|
80
81
|
| external | `ios-localization` | - | Implement, review, or improve localization and internationalization in iOS/macOS apps - String Catalogs (.xcstrings), generated localizabl |
|
|
81
82
|
| external | `ios-networking` | - | Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patt |
|
|
82
83
|
| external | `ios-security` | - | Secure iOS apps with Keychain Services, CryptoKit encryption, biometric authentication (Face ID, Touch ID), Secure Enclave key storage, LACo |
|
|
83
|
-
| external | `ios-simulator` | - | Manages iOS Simulator devices and tests app
|
|
84
|
+
| external | `ios-simulator` | - | Manages iOS Simulator devices and tests app behaviour with xcrun simctl: device lifecycle, app install and launch, push notification and loc |
|
|
84
85
|
| external | `kotlin-coroutines-expert` | - | Expert patterns for Kotlin Coroutines and Flow, covering structured concurrency, error handling, and testing. Use when writing or reviewing |
|
|
85
86
|
| external | `live-activities` | - | Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time upda |
|
|
86
87
|
| external | `macos-menubar-tuist-app` | - | Build, refactor, or review SwiftUI macOS menubar apps that use Tuist. Use when building, refactoring or reviewing a SwiftUI macOS menubar ap |
|
|
@@ -96,15 +97,16 @@
|
|
|
96
97
|
| core | `multi-agent-channels` | - | Multi-channel reporter - Jira/Confluence/Wiki/PR description. Multi-select channels + content, humanizer pass, reviewer-preserving Bitbuck |
|
|
97
98
|
| core | `multi-agent-create-jira` | - | Create a standards-compliant Jira issue (Task / Bug / Story): asks the type, mines project conventions, drafts from a standard template with |
|
|
98
99
|
| core | `multi-agent-design-check` | - | Mock-mode vs Figma design audit (iOS / Android, local-only). Pick repo + module, gate on mock support, enumerate every state driver into a c |
|
|
99
|
-
| core | `multi-agent-dev` | - | Fast development mode: Init → Dev (Opus) → Test → Commit → Report. Analysis
|
|
100
|
-
| core | `multi-agent-dev-autopilot` | - | Fastest mode: Dev (Opus) plus Autopilot. Init → Dev → Commit → Report with zero confirmations.
|
|
101
|
-
| core | `multi-agent-dev-local` | - | Fast mode + local - Init → Dev(Opus) → Commit → Report, no worktree. Use when a change should be developed on the
|
|
100
|
+
| core | `multi-agent-dev` | - | Fast development mode: Init → Dev (Opus) → Review → Test → Commit → Report. Analysis and planning are skipped, review is not. Use when the w |
|
|
101
|
+
| core | `multi-agent-dev-autopilot` | - | Fastest mode: Dev (Opus) plus Autopilot. Init → Dev → Review → Commit → Report with zero confirmations. Review still runs and auto-fixes blo |
|
|
102
|
+
| core | `multi-agent-dev-local` | - | Fast mode + local - Init → Dev(Opus) → Review → Commit → Report, no worktree. Use when a change should be developed and reviewed on the cu |
|
|
102
103
|
| core | `multi-agent-dev-local-autopilot` | - | Fastest + local - Dev(Opus) + autopilot, no worktree, zero interaction. Use when a change should be developed on the current branch with n |
|
|
103
104
|
| core | `multi-agent-diff-explain` | - | Map Phase 4 triage findings to branch diff lines. Read-only post-hoc command, used after review to answer 'which finding lines up with which |
|
|
104
|
-
| core | `multi-agent-
|
|
105
|
+
| core | `multi-agent-ship` | - | Continue already-done LOCAL work through the pipeline tail: Review → Build+Test → Commit/PR → Report (technical analysis + Jira test-scenari |
|
|
105
106
|
| core | `multi-agent-forget` | - | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
|
|
106
107
|
| core | `multi-agent-garbage-collect` | - | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
|
|
107
108
|
| core | `multi-agent-help` | - | Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatib |
|
|
109
|
+
| core | `multi-agent-ios-coding-standard` | - | Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to dev/dev-l |
|
|
108
110
|
| core | `multi-agent-issue` | - | List unassigned GitHub issues, pick one, auto-assign, and launch the multi-agent pipeline. Use when a GitHub issue should be picked up and s |
|
|
109
111
|
| core | `multi-agent-jira` | - | List open Jira issues, pick one, and launch the multi-agent pipeline. Use when a Jira issue should be picked up and started without knowing |
|
|
110
112
|
| core | `multi-agent-kill` | - | Stop the given task, then remove its worktree and branch. Asks for confirmation. Use when a running or stuck task should be stopped and its |
|
|
@@ -129,6 +131,7 @@
|
|
|
129
131
|
| core | `multi-agent-status` | - | Show every multi-agent task's ID, phase, branch, and status. Use when asked what is running, or for an overview of every task. |
|
|
130
132
|
| core | `multi-agent-sync` | - | One-shot sync of the entire multi-agent ecosystem: Claude Code, Copilot CLI, pipeline repo, website, and the dev-toolkit MCP server. Use whe |
|
|
131
133
|
| core | `multi-agent-test` | - | UI Bug Hunter - iOS Simulator (simctl) + Android Emulator (adb). Auto-detects platform. Screenshot + tap + analyze on the booted device. T |
|
|
134
|
+
| core | `multi-agent-testflight-validation` | - | Pre-submission validation for a TestFlight / App Store build (iOS, local-only). Three gates: static archive audit, Apple's own `altool --val |
|
|
132
135
|
| core | `multi-agent-uninstall` | - | Uninstall the pipeline from Claude Code + Copilot CLI. Keychain access tokens are always left untouched; --all-data also clears pipeline set |
|
|
133
136
|
| core | `multi-agent-update` | - | Update the pipeline to the latest version: git pull, install, migrate. Use when the installed pipeline is behind and should be brought to th |
|
|
134
137
|
| external | `musickit-audio` | - | Integrate Apple Music playback, catalog search, and Now Playing metadata using MusicKit and MediaPlayer. Use when adding music search, Apple |
|
|
@@ -155,7 +158,7 @@
|
|
|
155
158
|
| external | `speech-recognition` | - | Transcribe speech to text using Apple's Speech framework. Use when implementing live microphone transcription with AVAudioEngine, recognizin |
|
|
156
159
|
| external | `spm-build-analysis` | - | Analyze Swift Package Manager dependencies, package plugins, module variants, and CI-oriented build overhead that slow Xcode builds. Use whe |
|
|
157
160
|
| external | `storekit` | - | Implement, review, or improve in-app purchases and subscriptions using StoreKit 2. Use when building paywalls with SubscriptionStoreView or |
|
|
158
|
-
| external | `swift-api-design-guidelines` | - |
|
|
161
|
+
| external | `swift-api-design-guidelines` | - | Swift API Design Guidelines: clarity at the point of use, naming for readability over brevity, argument labels and prepositional phrases, me |
|
|
159
162
|
| external | `swift-architecture` | - | Select, implement, or migrate between app architecture patterns for Apple platform apps. Use when choosing between MV (Model-View with @Obse |
|
|
160
163
|
| external | `swift-charts` | - | Implement, review, or improve data visualizations using Swift Charts. Use when building bar, line, area, point, pie, donut, or iOS 26 3D cha |
|
|
161
164
|
| external | `swift-codable` | - | Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(f |
|
|
@@ -169,7 +172,7 @@
|
|
|
169
172
|
| external | `swift-testing-pro` | - | Writes, reviews, and improves Swift Testing code using modern APIs and best practices. Use when reading, writing, or reviewing projects that |
|
|
170
173
|
| external | `swiftdata` | - | Implement, review, or improve data persistence using SwiftData. Use when defining @Model classes with @Attribute, @Relationship, @Transient, |
|
|
171
174
|
| external | `swiftdata-pro` | - | Writes, reviews, and improves SwiftData code using modern APIs and best practices. Use when reading, writing, or reviewing projects that use |
|
|
172
|
-
| external | `swiftlint` | - | Configures and enforces SwiftLint
|
|
175
|
+
| external | `swiftlint` | - | Configures and enforces SwiftLint via build tool plugins, run scripts and CI: .swiftlint.yml rule sets, analyzer rules, baselines, autocorre |
|
|
173
176
|
| external | `swiftui-animation` | - | Implement, review, or improve SwiftUI animations and transitions. Use when adding explicit animations with withAnimation, configuring implic |
|
|
174
177
|
| external | `swiftui-expert-skill` | - | Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, macOS-specific APIs, an |
|
|
175
178
|
| external | `swiftui-gestures` | - | Implement, review, or improve SwiftUI gesture handling. Use when adding tap, long press, drag, magnify, or rotate gestures, composing gestur |
|