@mmerterden/multi-agent-pipeline 16.5.0 → 16.6.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,38 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ### Fixed
20
+
21
+ - **The website sync committed under whatever identity the run carried, and the site silently stopped updating.** Step 4 ran a bare `git commit`, so the commit took the active account's address. The deploy platform builds only a commit whose author is a contributor on the project; any other author is accepted by the push and then never built - the deployment is created, reports `readyState: BLOCKED` (rendered by the CLI as `UNKNOWN` with a 0ms build), and the live site keeps serving the previous version. v16.4.0 and v16.5.0 were both pushed that way, neither was ever built, and both syncs reported the website as done.
22
+ - **The commit is now made by `pipeline/scripts/website-deploy-commit.sh`, not by three lines of prose.** It reads the clone's own `user.name` / `user.email` and writes only on a mismatch (the website clone is usually already right, and overwriting it with the caller's identity is the defect), commits only when something is staged, reads the author back off the commit with `git log -1 --format=%ae` and halts before pushing on a mismatch - setting `git config` proves nothing, since an exported `GIT_AUTHOR_EMAIL` outranks it - then waits for a Ready production build instead of treating the push as the deploy. Exit codes separate the cases that need different responses: `1` wrong author and nothing pushed, `3` pushed but never built. The identity itself still resolves from `prefs.global.identities[]` routed by `platformIdentityRouting`, so no literal address enters a file that installs on every machine.
23
+ - **`multi-agent-refs/website-deploy.md`** carries the failure signature, the API call that names the reason the CLI hides, the empty-commit recovery that needs no history rewrite, and the live-site verification notes. The sync doc keeps three lines and a pointer, which is also what kept it inside its token ceiling.
24
+
25
+ ### Added
26
+
27
+ - **`smoke-website-deploy-identity.sh` (20 assertions).** It runs the script against throwaway repos with real remotes rather than grepping the doc: the happy path lands and pushes, an exported `GIT_AUTHOR_EMAIL` halts with the commit still local, an unchanged tree makes no empty commit, a matching config is preserved while a wrong name is corrected, and missing or non-repository arguments exit 2. Wiring is asserted separately, since a correct script nothing calls is its own failure mode.
28
+
29
+ ## [16.6.0] - 2026-08-25
30
+
31
+ Two analysis standards, one evidence record, and a references section that is built rather than remembered.
32
+
33
+ ### Added
34
+
35
+ - **`/multi-agent:analysis` asks which standard the document follows** (Locked 32). Phase 0 Step 1b offers `global` - the 23-section development handoff, unchanged - and `corporate`, a requirements document whose spine is `IG -> UC -> FG`: business requirements, use cases with actor, precondition, main flow and step-bound alternative flows, functional requirements each beginning "Sistem,", service details, and three cross matrices that prove the chain closes. Part A is the requirement document, Part B is the technical analysis, Part C is the development analysis. Both profiles read the same `state.analysisSpec.evidence.*`: intake, fetching, repo evidence and convention extraction are shared, so the projections cannot drift into two products. One run emits one profile - rendering both would produce two documents about the same feature and leave the next reader to guess which is current.
36
+ - **`pipeline/multi-agent-refs/analysis-template-corporate.md`**, the corporate projection, plus the corporate backbone rule (Locked 33): Part A and the footer render even with zero evidence, carrying `N/A` when a section is genuinely out of scope and `EKLENECEK` when evidence is expected but missing. A requirements document has to let a reader tell "we considered hardware needs and there are none" from "nobody looked", which the global profile's omission rule deliberately cannot express. Every `EKLENECEK` owes a Risks and Open Questions row naming what is missing and who can answer it; one without a row fails the dispatch gate. Missing inputs never halt the run - the gap is written down and raised, not waited on.
37
+ - **The traceability matrix is cross-checked, not just required.** In the corporate profile every `IG`, `UC` and `FG` id defined in the document must appear in the matrix, and every id in the matrix must be defined somewhere else; both directions block dispatch. A matrix that merely exists is not the claim worth making, because every downstream reader trusts it instead of re-deriving the chain, and a requirement quietly missing from it is invisible exactly where it matters. The remaining consistency rules (an IG realised by a use case, an FG naming a source that exists, a cancelled requirement struck through everywhere) stay renderer obligations and are written as instructions rather than as guarantees.
38
+ - **`pipeline/scripts/build-references.mjs`** builds Section 21 from the evidence record instead of leaving it to the model (Locked 34). Each row carries a precision anchor - Figma node id, Confluence `pageId` plus page version, the commit sha a repo was read at, the Swagger spec version - because a reference with no anchor points at a moving target six weeks later. Each row carries an access cell, so a declared source that could not be fetched is listed as unreachable rather than dropped; a silently dropped source reads to the next person as a source that never existed. Statements the user made in conversation that no fetched source carries are recorded verbatim as free-text rows with the decision they settled. A coverage gate blocks dispatch when a consumed source is missing from the table and when a listed row has no evidence behind it: an invented reference is worse than an absent one, because a reader will follow it.
39
+ - **Two preference keys, declared in the schema rather than only in prose.** `global.analysisProfiles` narrows which standards the Step 1b picker offers (listing one auto-resolves the step), and `global.analysisProfile.corporate` carries the corporate profile's deployment bindings: `confluenceSpaceKey`, `confluenceParentPageId`, `titleFormat` and `titlePrefix`. A corporate analysis always lands in the same tree, so the Phase 3.5 destination prompt is skipped when all four are set and falls back to asking when any is missing. The names are deliberately generic, so no organisation's space, page or tooling names live in the repo, and the key set is closed - `global` is `additionalProperties: false`, so a documented-but-undeclared key would have failed `validate-prefs.mjs` for anyone who set it.
40
+ - **Stack selection is optional** (Locked 35). `No platform yet` is a real answer: evidence is still fetched, everything that does not need a target repository renders in full, and only the development layer plus the Pass B projection are skipped, with an open-question row recording why. The output is a single file at `~/Desktop/multiAgentAnalysis/<feature-name>/<feature>.md`: without a repo the usual repo-relative `analysis/` path has nothing to be relative to, and the current working directory is never written to, since for a repo-less run it is arbitrary and creating a folder wherever the command happened to be invoked is the kind of surprise that costs a tool its trust. Desktop rather than a hidden directory because the document is a deliverable meant to be opened and handed over, and `Analysis` because a folder named after the command that produced it is guessable; the Phase 3.5 picker shows the resolved path and takes an override. A requirements document is useful before anyone has decided which repository will hold the code, and refusing to produce one until that decision exists inverts the order the work actually happens in.
41
+
42
+ ### Changed
43
+
44
+ - **The context-budget gate measures what one analysis run pays, not what the tree weighs.** With two profile templates on disk and exactly one loaded per run, summing both would bill every run for a file it never reads - and would push the project toward deleting a template to satisfy a number that was measuring the wrong thing. The gate is now shared refs plus the largest template; the pinned ceiling moves 145000 -> 155000, and the current cost is 146139 bytes.
45
+ - `validate-analysis-doc.mjs` reads `profile` from the front-matter and applies the matching contract. Three global-profile checks - the Section 3 flow-chart warning, the bare `N/A` placeholder warning, and the missing-`BR-` warning - no longer fire on a corporate document, where Section 3 is Business Requirements, `N/A` is required behaviour, and the spine is `IG`/`UC`/`FG`. The Test Plan check matches on title rather than number, so it covers both profiles, and is waived only for the stack-optional render where the development layer is legitimately absent. `platform: none` is a known platform value.
46
+
47
+ ### Fixed
48
+
49
+ - `analysis-template.md` cited the References-at-the-bottom rule as Locked 20; Locked 20 is the localization mode and the References rule is Locked 21.
50
+
19
51
  ## [16.5.0] - 2026-08-25
20
52
 
21
53
  Staying current stops being something a user has to be told to do.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "16.5.0",
3
+ "version": "16.6.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",
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: "Standalone feature-spec analysis. Platform-agnostic concept layer with repo-driven convention extraction (Phase 1c) and per-platform Pass B render. 23 sections in Full mode; 8 of them in Lite mode (auto for small features). Collects Figma / Swagger / Confluence / Jira / Standards (Confluence + Wiki + local file) / Firebase / repo inputs. Stops after emit - does not chain into a dev run. Use when a feature needs a written specification before any code, from Figma, Swagger, Confluence, Jira or repo inputs."
3
- description-tr: "Bağımsız özellik-spesifikasyonu analizi. Repo'dan konvansiyon çıkarımıyla (Faz 1c) platform-bağımsız kavram katmanı ve platform başına Pass B render. Full modda 23 bölüm; Lite modda bunlardan 8'i (küçük işlerde otomatik). Figma / Swagger / Confluence / Jira / Standartlar (Confluence + Wiki + yerel dosya) / Firebase / repo girdilerini toplar. Çıktıyı üretince durur - dev koşusuna zincirlenmez."
2
+ description: "Standalone feature-spec analysis. Two profiles picked at intake: global (23-section development handoff, 8 of them in Lite mode) or corporate (IG/UC/FG requirements document with traceability matrices). Platform-agnostic concept layer with repo-driven convention extraction (Phase 1c) and per-platform Pass B render; stack selection is optional. Collects Figma / Swagger / Confluence / Jira / Standards (Confluence + Wiki + local file) / Firebase / repo inputs. Stops after emit - does not chain into a dev run. Use when a feature needs a written specification before any code."
3
+ description-tr: "Bağımsız özellik-spesifikasyonu analizi. Girişte iki profil: global (23 bölümlük geliştirme dokümanı, Lite modda 8'i) veya kurumsal (izlenebilirlik matrisleriyle IG/UC/FG gereksinim dokümanı). Repo'dan konvansiyon çıkarımıyla (Faz 1c) platform-bağımsız kavram katmanı ve platform başına Pass B render; stack seçimi opsiyonel. Figma / Swagger / Confluence / Jira / Standartlar (Confluence + Wiki + yerel dosya) / Firebase / repo girdilerini toplar. Çıktıyı üretince durur - dev koşusuna zincirlenmez."
4
4
  argument-hint: "[\"<analysis-name>\"] [--lite | --full] [--no-cache] [--preview-conventions]"
5
5
  ---
6
6
 
@@ -18,7 +18,7 @@ This command is **independent** from the orchestrator's Phase 1 analysis (which
18
18
 
19
19
  These decisions are settled. Do not surface them as `AskUserQuestion` items, do not re-derive them from context, do not invite the user to override mid-run. If the user explicitly wants one of them changed, treat that as a separate request and update this list.
20
20
 
21
- The full list of 31, with the category index, lives in `$HOME/.claude/multi-agent-refs/analysis/locked.md`. Read it before the run starts; it is the contract the whole flow is judged against. `/multi-agent:analysis-resolve` inherits the same list.
21
+ The full list of 35, with the category index, lives in `$HOME/.claude/multi-agent-refs/analysis/locked.md`. Read it before the run starts; it is the contract the whole flow is judged against. `/multi-agent:analysis-resolve` inherits the same list.
22
22
 
23
23
  Cite a decision as `Locked <n> (<short label>)` so the category is inferable.
24
24
 
@@ -26,6 +26,19 @@ Cite a decision as `Locked <n> (<short label>)` so the category is inferable.
26
26
 
27
27
  - `$ARGUMENTS` - optional analysis name (e.g. `"UserProfile"`). If empty, asked at Phase 0 Step 1. Stored internally as `state.analysisSpec.featureName` for backward compatibility.
28
28
 
29
+ ## Profile
30
+
31
+ Phase 0 Step 1b asks which analysis standard the document follows (Locked 32), and the answer selects the template. Both profiles read the same evidence: intake, fetching, repo evidence and convention extraction are shared, so the two cannot drift into two products.
32
+
33
+ | Profile | Template | Shape |
34
+ |---|---|---|
35
+ | `global` (default) | `analysis-template.md` | Development handoff, 23 sections. Business rules with Gherkin acceptance criteria, architecture plan, files to add, test plan. Zero-evidence sections drop (Locked 2). |
36
+ | `corporate` | `analysis-template-corporate.md` | Requirements document. `IG -> UC -> FG` spine with three traceability matrices, current and target state with impact analysis, then Technical Analysis and Development Analysis. The Part A backbone always renders, carrying `N/A` or `EKLENECEK` (Locked 33). |
37
+
38
+ One run emits one profile. A missing input never blocks either profile: the gap is written as `EKLENECEK` and raised as a Section 20 open question rather than halting the run.
39
+
40
+ **Stack is optional** (Locked 35). When no platform is selected the run still completes: everything that does not need a target repository renders in full, and only the development layer plus the Pass B projection are skipped, with a Section 20 row recording why. The output is a single file at `~/Desktop/multiAgentAnalysis/<feature-name>/<feature>.md` instead of one per repo: without a repo there is no working tree for the usual `analysis/` path to be relative to, and the current working directory is never written to. The Phase 3.5 picker shows the resolved path and accepts an override.
41
+
29
42
  ## Template
30
43
 
31
44
  v3 template at `$HOME/.claude/multi-agent-refs/analysis-template.md`. Full mode renders up to 23 main sections + footer, minus any dropped by the omission table. The analysis doc is a development handoff, so low-signal sections are default-drop even in Full: **19 Alternatives** (render only for a real 2+-option architectural decision), **22 Glossary** (render only on request or >5 unfamiliar domain terms), and **18 Rollout** (compact checklist only, dropped when the pipeline's own commit/PR/rollout phase covers it). Lite mode renders 7 main sections (1 Summary, 2 Goals + Non-Goals, 4 User Stories, 9 API Contracts, 13 Architecture, 14 Files to Add, 21 References) plus optional 23 Changelog. Section omission rule (Locked 2): zero-evidence and default-drop sections omit entirely; numbering re-flows to stay sequential `1..N` over the rendered set.
@@ -38,7 +51,7 @@ The template is platform-agnostic (Locked 22). It speaks in concepts (state hold
38
51
 
39
52
  ### Phase 0 - Intake
40
53
 
41
- Full picker chain: `$HOME/.claude/multi-agent-refs/analysis/intake.md`. Sequential `AskUserQuestion` steps filling `state.analysisSpec.*`: analysis name, account, platform multi-select, repos per platform, the six-question source batch (Figma / Swagger / Confluence / Jira / Standards / Firebase) and the two coverage opt-ins. Step narration is required - the chain length is known up front, so every step prints its breadcrumb.
54
+ Full picker chain: `$HOME/.claude/multi-agent-refs/analysis/intake.md`. Sequential `AskUserQuestion` steps filling `state.analysisSpec.*`: analysis name, analysis profile, account, platform multi-select, repos per platform, the six-question source batch (Figma / Swagger / Confluence / Jira / Standards / Firebase) and the two coverage opt-ins. Step narration is required - the chain length is known up front, so every step prints its breadcrumb.
42
55
 
43
56
  ### Phases 1, 1b, 1b.1, 1c - Evidence gathering
44
57
 
@@ -50,7 +63,7 @@ Full contract: `$HOME/.claude/multi-agent-refs/analysis/synthesis.md`. Pass A bu
50
63
 
51
64
  ### Phases 3, 3.5, 4, 5 - Render, publish, report
52
65
 
53
- Full contract: `$HOME/.claude/multi-agent-refs/analysis/render.md`. Renders one markdown file per platform, runs the **required** `ai-common-toolkit:humanizer` pass, gates on `validate-analysis-doc.mjs`, asks for the output destination, dispatches to Local / Confluence / Jira (a Jira issue receives a comment unless the user explicitly asks for the description, which is never overwritten without a backup and a confirmation), then reports and stops. The humanizer pass and the validator are required in every mode; a document that skipped either is not shippable.
66
+ Full contract: `$HOME/.claude/multi-agent-refs/analysis/render.md`. Renders one markdown file per platform, runs the **required** `ai-common-toolkit:humanizer` pass, gates on `validate-analysis-doc.mjs` and on the `build-references.mjs --check` references coverage gate (Locked 34), asks for the output destination, dispatches to Local / Confluence / Jira (a Jira issue receives a comment unless the user explicitly asks for the description, which is never overwritten without a backup and a confirmation), then reports and stops. The humanizer pass and the validator are required in every mode; a document that skipped either is not shippable.
54
67
 
55
68
  ### Resume contract
56
69
 
@@ -242,10 +242,8 @@ node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs"
242
242
  ```bash
243
243
  cd "$PLUGINS_REPO"
244
244
  if ! git diff --quiet; then
245
- # Identity resolves from prefs.global.identities[] routed by platformIdentityRouting,
246
- # exactly as Phase 6 does. Never hardcode a name, email or gh account here: this file
247
- # ships to every installation, so a literal identity attributes a downstream user's
248
- # plugins-repo commits to someone else and switches their gh account out from under them.
245
+ # Identity: routed for {owner} (platformIdentityRouting). Never a literal: this file
246
+ # installs everywhere and would misattribute a downstream user's commits.
249
247
  git config user.name "{identity.name}"
250
248
  git config user.email "{identity.email}"
251
249
  git add -A
@@ -436,7 +434,7 @@ With `release`, or in autopilot, run the full ship path without asking. If the w
436
434
 
437
435
  ## Website Sync (Step 4)
438
436
 
439
- Propagate pipeline version, phase count, model count, and feature descriptions to the website.
437
+ Propagate version, phase and model counts and feature descriptions to the website.
440
438
 
441
439
  ```bash
442
440
  gh auth switch --user {owner}
@@ -451,9 +449,10 @@ cd "$WEBSITE_DIR" && git pull origin main
451
449
  | `src/data/projects.ts` | Version number, tagline, description, feature list |
452
450
 
453
451
  ```bash
454
- cd "$WEBSITE_DIR"
455
- git add -A && git commit -m "chore: sync pipeline v{VERSION}"
456
- git push origin main # Vercel auto-deploy
452
+ # A commit the platform does not recognise is pushed fine and never built, so the site
453
+ # keeps the old version. {identity} is the one routed to {owner}, not the run's own.
454
+ # Why, signature, recovery: `$HOME/.claude/multi-agent-refs/website-deploy.md`.
455
+ bash "$HOME/.claude/scripts/website-deploy-commit.sh" "{identity.name}" "{identity.email}" "{VERSION}" "$WEBSITE_DIR"
457
456
  ```
458
457
 
459
458
  ---
@@ -470,7 +469,7 @@ When invoked with the `release` argument:
470
469
  5. Commit + TAG git commit + git tag v{VERSION}
471
470
  6. PUSH git push --tags -> release.yml auto-publish
472
471
  7. DEV-TOOLKIT Ship the companion MCP server if it moved (Step 3d gates, then publish)
473
- 8. WEBSITE Version + features -> {website-host}
472
+ 8. WEBSITE Version + features -> {website-host} (maintainer identity, build verified Ready)
474
473
  9. COPILOT Copilot CLI instructions + skills sync
475
474
  9b. CODEX Codex CLI router skill + refs + agent TOML (node install.js --codex)
476
475
  10. Report Summary: version, touched repos, deploy status
@@ -6,7 +6,7 @@
6
6
 
7
7
  Sequential `AskUserQuestion` chain. Each answer is written to state under `state.analysisSpec.*`.
8
8
 
9
- **Step narration (required, per `$HOME/.claude/multi-agent-refs/picker-contract.md`)**: the chain length is known up front - 1 analysis-name + 1 account + 1 platform + 1 repo-round per selected platform + 1 input-URL batch + 1 coverage-options batch (so a single-platform run is 6 steps; account is skipped for local-only flows, which lowers the total). Before each step's `AskUserQuestion`, print the narrator line `<localized: "Step <i>/<n>: <what this step decides>">` in `outputLanguage`. Auto-resolved steps (single account, local-only) still print their breadcrumb with the resolution noted. This is what makes the picker show, step by step, what it is doing.
9
+ **Step narration (required, per `$HOME/.claude/multi-agent-refs/picker-contract.md`)**: the chain length is known up front - 1 analysis-name + 1 profile + 1 account + 1 platform + 1 repo-round per selected platform + 1 input-URL batch + 1 coverage-options batch (so a single-platform run is 7 steps; account is skipped for local-only flows and the profile step auto-resolves when only one profile is available, either of which lowers the total). Before each step's `AskUserQuestion`, print the narrator line `<localized: "Step <i>/<n>: <what this step decides>">` in `outputLanguage`. Auto-resolved steps (single account, local-only) still print their breadcrumb with the resolution noted. This is what makes the picker show, step by step, what it is doing.
10
10
 
11
11
  #### Step 0 - Language resolution (BLOCKING, runs before any picker)
12
12
 
@@ -17,6 +17,29 @@ Before emitting the first `AskUserQuestion`, read `prefs.global.outputLanguage`
17
17
  If `$ARGUMENTS` is empty, ask via AskUserQuestion (single question, user types via Other).
18
18
  Result: `state.analysisSpec.featureName` (state key kept for backward compatibility; user-facing label is "analysis name").
19
19
 
20
+ #### Step 1b - Analysis profile
21
+
22
+ Asked once, immediately after the analysis name and before anything is fetched, because the profile decides which template the whole run renders against (Locked 32). Never re-asked mid-run.
23
+
24
+ Read the available profiles from `prefs.global.analysisProfiles` (default `["global", "corporate"]`). When only one is available, auto-resolve and print the breadcrumb with the resolution noted rather than asking a question whose answer is already settled.
25
+
26
+ AskUserQuestion (single-select):
27
+ ```
28
+ header: "Profile"
29
+ question: <localized: "Which analysis standard should this document follow?">
30
+ options:
31
+ - label: "Global"
32
+ description: <localized: "Development handoff, 23 sections. Business rules with Gherkin acceptance criteria, architecture plan, files to add, test plan. Sections with no evidence are dropped.">
33
+ - label: "Corporate"
34
+ description: <localized: "Requirements document. Business requirement to use case to functional requirement spine with three traceability matrices, current and target state with impact analysis, then technical analysis and development analysis. Backbone sections always render.">
35
+ ```
36
+
37
+ Result: `state.analysisSpec.profile` (`global` | `corporate`). Empty submit re-asks; an empty answer does not imply the default (`feedback_no-inferred-defaults-from-empty-answer`).
38
+
39
+ The profile changes nothing about intake, fetching, repo evidence or convention extraction - those are shared. It selects the template at Phase 3 and switches the omission rule for the corporate backbone (Locked 33).
40
+
41
+ **Corporate profile bindings.** Publication targets and house terminology are read from `prefs.global.analysisProfile.corporate` when present: `confluenceSpaceKey`, `confluenceParentPageId`, `titleFormat`, `titlePrefix`, `apiSpecCommand` and a `glossary` map. The key set is closed in `prefs.schema.json`, so a typo is caught by `validate-prefs.mjs` rather than silently ignored at emit time. They are deployment configuration, not part of the shipped template: an unconfigured corporate run still renders the full document and asks for the destination at Phase 3.5 like any other run.
42
+
20
43
  #### Step 2 - Account picker
21
44
 
22
45
  Reuse `_account-picker.md`. Skipped if the resolved flow is local-only.
@@ -38,9 +61,13 @@ options:
38
61
  - label: "Android"
39
62
  - label: "Backend"
40
63
  - label: "Frontend"
64
+ - label: "No platform yet"
65
+ description: <localized: "Analysis and technical analysis only. The development analysis is skipped until a repo is chosen.">
41
66
  ```
42
67
  Empty submit → re-ask. Result: `state.analysisSpec.platforms[]`.
43
68
 
69
+ **`No platform yet` is a real answer, not a cancel** (Locked 35). It leaves `platforms[]` empty, skips Step 4 entirely, and the run continues: evidence is still fetched from every declared source, and the document renders every layer that does not need a target repository. Only the development layer and the Pass B projection drop, and Section 20 records that they await a repo selection. Output is a single `analysis/<feature>.md` rather than one file per platform, since the per-platform split exists to carry per-platform projections and there are none.
70
+
44
71
  **Platform coverage = provided platforms.** The analysis renders exactly one per-platform file (Locked 9) for each platform selected here and given a repo in Step 4: select iOS only -> a single iOS document; select iOS + Android -> one iOS and one Android document, each projected through that repo's own conventions (Phase 1c) and its own Code Connect index (Phase 1b.1, discovered from that repo's `*.figma.swift` / `*.figma.kt`). Do not analyze a platform the user did not select, and do not drop a selected platform that has a repo.
45
72
 
46
73
  #### Step 4 - Repos (from Phase 0, not re-asked)
@@ -49,6 +76,8 @@ Repos come from the orchestrator's Step 2 project selection plus `_dev-context.m
49
76
 
50
77
  Map `state.analysisSpec.repos[]` from the Phase 0 selection: one entry per repo with `platform` (its stack tag), `name`, `path`, `canPush`. If a selected platform has no repo, ask for that one platform only.
51
78
 
79
+ When `platforms[]` is empty (Step 3 resolved to `No platform yet`), skip Steps 4 and 4 (fallback) entirely and print the breadcrumb noting that repo selection is deferred.
80
+
52
81
  #### Step 4 (fallback) - Repo multi-select per platform
53
82
 
54
83
  For each selected platform, run one AskUserQuestion round. Reuse `_dev-context.md` logic:
@@ -1,16 +1,16 @@
1
- # Locked decisions (31)
1
+ # Locked decisions (35)
2
2
 
3
- > The 31 Locked decisions of the analysis flow. Loaded by `/multi-agent:analysis`, by `/multi-agent:analysis-resolve` (which inherits them) and by pipeline Phase 1 when it runs the analysis engine. Numbering is canonical: cite as `Locked <n> (<short label>)`.
3
+ > The 35 Locked decisions of the analysis flow. Loaded by `/multi-agent:analysis`, by `/multi-agent:analysis-resolve` (which inherits them) and by pipeline Phase 1 when it runs the analysis engine. Numbering is canonical: cite as `Locked <n> (<short label>)`.
4
4
 
5
5
  ### Index by category (v9.1.0+)
6
6
 
7
- Browse-friendly grouping of the 31 Locked decisions. Numbering stays canonical (matches the list below); the index is read-only navigation.
7
+ Browse-friendly grouping of the 35 Locked decisions. Numbering stays canonical (matches the list below); the index is read-only navigation.
8
8
 
9
9
  | Category | Decisions | Concern |
10
10
  |---|---|---|
11
- | **A. Governance** | 1, 5, 6, 7, 10, 26, 27 | Run-level process rules: one feature per run, default output, auto-commit ban, punctuation policy, output picker timing, Pass B preview, evidence digest cache |
12
- | **B. Citation and Evidence** | 3, 4, 8, 11, 24, 30 | Every fact in the doc traces back to a source: citation discipline, forward-looking spec, standards binding, repo-evidence reuse-first, Pass B footnote mandatory, analysis self-contained (pipeline-wide) |
13
- | **C. Output Format and Structure** | 2, 9, 13, 14, 16, 17, 20, 21, 25 | How the document is laid out: section omission rule, per-platform output split, Gherkin user stories, Goals + Non-Goals paired, Files-to-Add tag, API response variants exhaustive, localization mode (ownership-aware), References at the bottom, Lite mode |
11
+ | **A. Governance** | 1, 5, 6, 7, 10, 26, 27, 32 | Run-level process rules: one feature per run, default output, auto-commit ban, punctuation policy, output picker timing, Pass B preview, evidence digest cache, analysis profile |
12
+ | **B. Citation and Evidence** | 3, 4, 8, 11, 24, 30, 34 | Every fact in the doc traces back to a source: citation discipline, forward-looking spec, standards binding, repo-evidence reuse-first, Pass B footnote mandatory, analysis self-contained (pipeline-wide), references built from the evidence record |
13
+ | **C. Output Format and Structure** | 2, 9, 13, 14, 16, 17, 20, 21, 25, 33, 35 | How the document is laid out: section omission rule, per-platform output split, Gherkin user stories, Goals + Non-Goals paired, Files-to-Add tag, API response variants exhaustive, localization mode (ownership-aware), References at the bottom, Lite mode, corporate backbone always renders, stack-optional render |
14
14
  | **D. Design Source and Pipeline Architecture** | 12, 22, 23 | Where design comes from and how the pipeline renders: Figma 3-tier access (BLOCKING), platform-agnostic template + Pass B render, convention extraction (Phase 1c) |
15
15
  | **E. UI, Variant, and Test Coverage** | 15, 18, 19, 28, 29, 31 | UI artefact rules: SVG default for new assets, screenshots embedded, all Figma variants drilled, SwiftUI Preview block (iOS), variant usage explicit, business-rule to acceptance-criterion to test traceability |
16
16
 
@@ -51,3 +51,8 @@ When citing a Locked decision in code or docs, prefer `Locked <n> (<short label>
51
51
  29. **Variant usage explicit and bounded.** Section 6 inventory rows list which variants this feature consumes per component (concrete enum case + bool value). New Section 6.X (Variant Usage Matrix) catalogues the full variant axis vs. used subset with a rationale per excluded variant. Sections 13.6 (Preview) and 15.2 (Snapshot) cover only the used subset; expanding the variant set requires updating Section 6.X first.
52
52
  30. **Analysis as self-contained design bridge - no MCP outside analysis phase (BLOCKING, pipeline-wide).** The analysis document is the sole design source for every downstream phase. After Phase 1 of `/multi-agent:analysis` produces `analysis/<feature>-<platform>.md`, Phase 2 Planning, Phase 3 Dev, Phase 4 Review, Phase 5 Test, Phase 6 Commit, and Phase 7 Report consume only the analysis document plus repo Code Connect mappings (`*.figma.swift` / `*.figma.kt`). Calling `mcp__claude_ai_Figma__*`, hitting `api.figma.com`, or fetching a `figma.com/design/...` URL during Phase 2+ is a violation. Applies to every mode that runs Phase 2+: `/multi-agent`, `/multi-agent:autopilot`, `/multi-agent:local`, `/multi-agent:local-autopilot`, at either depth. Hard requirement (v9.0.0): Phase 2 Pre-item and Phase 3 Pre-item (BLOCKING) abort the run when the analysis document is missing. Memory: `[[mcp-only-in-analysis]]`. Generic rule rationale and access matrix: see `$HOME/.claude/rules/figma-pipeline.md` "MUST: No MCP outside analysis phase".
53
53
  31. **Business-rule to acceptance-criterion to test traceability (AI + human spine).** The analysis is a development handoff that both an AI implementer and a human reviewer must act on, so it is bound by one shared-ID vocabulary. Every business rule carries a stable id `BR-<slug>-NN` (Section 4.4). Each rule maps to at least one acceptance criterion written Given / When / Then (binary - two readers must not be able to disagree on pass/fail). Each acceptance criterion maps to unit-test scenarios in Section 15.1, one row per case across happy / boundary / error / empty-nil (enumerate at least the failure modes; agents hallucinate error handling when it is omitted). The same ids thread onward: Section 15.6 UI-test flows reference the `BR-` ids and use stable selectors (accessibilityIdentifier / testTag), Section 16 accessibility items reuse those identifiers, Section 11 analytics events cite their triggering rule or story, and Section 5/7 layout cells carry token + Figma node refs. Never invent copy or values (blank beats a guess; a missing source becomes a Section 20 Open Question). **Mode-aware gate:** in Full mode a business rule with no acceptance criterion, or an acceptance criterion with no Section 15.1 scenario, fails the dispatch gate. In **Lite mode Section 15 is not rendered**, so the rule-to-test half does not apply - Section 4.4 still lists each rule with its Given/When/Then acceptance criterion (the acceptance criterion is itself the testable statement), and the 15.1 mapping is deferred to whenever the feature is later analyzed in Full or implemented by a dev run. The rule-to-acceptance-criterion half always holds, in both modes.
54
+
55
+ 32. **Analysis profile selected at intake.** `state.analysisSpec.profile` is `global` (default) or `corporate`, asked once at Phase 0 Step 1b and never re-asked mid-run. `global` renders `$HOME/.claude/multi-agent-refs/analysis-template.md` (23 sections, development handoff). `corporate` renders `$HOME/.claude/multi-agent-refs/analysis-template-corporate.md` (requirements document: `IG -> UC -> FG` spine, three traceability matrices, current-to-target state with impact analysis, then Technical Analysis and Development Analysis). **Both profiles read the same `state.analysisSpec.evidence.*`** - intake, fetching, repo evidence and convention extraction are shared and profile-independent; only the projection differs. This is what keeps the two templates from drifting into two products. One run emits one profile: rendering both from a single run would produce two documents describing the same feature, and the next reader would have to decide which one is current. When only one profile is available (`prefs.global.analysisProfiles` lists one, or the corporate profile has no binding configuration), the step auto-resolves and prints its breadcrumb with the resolution noted, per the picker contract.
56
+ 33. **Corporate backbone always renders.** In the `corporate` profile the Locked 2 omission rule is replaced for Part A and the footer: those sections render even with zero evidence, carrying `N/A` when the section is genuinely out of scope for the feature and `EKLENECEK` when evidence is expected but missing. This is the point of a requirements document - a reader has to be able to tell "we considered hardware needs and there are none" from "nobody looked". Every `EKLENECEK` emits a matching Section 20 Risks and Open Questions row naming what is missing and who can answer it; an `EKLENECEK` with no such row fails the dispatch gate, because an unanswered question nobody owns is how a placeholder reaches production. **Missing inputs never block the run**: the corporate source practice of halting until every input arrives is deliberately not adopted - the document is produced with `EKLENECEK` in the gaps and the gaps are raised in Section 20. Part B follows the global omission table unchanged. In the `global` profile Locked 2 applies as written, with no placeholder of any kind.
57
+ 34. **References are built from the evidence record, not written.** Section 21 is emitted by `$HOME/.claude/scripts/build-references.mjs` from `state.analysisSpec.evidence.*` in both profiles. Each row carries a precision anchor in its `Sürüm / Ref` column - Figma node id, Confluence `pageId` plus page version, the commit SHA a repo was read at, the Swagger spec version - because a reference with no anchor points at a moving target. Each row carries an `Erişim / Access` cell: a declared source that could not be fetched still gets a row reading `erişilemedi (<reason>)`, since a silently dropped source reads to the next person as a source that never existed. User statements from the conversation that no fetched source contains are recorded as `Serbest metin` rows, quoted verbatim, with the decision they settled. **Coverage gate**: every entry in `evidence.figma[]`, `confluence[]`, `jira[]`, `swagger[]`, `repo[]`, `standards[]`, `firebase[]`, `documents[]`, `outside[]`, `freeText[]` and every entry in `evidence.fetchErrors[]` must appear as a row, and every row must map to an evidence entry. A source that shaped the document but is missing from References fails the dispatch gate; so does an invented row with no evidence behind it.
58
+ 35. **Stack-optional render.** Platform and repo selection are optional. When `state.analysisSpec.platforms[]` is empty, the run still completes: the analysis layers that do not need a target repository render in full - Part A and Part B in the corporate profile, Sections 1-12 and 16-17 in the global profile - and only the development layer is dropped (corporate Part C; global Sections 13, 14, 15) along with the Pass B projection, since there are no conventions to project onto. A Section 20 row records that the development analysis awaits a repo selection. One document is emitted rather than one per platform, because the per-platform split (Locked 9) exists to carry per-platform projections and there are none. It lands at `~/Desktop/multiAgentAnalysis/<feature-name>/<feature>.md`: the repo-relative `analysis/` path has nothing to be relative to without a repo, and the current working directory is never used, since for a repo-less run it is arbitrary. Desktop rather than a hidden directory because the document is a deliverable somebody is meant to open and hand over, and `multiAgentAnalysis` rather than a bare `Analysis` because a generic word collides with whatever else is on a desktop while the producer name groups every run this command ever writes. The Phase 3.5 picker shows the resolved path and takes an override through its Other input. A requirements document is useful before anyone has decided which repository will hold the code, and refusing to produce one until that decision exists inverts the order the work actually happens in.
@@ -6,7 +6,11 @@
6
6
 
7
7
  1. **Language resolution**: read `prefs.global.outputLanguage` (`tr` or `en`, default `tr`). Use the `Output language matrix` table in `$HOME/.claude/multi-agent-refs/analysis-template.md` to swap headings and system strings.
8
8
 
9
- 2. **Per-platform markdown render**: for each platform in `state.analysisSpec.platforms[]`, concatenate the per-platform spec into one markdown file. Tables in pipe-syntax. Numbering uses plain `## 1.`, `## 2.`, ... - omitted sections do **not** create gaps. Visible numbering is sequential 1..N over the rendered set.
9
+ 1b. **Template selection** (Locked 32): read `state.analysisSpec.profile`. `global` renders against `$HOME/.claude/multi-agent-refs/analysis-template.md`; `corporate` renders against `$HOME/.claude/multi-agent-refs/analysis-template-corporate.md`. The evidence is the same either way - this step chooses the projection, nothing else. In the corporate profile the Part A backbone and the footer render even with zero evidence, carrying `N/A` or `EKLENECEK`, and each `EKLENECEK` emits its Section 20 row (Locked 33).
10
+
11
+ 2. **Markdown render**: for each platform in `state.analysisSpec.platforms[]`, concatenate the per-platform spec into one markdown file. Tables in pipe-syntax. Numbering uses plain `## 1.`, `## 2.`, ... - omitted sections do **not** create gaps. Visible numbering is sequential 1..N over the rendered set. In the corporate profile the Part A and footer numbers are fixed and do not re-flow, since the backbone never drops; Part B and Part C follow the omission table as usual.
12
+
13
+ **When `platforms[]` is empty** (Locked 35), render one platform-agnostic file instead of one per platform: the development layer (corporate Part C, global Sections 13, 14, 15) and the Pass B projection are skipped, Section 20 carries a row recording that they await a repo selection, and the front-matter `platform` key reads `none`. Everything that does not need a target repository still renders in full.
10
14
 
11
15
  3. **Humanizer pass (MANDATORY: actually invoke the `ai-common-toolkit:humanizer` skill on the rendered markdown - the punctuation grep alone does NOT satisfy this step)** (`technical-explanatory` tone for the scratch buffer; per-channel re-humanize happens in Phase 4 when actually emitting):
12
16
  ```
@@ -19,7 +23,15 @@
19
23
 
20
24
  **Explicit punctuation policy** (enforced by `stripFancyPunctuation: true`): no em-dash (U+2014), no en-dash (U+2013), no horizontal ellipsis (U+2026), no curly quotes (U+2018, U+2019, U+201C, U+201D), no section sign (U+00A7). The humanizer replaces these with ASCII equivalents (`-`, `:`, `,`, `...`, `'`, `"`, and `bölüm` / `section` for the section sign per `outputLanguage`) before emit. Tables, code blocks, URLs, and front-matter YAML are exempt. Post-emit verification runs through `node $HOME/.claude/scripts/validate-analysis-doc.mjs <file>`, which checks this policy deterministically in Node on all three operating systems. Do NOT verify with `grep -P`: BSD grep (macOS, the pipeline's primary platform) has no `-P`, so that command never runs and "zero matches" is trivially true. The per-language readability rules (Turkish sentence shape, nominalization chains, which terms stay untranslated) live in the humanizer skill itself, not here; this paragraph owns only the punctuation policy the validator enforces. **Diacritics are PRESERVED, not stripped: this policy targets ONLY the listed fancy-punctuation codepoints. Turkish letters (ş/Ş, ç/Ç, ğ/Ğ, ı/I, İ, ö/Ö, ü/Ü) and all other `outputLanguage` letters MUST stay verbatim. Never ASCII-fold the prose - emit `Geliştirme Özeti`, `için`, `Kullanıcı Hikayeleri`, NOT `Gelistirme Ozeti`, `icin`, `Kullanici`. ASCII-folded Turkish is a humanizer-skipped smell and fails review.**
21
25
 
22
- 4. **Write scratch drafts**: create `/tmp/analysis-<feature-slug>-<UTC-iso8601>/` and write `<feature>-<platform>.md` for each selected platform. Update `state.analysisSpec.outputs.draftDir` with the path.
26
+ 3b. **Build Section 21 References** (Locked 34): emit the table with
27
+
28
+ ```bash
29
+ node "$HOME/.claude/scripts/build-references.mjs" <state.json> --lang "$OUTPUT_LANGUAGE"
30
+ ```
31
+
32
+ and paste its output under the References heading. The model does not hand-write this table; it is a projection of `state.analysisSpec.evidence.*`, which is what keeps a source the run actually read from going unlisted and a remembered-but-unread source from appearing.
33
+
34
+ 4. **Write scratch drafts**: create `/tmp/analysis-<feature-slug>-<UTC-iso8601>/` and write `<feature>-<platform>.md` for each selected platform, or a single `<feature>.md` when `platforms[]` is empty. Update `state.analysisSpec.outputs.draftDir` with the path.
23
35
 
24
36
  5. **Surface the draft tree to the user**:
25
37
  ```
@@ -39,7 +51,7 @@ header: "Output"
39
51
  question: <localized: "Where should the per-platform analyses be written?">
40
52
  options:
41
53
  - label: "Local file"
42
- description: "analysis/<feature>-<platform>.md in each selected repo's working tree"
54
+ description: <resolved path, shown literally: "<repo>/analysis/<feature>-<platform>.md" per selected repo, or "~/Desktop/multiAgentAnalysis/<feature-name>/<feature>.md" when no repo was selected>
43
55
  - label: "Confluence page"
44
56
  - label: "Jira issue"
45
57
  description: <localized: "As a comment by default; writing the description is a separate, explicit choice">
@@ -74,16 +86,17 @@ Result: `state.analysisSpec.outputs.requested[]`.
74
86
  ```bash
75
87
  for f in /tmp/analysis-<feature-slug>-<ts>/*.md; do
76
88
  node "$HOME/.claude/scripts/validate-analysis-doc.mjs" "$f" || GATE_FAILED=1
89
+ node "$HOME/.claude/scripts/build-references.mjs" <state.json> --check "$f" || GATE_FAILED=1
77
90
  done
78
91
  ```
79
92
 
80
- `validate-analysis-doc.mjs` enforces the mechanically-checkable Locked decisions on the emitted markdown itself (front-matter completeness, never-omitted sections per Locked 2, humanizer punctuation per Locked 7, and Full-mode business-rule traceability per Locked 31). Any ERROR blocks dispatch: fix the draft and re-validate. Warnings are advisory (run with `--strict` to treat them as blocking). This turns the "fails the dispatch gate" prose into a real, model-independent check.
93
+ `validate-analysis-doc.mjs` enforces the mechanically-checkable Locked decisions on the emitted markdown itself (front-matter completeness, never-omitted sections per Locked 2, humanizer punctuation per Locked 7, Full-mode business-rule traceability per Locked 31, and in the corporate profile the backbone presence and `EKLENECEK`-to-Section-20 pairing per Locked 33). `build-references.mjs --check` runs the References coverage gate (Locked 34): a source the run consumed but did not list, or a listed row with no evidence behind it, blocks dispatch. Any ERROR blocks dispatch: fix the draft and re-validate. Warnings are advisory (run with `--strict` to treat them as blocking). This turns the "fails the dispatch gate" prose into a real, model-independent check.
81
94
 
82
95
  Iterate `state.analysisSpec.outputs.requested`. For each target:
83
96
 
84
97
  | Target | Action |
85
98
  |--------|--------|
86
- | Local | For each per-platform draft, `cp /tmp/analysis-<feature-slug>-<ts>/<feature>-<platform>.md` into `analysis/<feature>-<platform>.md` in the matching repo's working tree. When multiple repos exist for the same platform, the file is duplicated into each and the dispatch report lists every destination. **No commit.** |
99
+ | Local | For each per-platform draft, `cp /tmp/analysis-<feature-slug>-<ts>/<feature>-<platform>.md` into `analysis/<feature>-<platform>.md` in the matching repo's working tree. When multiple repos exist for the same platform, the file is duplicated into each and the dispatch report lists every destination. **When no repo was selected** (Locked 35) there is no working tree to be relative to, so the file lands in `~/Desktop/multiAgentAnalysis/<feature-name>/<feature>.md`. The current working directory is never written to: for a repo-less run it is arbitrary, and creating a folder in whatever directory the command happened to be invoked from is the kind of surprise that costs a tool its trust. The user can override the path through the picker's Other input. **No commit.** |
87
100
  | Confluence | Re-humanize each per-platform draft with `formal-stakeholder` tone. One Confluence page per platform under the chosen parent, titled `<Feature> - <Platform>`. Cross-link siblings inside each page via `<ac:link><ri:page ri:content-title="<Feature> - <OtherPlatform>"/></ac:link>`. Markdown -> storage XML via `$HOME/.claude/multi-agent-refs/channels/confluence.md`. Re-emit on existing pages uses PUT with version bump. |
88
101
  | Jira | Re-humanize the combined body with `informal-technical` tone. Concatenate per-platform drafts under `h2. Platform: iOS`, `h2. Platform: Android`, `h2. Platform: Backend`, `h2. Platform: Frontend` separators (in the order platforms were selected), then run the whole body through the markdown → Jira wiki conversion table in `$HOME/.claude/multi-agent-refs/channels/jira.md` - both the comment body and the `description` field render wiki markup, so raw `##`/`**`/backticks arrive as literal text. Write the converted body to a file and publish it with `$HOME/.claude/lib/jira-publish.sh`, never with a hand-rolled `curl`: <br><br>`bash "$HOME/.claude/lib/jira-publish.sh" --issue "$KEY" --body-file "$F" --target comment` <br>`bash "$HOME/.claude/lib/jira-publish.sh" --issue "$KEY" --body-file "$F" --target description --mode append` <br><br>The script owns the parts that are easy to get wrong: it runs `jira-wiki-escape.mjs` on the body, resolves host + token without putting either in argv, and on the description path it GETs the current value, writes it to a backup under `~/.claude/logs/multi-agent/jira-backups/` and reports the path, appends below a `----` rule by default, and **refuses with exit 3** when `--mode replace` would discard a non-empty description unless `--confirm-overwrite` is passed. Exit 3 is reported to the user with the backup path, never retried with the flag added automatically - only the user's explicit "Description - replace" answer from Phase 3.5 supplies it. `--dry-run` previews the exact final body without writing. |
89
102
 
@@ -142,6 +155,8 @@ Per the `analysis-output-confluence-on-request` memory, Confluence post is NEVER
142
155
 
143
156
  1. Resolve the token via `~/.claude/lib/credential-store.sh get <key>`, where `<key>` is read from `prefs.global.keychainMapping.confluence` (per-user mapping; never hardcode the service name in this doc - see channel adapter doc `$HOME/.claude/multi-agent-refs/channels/confluence.md` for the lookup contract).
144
157
  2. Use parent page URL from user input. No default parent is hardcoded here; the user picks one at the prompt (LRU recents come from `prefs.projects[<project>].confluenceUrls`).
158
+
159
+ **Corporate profile exception.** When `state.analysisSpec.profile == "corporate"` and the bindings are configured, the destination is already settled and the prompt is skipped: the space comes from `prefs.global.analysisProfile.corporate.confluenceSpaceKey`, the parent from `prefs.global.analysisProfile.corporate.confluenceParentPageId`, and the page title is built from `prefs.global.analysisProfile.corporate.titleFormat` with `prefs.global.analysisProfile.corporate.titlePrefix` filling its `{prefix}` placeholder. A corporate analysis always lands in the same tree, so asking each time is a question whose answer never changes. Any of the four missing falls back to the prompt above rather than guessing, and a title that would collide with an existing page becomes an update (PUT with version bump), never a second page.
145
160
  3. Convert markdown to storage XML using the table in `$HOME/.claude/multi-agent-refs/channels/confluence.md`.
146
161
  4. Upload Figma frame screenshots as page attachments via `POST /rest/api/content/{pageId}/child/attachment` (cache the MCP asset locally first because the upstream URLs expire after 7 days).
147
162
  5. Reference attachments inside the page body via `<ac:image><ri:attachment ri:filename="frame-<nodeId>.png"/></ac:image>`.
@@ -88,7 +88,7 @@ For each `platform` in `state.analysisSpec.platforms[]`:
88
88
  - `frontend` → `evidence.standards[]` entries matching `react`, `vue`, `next`, `sveltekit` → `~/.claude/rules/code-style.md`
89
89
  2. **Apply per-platform omission rules.** Backend-only file drops Sections 5, 6, 7, 8, 16. Frontend with no UI inventory still keeps 5 (UI exists in code). Sections 1, 2, 4, 9, 13, 14, 20, 21 always present per Locked decision 2 + 13.
90
90
  3. **Resolve mode.** If user passed `--lite` → Lite. If user passed `--full` → Full. Otherwise use `state.analysisSpec.liteModeAuto`. Lite mode renders only Sections 1, 2, 4, 9, 13, 14, 21 plus optional 23.
91
- 4. **Produce YAML front-matter header** (see `$HOME/.claude/multi-agent-refs/analysis-template.md`). Include `mode: full | lite`, plus `ui_tests: <state.analysisSpec.options.uiTests | false>` and `a11y_depth: <state.analysisSpec.options.a11yDepth | basic>` so the pre-dispatch validator can enforce the opt-in coverage (15.6 present when ui_tests, 16.2 walkthrough present when a11y_depth is full).
91
+ 4. **Produce YAML front-matter header** (see `$HOME/.claude/multi-agent-refs/analysis-template.md`). Include `profile: <state.analysisSpec.profile | global>` and `platform: <platform | none>` so the validator applies the right contract per profile (Locked 32) and recognises the stack-optional render (Locked 35), `mode: full | lite`, plus `ui_tests: <state.analysisSpec.options.uiTests | false>` and `a11y_depth: <state.analysisSpec.options.a11yDepth | basic>` so the pre-dispatch validator can enforce the opt-in coverage (15.6 present when ui_tests, 16.2 walkthrough present when a11y_depth is full).
92
92
  5. **Read conventions for this platform's repo.** For each cell Pass B fills in Section 13 and in any per-platform projection (Sections 5, 6, 7, 8, 10, 11, 13, 14, 15, 16, 17), read `state.analysisSpec.evidence.conventions[<repo>].<field>` and emit the value with a footnote (Locked 24). If `conventionOverrides` has an entry for that field, use the override and footnote with `^[user-override: <reason>]` instead of evidence path.
93
93
  6. **Concatenate non-null sections in canonical order.** Numbering stays sequential `1..N` over the rendered set (omitted sections do not create gaps).
94
94
  7. **Schema validation** on the per-platform spec object: