@mmerterden/multi-agent-pipeline 16.27.0 → 16.29.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +144 -1
  2. package/README.md +4 -4
  3. package/README.tr.md +3 -3
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/install/claude.mjs +17 -0
  7. package/package.json +4 -4
  8. package/pipeline/commands/multi-agent/SKILL.md +1 -1
  9. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +93 -0
  10. package/pipeline/commands/multi-agent/doctor/SKILL.md +78 -0
  11. package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
  12. package/pipeline/commands/multi-agent/issue/SKILL.md +1 -0
  13. package/pipeline/commands/multi-agent/jira/SKILL.md +3 -0
  14. package/pipeline/commands/multi-agent/log/SKILL.md +7 -1
  15. package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -0
  16. package/pipeline/commands/multi-agent/setup/SKILL.md +14 -1
  17. package/pipeline/commands/multi-agent/sync/SKILL.md +12 -9
  18. package/pipeline/commands/multi-agent/update/SKILL.md +12 -0
  19. package/pipeline/lib/_jira-auth.sh +99 -0
  20. package/pipeline/lib/analysis-jira-write.sh +203 -0
  21. package/pipeline/lib/issue-fetcher.sh +138 -9
  22. package/pipeline/lib/multi-repo-pipeline.sh +8 -0
  23. package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
  24. package/pipeline/multi-agent-refs/analysis/intake.md +11 -2
  25. package/pipeline/multi-agent-refs/analysis/locked.md +1 -0
  26. package/pipeline/multi-agent-refs/analysis/redesign.md +112 -0
  27. package/pipeline/multi-agent-refs/analysis/render.md +6 -1
  28. package/pipeline/multi-agent-refs/analysis/resolve.md +1 -0
  29. package/pipeline/multi-agent-refs/analysis/review.md +15 -0
  30. package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
  31. package/pipeline/multi-agent-refs/analysis-template-corporate.md +3 -3
  32. package/pipeline/multi-agent-refs/analysis-template.md +36 -0
  33. package/pipeline/multi-agent-refs/cross-cli-contract.md +6 -3
  34. package/pipeline/multi-agent-refs/features/analysis-jira.md +128 -0
  35. package/pipeline/multi-agent-refs/features/doctor.md +197 -0
  36. package/pipeline/multi-agent-refs/features/jira-context.md +101 -0
  37. package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
  38. package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -7
  39. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +1 -1
  40. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
  41. package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
  42. package/pipeline/multi-agent-refs/picker-contract.md +35 -0
  43. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  44. package/pipeline/multi-agent-refs/tracker-contract.md +5 -1
  45. package/pipeline/preferences-template.json +1 -1
  46. package/pipeline/schemas/agent-state.schema.json +24 -0
  47. package/pipeline/schemas/analysis-spec.schema.json +336 -95
  48. package/pipeline/schemas/prefs.schema.json +86 -2
  49. package/pipeline/scripts/analysis-story-tree.mjs +441 -0
  50. package/pipeline/scripts/anonymize-findings.mjs +24 -0
  51. package/pipeline/scripts/build-references.mjs +10 -6
  52. package/pipeline/scripts/council-view.mjs +144 -0
  53. package/pipeline/scripts/doctor.mjs +758 -0
  54. package/pipeline/scripts/phase-tracker.sh +100 -3
  55. package/pipeline/scripts/scan-agent-config.sh +48 -10
  56. package/pipeline/scripts/skill-siblings.mjs +42 -9
  57. package/pipeline/scripts/validate-analysis-doc.mjs +371 -7
  58. package/pipeline/scripts/validate-analysis.mjs +7 -5
  59. package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +94 -0
  60. package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +79 -0
  61. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -0
  62. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -0
  63. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +13 -0
  64. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +9 -6
  65. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +18 -0
@@ -202,7 +202,7 @@ Phase 2 Section 20 Risks reads this list and emits one open question per entry.
202
202
 
203
203
  **Fallback source**: when `confidence == "none"` AND `evidence.standards[]` does not contain an explicit rule for that field, the renderer reads `$HOME/.claude/multi-agent-refs/conventions-defaults.md` and applies the platform default.
204
204
 
205
- **Caching (Locked 27)**: compute `evidence_digest = sha256(featureName || sorted(platforms) || hash(evidence.repoEvidence) || hash(evidence.conventions))`. Cache key on disk: `/tmp/multi-agent-analysis-cache/<digest>.json` with mtime <= 24h. Cache hit skips Phase 1b and Phase 1c. `--no-cache` flag forces re-run.
205
+ **Caching (Locked 27)**: compute `evidence_digest = sha256(featureName || sorted(platforms) || options.redesign || hash(evidence.repoEvidence) || hash(evidence.conventions))`. `options.redesign` is a digest input: without it a redesign within a day of a normal run on the same feature reuses that cache, skips Phase 1b, and renders an empty current-behaviour table every redesign check then passes over. Cache key on disk: `/tmp/multi-agent-analysis-cache/<digest>.json` with mtime <= 24h. Cache hit skips Phase 1b and Phase 1c. `--no-cache` flag forces re-run.
206
206
 
207
207
  Phase 1d is deliberately absent from the digest inputs. Community signal changes by the hour, so folding it in would produce a new digest on every run, invalidate the cache every time, and re-run the two expensive repo phases the cache exists to skip. The consequence is worth stating plainly: a cache hit reuses yesterday's signal rows. That is the correct trade for an advisory tier, and `--no-cache` is the way to refresh them.
208
208
 
@@ -239,9 +239,9 @@ Firebase question - Auto-detect mode probe order (option 2):
239
239
  2. Generated `AnalyticsEvents/*.swift` / `AnalyticsEvents/*.kt` if present (treat each public struct conforming to `AnalyticsEvent` as one event)
240
240
  3. Repo-level `firebase-events.json` / `analytics/events.json` files
241
241
 
242
- #### Step 5a - Coverage options (opt-in, 2 questions)
242
+ #### Step 5a - Coverage options (opt-in, 3 questions)
243
243
 
244
- One `AskUserQuestion` call with 2 parallel yes/no questions - or 1 when
244
+ One `AskUserQuestion` call with 3 parallel questions - or 2 when
245
245
  `state.analysisSpec.platforms[]` is empty, since `uiTests` only gates a section the
246
246
  development layer would have carried (see the gating table in Step 5). These are opt-INs, not source intake: an empty answer is NOT consent (per `feedback_no-inferred-defaults-from-empty-answer`) - re-ask on empty rather than defaulting silently once the picker is shown.
247
247
 
@@ -257,12 +257,21 @@ Q2: header="A11y depth"
257
257
  options:
258
258
  - label: "Basic checklist" -> state.analysisSpec.options.a11yDepth = "basic" (default)
259
259
  - label: "Full walkthrough" -> state.analysisSpec.options.a11yDepth = "full"
260
+
261
+ Q3: header="Redesign"
262
+ question: <localized: "Does this replace something that already exists? A redesign also records what v1 does today and where each behaviour goes.">
263
+ options:
264
+ - label: "New work" -> state.analysisSpec.options.redesign = false (default)
265
+ - label: "Redesign" -> state.analysisSpec.options.redesign = true
260
266
  ```
261
267
 
262
268
  `options.uiTests` gates Section 15.6 (UI test flows), which lives in the development
263
269
  layer; `options.a11yDepth` gates the Section 16 VoiceOver / TalkBack walkthrough, which
264
270
  does not. Both default to the lighter choice so the doc stays lean unless the user opts in.
265
271
 
272
+ `options.redesign` gates Sections 4.5, 4.6 and 9.5 and loads `analysis/redesign.md`, read
273
+ on no other run (Locked 37).
274
+
266
275
  #### Step 5b - Repo-evidence collector (automatic, no prompt)
267
276
 
268
277
  Runs after Step 5a submits and before Phase 1 begins. Reads from `state.analysisSpec.repos[]`. For each repo, walks the platform whitelist and produces a 13-bucket evidence catalogue. No user interaction. Output: `state.analysisSpec.evidence.repoEvidence[<repo>]`. See Phase 1b for the bucket list and tagging rules.
@@ -57,3 +57,4 @@ When citing a Locked decision in code or docs, prefer `Locked <n> (<short label>
57
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
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. **The channel split survives the missing repo.** Channels are derived from the evidence instead of repo stack tags (`intake.md` Step 3 carries the signal table) and one document is emitted per derived channel - `mobile`, `web`, or both. A phone screen and a browser screen carry different requirements before anyone has picked a repository; the split (Locked 9) exists to carry that difference and only its *projection* half needs conventions. `mobile` stays one channel rather than iOS plus Android, since without conventions nothing tells the two apart. Evidence with no interface at all yields a single channel-agnostic `<feature>.md`. Files land under `~/Desktop/multiAgentAnalysis/<feature-name>/`, named `<feature>-<channel>.md` (or `<feature>.md` for the channel-agnostic case): 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.
59
59
  36. **The document is reviewed before it is published.** An analysis run used to go from draft straight to dispatch behind a deterministic validator, so nothing read what it was about to publish: one run put a channel it never searched for, an open question about a frame it never opened, and twenty-three unowned `EKLENECEK` markers onto a live page. Every one is what a reader catches on the first pass. Phase 3.2 runs `phases/phase-4-review.md` Step 0 (strict validator, the host's three-reviewer set, triage) on the draft before the destination is chosen: a finding is cheap while nothing is written. Reviewers are subagents holding `analysis/review.md`, never the context that wrote the document, which cannot notice a search it never thought to run. A blocking finding returns to Phase 2b with dispatch closed and never becomes an open question, since "the document is wrong" is not something to ask the reader; capped at two returns. Phase 3.3 sorts every remaining gap into searched-and-closed, asked-and-answered, or `AS-NN`; an unstamped gap fails the dispatch gate. Autopilot runs both; only the asking degrades, into rows stamped `autopilot: could not ask`.
60
+ 37. **A redesign records v1 before it plans v2.** `options.redesign` is an opt-in on the `options.uiTests` axis, never a third `mode` value: `mode` says how many sections, `redesign` says which content, and a `mode: redesign` would switch off the Full-mode traceability, Test Plan and rule-to-test gates in exactly the documents that need them. It adds three sub-sections and no top-level section: current behaviour with `CB-<slug>-NN` ids and `repo/file:line` citations, the v1 to v2 endpoint mapping, and a difference list over a closed status vocabulary. Every `Missing` and `Partial` owes a Section 20 row by `AS-NN`, and a `CB-` id in one table but not the other fails in both directions - a behaviour that is in the code and on nobody's difference list is what a redesign loses and production finds. Evidence and certainty are derived from `repoEvidence`, never graded by the writer (Locked 24, same reason), and `options.redesign` is an `evidence_digest` input. Contract and the eight checks: `analysis/redesign.md`, loaded only on a redesign run.
@@ -0,0 +1,112 @@
1
+ # Redesign mode - the contract
2
+
3
+ Loaded only when `state.analysisSpec.options.redesign` is true, the same way
4
+ `analysis/review.md` is loaded only by a reviewer subagent. A run that is not a
5
+ redesign never pays for this file.
6
+
7
+ ## What the mode is for
8
+
9
+ A redesign is not a feature. The thing already exists, somebody is going to
10
+ replace it, and the question a reader has is not "what should v2 do" but "what
11
+ does v1 do that v2 must not lose". A normal analysis answers the first question
12
+ and is silent on the second, so the losses surface in production: a validation
13
+ rule nobody wrote down, an error state only the old screen had, a query
14
+ parameter one caller still sends.
15
+
16
+ So the mode adds exactly three artefacts, and every one of them is a claim about
17
+ v1 rather than a plan for v2.
18
+
19
+ ## Why it is an option and not a mode
20
+
21
+ `mode` says how many sections the document carries. `redesign` says which
22
+ content is required. They are different axes, and collapsing them costs real
23
+ checks: `mode === "full"` branches in four places inside
24
+ `validate-analysis-doc.mjs`, so a `mode: redesign` value would silently switch
25
+ off the traceability matrix, the Test Plan requirement and the business-rule to
26
+ test-scenario cross-check - in the documents that need them most. A small screen
27
+ can also have a lite redesign, and that is a real thing to want.
28
+
29
+ The shape therefore matches `options.uiTests` and `options.a11yDepth` exactly: an
30
+ intake opt-in, written into the front-matter, enforced by the validator's
31
+ "front-matter says so, the section must exist" rule.
32
+
33
+ ## The three artefacts
34
+
35
+ Section numbers are per profile. The global profile uses sub-sections of existing
36
+ sections (4.5, 4.6, 9.5) and the corporate profile uses 2.1, 2.3 and 5.N+4; no
37
+ new top-level section is introduced in either, because section numbers are quoted
38
+ in 172 places and renumbering them to add a mode is a worse trade than nesting.
39
+
40
+ ### 1. Current behaviour
41
+
42
+ Every behaviour v1 has that a reader could otherwise only find by reading v1.
43
+ One row per behaviour, with a stable `CB-<slug>-NN` id and a `repo/file:line`
44
+ citation.
45
+
46
+ | Column | Contract |
47
+ |---|---|
48
+ | Id | `CB-<slug>-NN`, `<slug>` the same feature slug the `BR-` ids use |
49
+ | Behaviour | one sentence, present tense, no v2 language |
50
+ | Evidence | `<repo>/<path>:<line>`, or the evidence label when no line can be named |
51
+ | Certainty | `confirmed` or `uncertain` |
52
+
53
+ `Evidence` and `Certainty` are **derived by the renderer** from
54
+ `state.analysisSpec.evidence.repoEvidence`, never asked of the author and never
55
+ written by the model from its own impression. A `direct-match` or `same-domain`
56
+ hit yields `confirmed` with its path and line; a `cross-cutting` hit yields
57
+ `uncertain` with the module name. Letting the writer grade its own evidence is
58
+ the failure Locked 24 already refuses for the concept table, and it is the same
59
+ failure here.
60
+
61
+ ### 2. Endpoint mapping
62
+
63
+ The v1 call and the v2 call side by side, one row per endpoint, plus what
64
+ changed in the shape. A redesign that keeps the same paths still gets the table,
65
+ with the rows saying so: "unchanged" is an answer, and its absence is not.
66
+
67
+ ### 3. Difference list
68
+
69
+ Where each v1 behaviour went. One row per `CB-` id, and the status comes from a
70
+ closed five-value vocabulary:
71
+
72
+ | Status | Meaning |
73
+ |---|---|
74
+ | `Moved` | v2 keeps the behaviour, in a different place; the row names where |
75
+ | `Partial` | v2 keeps part of it; the row names what is dropped |
76
+ | `Missing` | v2 does not have it, and nobody has decided that yet |
77
+ | `New` | v2 behaviour with no v1 counterpart |
78
+ | `Out of scope` | deliberately dropped, with the decision recorded |
79
+
80
+ The cell is bilingual in the same shape Section 20 uses for `Açık / Open`, so a
81
+ Turkish document reads as Turkish while the machine-checked half stays a fixed
82
+ English token.
83
+
84
+ `Missing` and `Partial` are the two that mean work is unfinished, so each owes a
85
+ Section 20 row by `AS-NN`. That is the reason the mode exists: a behaviour that
86
+ v2 drops without a decision is exactly the thing that gets found in production.
87
+
88
+ ## The eight checks
89
+
90
+ All in `validate-analysis-doc.mjs`, all live only when the front-matter says
91
+ `redesign: true`.
92
+
93
+ | Check | Severity | What it catches |
94
+ |---|---|---|
95
+ | the three sections exist | ERROR | Without the sections the row checks below pass over nothing, which is the defect that shows a gate green |
96
+ | a row has no `file:line` and is not marked `uncertain` | ERROR | An unmarked guess. Prose can only ask an author to mark it; this finds the row that was not marked |
97
+ | evidence is `cross-cutting` only, yet `confirmed` | WARN | A legitimate rule in a shared module reads this way, so it must not block - but Phase 4 runs `--strict`, so it blocks in review |
98
+ | a status value outside the vocabulary | ERROR | A controlled vocabulary decaying into free text |
99
+ | a `Missing` or `Partial` row with no Section 20 counterpart | ERROR | The reason the mode exists |
100
+ | a `CB-` id in one table and not the other, both directions | ERROR | A behaviour that is in the code and on nobody's difference list: the thing that disappears in a redesign and is found in production |
101
+ | the endpoint table's column count, then a half-written row | ERROR | A renderer that adds a column silently disabling the row checks |
102
+ | the sections are present but the front-matter does not say `redesign` | WARN | Drift |
103
+
104
+ ## The cache, and the silent failure to avoid
105
+
106
+ `evidence_digest` (Locked 27) summarises `featureName || platforms ||
107
+ repoEvidence || conventions`, with a 24-hour TTL. `options.redesign` **is a
108
+ digest input**. Without it, a redesign started within a day of a normal run on
109
+ the same feature hits that run's cache, skips Phase 1b entirely, and renders a
110
+ redesign document whose current-behaviour table is empty - at which point all
111
+ eight checks above pass over nothing. This is the most likely quiet failure in
112
+ the whole mode, and one digest input is the whole fix.
@@ -137,7 +137,7 @@ Iterate `state.analysisSpec.outputs.requested`. For each target:
137
137
  | Confluence | Re-humanize each draft with `formal-stakeholder` tone. One page per draft under the chosen parent, titled `<Feature> - <Platform>` - including a repo-less run, whose drafts are the derived channels (`<Feature> - Mobile` / `<Feature> - Web`, Locked 35). A single channel-agnostic draft becomes one page titled `<Feature>`, with no suffix implying a split that was not made. Cross-link siblings inside each page via `<ac:link><ri:page ri:content-title="<Feature> - <OtherPlatform>"/></ac:link>`; a lone page has no sibling block. Markdown -> storage XML via `$HOME/.claude/multi-agent-refs/channels/confluence.md`. Re-emit on existing pages uses PUT with version bump. |
138
138
  | Jira | Re-humanize the combined body with `informal-technical` tone. Concatenate the drafts under `h2. Platform: <X>` separators in production order - `iOS`, `Android`, `Web`, `Backend` repo-backed, `Mobile` / `Web` for channels derived on a repo-less run (Locked 35). A single channel-agnostic draft gets no separator: a heading announcing a split of one is noise. 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. |
139
139
 
140
- **Output capture**: fill `state.analysisSpec.outputs.localPaths[]` (one entry per per-platform-per-repo write, or one per derived channel on a repo-less run), `outputs.confluencePageUrls[]` (one entry per emitted document), `outputs.jiraIssueKey` (single string).
140
+ **Output capture**: fill `state.analysisSpec.outputs.localPaths[]` (one entry per per-platform-per-repo write, or one per derived channel on a repo-less run), `outputs.confluencePageUrls[]` (one entry per emitted document), `outputs.confluencePages[]` (the same pages by IDENTITY - `pageId`, `title`, `space`, `channel` - because a write-back needs the id and a `/display/SPACE/Title` URL cannot be parsed for one), `outputs.jiraIssueKey` (single string).
141
141
 
142
142
  ### Phase 5 - Report & stop
143
143
 
@@ -191,8 +191,13 @@ Outputs:
191
191
  https://{CONFLUENCE_HOST}/pages/viewpage.action?pageId=... (Android)
192
192
  https://{CONFLUENCE_HOST}/pages/viewpage.action?pageId=... (Backend)
193
193
  - Jira: {JIRA_KEY}-12345 (description updated with 3 platform sections)
194
+
195
+ Provenance:
196
+ evidence_digest: sha256:1f3a9c2 base_commit: 4c1b2de
194
197
  ```
195
198
 
199
+ **Provenance line.** Print the front-matter's `evidence_digest` and `base_commit` in the report too. A timestamp cannot separate two reports made the same day; these two say which side moved.
200
+
196
201
  **Stop. Do not chain into a dev run. Do not open a worktree. Do not create a branch.**
197
202
 
198
203
  **Open-question follow-up**: when any rendered file's Section 20 (Risks and Open Questions) has rows with status `Acik / Open`, append one line to the report: `<localized: "Section 20 has <N> open rows. Run /multi-agent:analysis-resolve to resolve them interactively before dispatching to dev.">`. This is a suggestion line only - never auto-invoke the resolver.
@@ -71,6 +71,7 @@ For each open row in source order:
71
71
  1. **Sibling propagation** (only if front-matter `siblings[]` is non-empty AND at least one resolved row's question text appears verbatim with an open status in a sibling file on disk): one AskUserQuestion, `header: "Siblings"`, question `<localized: "N resolved rows also appear open in sibling file(s) <list>. Apply the same resolutions there?">`, options `Apply to all listed` / `Skip siblings`. On apply: repeat the Phase 2 apply step per matching row per sibling, then give each touched sibling its own changelog row. Platform-specific classes (`convention-fallback`, `reuse-vs-new`) are excluded from matching (New Locked 9).
72
72
  2. **Changelog**: append one row to the Changelog section of every touched file: next version (integer scheme `v1 -> v2`; dotted scheme bumps the minor), today's date, author `analysis-resolve`, change `Resolved <N> of <M> Section 20 rows; <K> deferred`.
73
73
  3. **Punctuation gate**: run the Locked 7 verification grep over every touched file; fix any hit before reporting.
74
+ 3b. **Closure**: when a touched file has zero rows left at `Acik / Open` or `Girdi bekleniyor / Pending input` AND no `AS-NN` survives in its body, ask once per file - `header: "Publish"`, question `<localized: "<file> has no open questions left. Mark it status: final?">`, options `Mark final` / `Keep as draft`. On `Mark final`, set front-matter `status: final`; otherwise leave the key alone. Never flip it without the answer: `final` is a publication claim, and this command is built on one question per decision. `status: final` with anything still open is an ERROR in `validate-analysis-doc.mjs`, so the flip is what makes the closure gate real rather than advisory. Nothing flips back to `draft` here - reopening a question is a new run's decision.
74
75
  4. **Report** (in `outputLanguage`, max 12 lines): doc path(s) + new version, counts (resolved / deferred / follow-ups created / still open), `design-gap` rows that need a `/multi-agent:analysis` re-run (list inputs to re-supply), and - if open rows remain - a reminder that re-running this command resumes where it left off (state is the doc itself; no separate state file).
75
76
 
76
77
  **Stop. No commit, no branch, no dispatch.** The user reviews the diff and commits manually (Locked 6).
@@ -86,6 +86,21 @@ Fable triages the pooled findings exactly as in `/multi-agent:review`: drop dupl
86
86
 
87
87
  The verdict names the profile it judged against, the counts per severity, and what was NOT checked (the references gate without a state file, anything the fetch could not reach). Follow the pipeline rule on claiming: state which findings are mechanically proven and which are judgement.
88
88
 
89
+ **Every rubric class is printed, including the empty ones.** A verdict that lists only the classes that fired says nothing about the rest: the reader cannot tell "checked, clean" from "never looked". Print A through F in order with a count each, and write `none` where the count is zero. Same rule for the deterministic gates: name each one and its verdict, including the ones that skipped and why.
90
+
91
+ **Colophon.** Close the verdict with the reviewed document's `evidence_digest` and `base_commit`, taken from its front-matter, plus the reviewer models used. Two reviews of the same feature on the same day are otherwise indistinguishable, and the first question anyone asks of an older verdict is which version of the document it judged. `validate-analysis-doc.mjs --report` prints the per-check verdicts to paste under it.
92
+
93
+ ```
94
+ Verdict: 2 Blocker, 3 Important, 1 Suggestion (profile: global, mode: full)
95
+
96
+ A evidence 2 D altitude none
97
+ B backbone none E admitted gaps 1
98
+ C ... F contradiction 3
99
+
100
+ Not checked: references coverage (no state file passed)
101
+ Provenance: evidence_digest sha256:1f3a9c2 base_commit 4c1b2de
102
+ ```
103
+
89
104
  ## Phase 4 - Output
90
105
 
91
106
  Default is the chat report. Nothing is written anywhere without an explicit choice.
@@ -88,7 +88,7 @@ For each `platform` in `state.analysisSpec.platforms[]`:
88
88
  - `web` → `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. Web 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 `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).
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>`, `a11y_depth: <state.analysisSpec.options.a11yDepth | basic>` and `redesign: <state.analysisSpec.options.redesign | false>` 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, 4.5 / 4.6 / 9.5 present when redesign). `status` is written only by `/multi-agent:analysis-resolve`; a rendered document is a draft.
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:
@@ -433,12 +433,12 @@ Never omitted.
433
433
  ## 20. Riskler ve Açık Sorular <!-- TR -->
434
434
  ## 20. Risks and Open Questions <!-- EN -->
435
435
 
436
- | # | Konu | Neden açık | Kime sorulacak | Etkilediği bölüm |
436
+ | Id | Konu | Neden açık | Kime sorulacak | Etkilediği bölüm |
437
437
  |---|---|---|---|---|
438
- | 1 | <question> | <what evidence is missing> | <role or team> | 2.3 |
438
+ | AS-01 | <question> | <what evidence is missing> | <role or team> | 2.3 |
439
439
  ```
440
440
 
441
- The source corporate documents keep open questions out of the page and raise them in conversation instead. This profile keeps them in the document deliberately: an `EKLENECEK` with no matching row here is an unanswered question nobody owns. Every `EKLENECEK` and every unverified assumption in 2.4 emits a row (Locked 33).
441
+ The source corporate documents keep open questions out of the page and raise them in conversation instead. This profile keeps them in the document deliberately: an `EKLENECEK` with no matching `AS-NN` row here is an unanswered question nobody owns. Every `EKLENECEK` and every unverified assumption in 2.4 emits a row (Locked 33).
442
442
 
443
443
  ## 21. Referanslar / References
444
444
 
@@ -46,6 +46,8 @@ platform: ios | android | web | backend | mobile | none
46
46
  profile: global | corporate
47
47
  language: tr | en
48
48
  mode: full | lite
49
+ status: draft | final
50
+ redesign: true | false
49
51
  ui_tests: true | false
50
52
  a11y_depth: basic | full
51
53
  generated: <ISO 8601 UTC timestamp>
@@ -64,6 +66,8 @@ template_version: v3
64
66
 
65
67
  `ui_tests` and `a11y_depth` record the Phase 0 Step 5a opt-ins (defaults `false` / `basic`) so the coverage choice is auditable and the pre-dispatch validator can enforce it: `ui_tests: true` requires Section 15.6, `a11y_depth: full` requires the Section 16.2 walkthrough.
66
68
 
69
+ `status` is the publication claim; absent means `draft`. Under `final` an unresolved `AS-NN` or open Section 20 row is an ERROR, under `draft` it is counted. `/multi-agent:analysis-resolve` flips it, after a confirmation.
70
+
67
71
  `profile` names the template the document was rendered against (Locked 32) and `platform: none` marks the stack-optional render (Locked 35). Both are read by `validate-analysis-doc.mjs`, which applies a different contract per profile: without the key a corporate document would be judged against the global rules and its backbone would read as a pile of Locked 2 violations.
68
72
 
69
73
  Phase 1 compares `evidence_digest` against an existing document to decide whether to reuse it (Locked 27). Phase 3 reads `platform` to verify file match, `mode` to know which section set to expect, and both `evidence_digest` and `base_commit` to judge freshness: the digest says the evidence changed, `base_commit` says the repo moved. Phase 2 parses the block but gates only on `template_version`.
@@ -284,6 +288,27 @@ EARS states the rule; Gherkin states how you check it. Each rule still maps to a
284
288
 
285
289
  Each scenario in 4.1-4.3 cites the matching `BR-<slug>-NN` id (and, when a server code applies, the Section 9.4 code). Plain-prose user stories without Given/When/Then are rejected by the renderer.
286
290
 
291
+ ### 4.5 Mevcut Davranış / Current Behaviour (OPTIONAL)
292
+
293
+ `redesign: true` only. Locked 37; contract and checks in `analysis/redesign.md`. Evidence and certainty are derived from `repoEvidence`, never graded by the writer.
294
+
295
+ ```markdown
296
+ | Id | Davranış / Behaviour | Kanıt / Evidence | Kesinlik / Certainty |
297
+ |---|---|---|---|
298
+ | CB-<slug>-01 | <what v1 does, present tense, no v2 language> | <repo>/<path>:<line> | confirmed |
299
+ | CB-<slug>-02 | <what v1 does> | <module> (cross-cutting) | uncertain |
300
+ ```
301
+
302
+ ### 4.6 Endpoint Eşlemesi / Endpoint Mapping (OPTIONAL)
303
+
304
+ `redesign: true` only. An unchanged path still gets its row: "unchanged" is an answer, its absence is not.
305
+
306
+ ```markdown
307
+ | v1 | v2 | Değişiklik / Change |
308
+ |---|---|---|
309
+ | GET /v1/<path> | GET /v2/<path> | <shape change, or unchanged> |
310
+ ```
311
+
287
312
  ## 5. Tasarım Referansı / Design Reference
288
313
 
289
314
  Per-platform projection. Omitted if no Figma URL AND no Code Connect mapping. Locked 17 + 18.
@@ -484,6 +509,17 @@ List every HTTP status code one by one.
484
509
  | ERR-211 | Empty result set | <screen> |
485
510
  ```
486
511
 
512
+ ### 9.5 Fark Listesi / Difference List (OPTIONAL)
513
+
514
+ `redesign: true` only. One row per `CB-` id, both directions checked. Closed status vocabulary - `Moved`, `Partial`, `Missing`, `New`, `Out of scope` - in a bilingual cell like Section 20's `Açık / Open`. Every `Missing` and `Partial` owes a Section 20 row by `AS-NN`.
515
+
516
+ ```markdown
517
+ | Id | Durum / Status | Nereye / Where | AS-NN |
518
+ |---|---|---|---|
519
+ | CB-<slug>-01 | Taşındı / Moved | 4.2 | - |
520
+ | CB-<slug>-02 | Eksik / Missing | - | AS-03 |
521
+ ```
522
+
487
523
  ## 10. Lokalizasyon Anahtarları / Localization Keys
488
524
 
489
525
  Per-platform projection. Locked 20 - shape depends on the project `figma-config` `localization.ownership`. The locale set comes from `localization.locales` (default `tr, en, ar, de, es, fr, it, ru`); never hardcode a locale list.
@@ -6,11 +6,11 @@
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Command Inventory (51 commands)
9
+ ## 1. Command Inventory (53 commands)
10
10
 
11
11
  ```
12
- analysis, analysis-resolve, autopilot, build-optimize, channels,
13
- complaint-analysis, create-jira, design-check, diff-explain, feedback,
12
+ analysis, analysis-jira, analysis-resolve, autopilot, build-optimize, channels,
13
+ complaint-analysis, create-jira, design-check, doctor, diff-explain, feedback,
14
14
  forget, garbage-collect, graph, help, ios-coding-standard, issue, jira,
15
15
  kill, language, local, local-autopilot, log, manual-test, prune-logs,
16
16
  prune-prompts, purge, refactor, resume, resume-local, review,
@@ -230,6 +230,9 @@ argument-hint: "<input hint>"
230
230
  |---|---|
231
231
  | Claude → Copilot | Add `name`, `user-invocable: true`, `argument-hint`; remove `allowed-tools`; `description` stays English (localization happens only in the installed Claude tree via `description-tr` + `localize-commands.mjs`, never in repo or Copilot files) |
232
232
  | Copilot → Claude | Remove `name`, `user-invocable`, `argument-hint`; add `allowed-tools` (inferred from command's actual tool calls) |
233
+ | Both directions | `not-for: <sibling>` carries across unchanged |
234
+
235
+ **`not-for`** is optional and lists sibling commands this one must not be chosen for, as bare names (`not-for: jira, review-jira`). It is written for the model reading the routing surface, so it belongs beside the description rather than in prose further down. `lint-skills.mjs` also consumes it: a trigger-vocabulary collision either side has named drops off the warning list and is reported as settled instead, and a name that resolves to no sibling on the same surface is an error - a typo would otherwise leave the pair unanswered while the author believes it is handled. Both trees spell the value the same way; the `multi-agent-` prefix on the Copilot side is stripped before matching.
233
236
 
234
237
  **Invariant**: the English `description` must have the same meaning on both sides, and a `description-tr` line (when present) must be a faithful translation of it. Semantic drift is not allowed. `description-en` sidecars are an installed-tree artifact and must never appear in repo or Copilot files.
235
238
 
@@ -0,0 +1,128 @@
1
+ # analysis-jira - an analysis document, read as work
2
+
3
+ `/multi-agent:analysis-jira` turns a rendered analysis document into a Jira tree.
4
+ Two files do it, and the split is the design:
5
+
6
+ | File | Does | Network |
7
+ |---|---|---|
8
+ | `scripts/analysis-story-tree.mjs` | decides WHAT to create, checks coverage, assigns identity | none |
9
+ | `lib/analysis-jira-write.sh` | creates it | yes |
10
+
11
+ Planning is deterministic and testable offline; creating issues is neither. Kept
12
+ apart, the file that can write to a tracker is small enough to read in one
13
+ sitting.
14
+
15
+ `jira-publish.sh` could not be extended to do this: it writes a comment or a
16
+ description on an issue that already exists. The only creation path in the repo
17
+ was a curl hand-written inside a markdown instruction, which is what this
18
+ replaces.
19
+
20
+ ## The marker gate runs first, before any network call
21
+
22
+ Zero `EKLENECEK`, zero `TBD`, no open Section 20 row. A document with an open
23
+ placeholder is not a plan, and turning it into a tree publishes the gap as work
24
+ somebody is now assigned. Refusal is exit 4 and it names
25
+ `/multi-agent:analysis-resolve` as the step.
26
+
27
+ This is the same closure contract `status: final` enforces in
28
+ `validate-analysis-doc.mjs`, applied at the point the document leaves the
29
+ analysis world.
30
+
31
+ ## Coverage is two-way, and the second direction is the useful one
32
+
33
+ | Direction | Catches |
34
+ |---|---|
35
+ | every defined id appears in some story | a dropped requirement |
36
+ | every cited id is defined in the document | an **invented story** - a node with no requirement behind it |
37
+
38
+ No forward check can see the second one, and it is the failure mode of building
39
+ a tree from a model's reading rather than from the document's own ids.
40
+
41
+ The atom is `BR-<slug>-NN` in the global profile and `FG-NN` in the corporate
42
+ one; the group is the `BR-<slug>` prefix, or `UC-NN`. Locked 31 guarantees both
43
+ exist, which is why the tree can be derived rather than invented.
44
+
45
+ `coverageOf()` is exported and tested directly. The planner cannot emit an
46
+ invented id - it derives every `sourceIds` from the defined set - so a test that
47
+ fabricates one and re-checks it with its own logic proves nothing about the
48
+ shipped code. The backward direction guards the boundary where a plan arrives
49
+ from somewhere else: hand-edited, resumed from an older format, or produced by
50
+ something that read the prose instead of the ids.
51
+
52
+ ## An unverifiable run is allowed; looking verified is not
53
+
54
+ A lite document may carry no ids at all. Coverage then cannot run, and the
55
+ verdict is `unverifiable`, never `ok`. It appears:
56
+
57
+ - on its own line in the preview, where a reader looks for `ok`
58
+ - as a separate fourth approval option, not folded into `Approve`
59
+ - in the writer's own output, and beside every key in the ledger
60
+
61
+ Such a document still plans work, from its user-story sub-sections. That is not
62
+ a convenience: without it the no-atom document produced no stories, exited
63
+ "nothing to plan", and the `unverifiable` branch was unreachable - a case the
64
+ code claimed to handle and never could.
65
+
66
+ ## Identity is a label, not a title
67
+
68
+ Each node carries `<labelPrefix>-<10 hex>`, hashed from the document id
69
+ (`evidence_digest`) plus the node's own source ids. A second run finds its tree
70
+ back with one JQL search on those labels.
71
+
72
+ Titles were the obvious key and are the wrong one: they get edited, and matching
73
+ on them breaks exactly when someone has improved the wording. The label lives
74
+ server-side, so it survives a new machine, a deleted `~/.claude`, and a **second
75
+ analyst** - who is precisely the person positioned to open a duplicate tree.
76
+
77
+ ## The write is ledgered
78
+
79
+ `~/.claude/logs/multi-agent/_analysis-jira/<docId>.jsonl`. An `intent` line
80
+ before each POST, the key after the response. A crash between them leaves an
81
+ intent with no key; the next run sees that and searches by label before sending
82
+ anything. Without the ledger the failure mode is not "the run stopped" but "the
83
+ run stopped and the retry made a second tree", which is the expensive one.
84
+
85
+ Every line also carries the coverage verdict, so "was this tree checked?" stays
86
+ answerable from the tree's own trail.
87
+
88
+ ## An existing node is skipped, never updated
89
+
90
+ Jira has no backup path for fields other than description. Rewriting a body an
91
+ engineer has since edited would repeat, at tree scale, the defect that made
92
+ `jira-publish.sh` take backups in the first place.
93
+
94
+ ## Every site-specific name is a VALUE, never a schema key
95
+
96
+ `prefs.global.issueTree` holds the vocabulary. A key is a published literal and
97
+ this schema ships to everyone, so a site's component, team and issue-type names
98
+ are values the site fills in:
99
+
100
+ - `channelComponents` / `channelTeams` are free-form maps: the key is the
101
+ channel, the value is the site's own name for it
102
+ - `subtaskRoles` ships **empty**. An empty list is an instruction to look at how
103
+ this board actually splits work; a ready-made list would be the guess most
104
+ worth avoiding
105
+ - `subtaskIssueType: null` means discover it - `createmeta` returns whichever
106
+ type carries `subtask: true`, under whatever name the site gave it. Same rule
107
+ as `features/jira-context.md`: the type travels as it comes from Jira and is
108
+ never a matching criterion in code
109
+
110
+ The preview prints every field beside the pref key it came from, so a wrong
111
+ setting is visible before the writes rather than in Jira afterwards.
112
+
113
+ ## Auth
114
+
115
+ `lib/_jira-auth.sh` holds one host-and-token resolution and one curl idiom: the
116
+ token reaches curl through a `-K` config on process substitution and never
117
+ touches argv, a log, or `ps`. That idiom is the part most easily retyped badly,
118
+ which is why the third writer got a shared copy instead of a third hand-written
119
+ one.
120
+
121
+ **Only this writer consumes it today.** `jira-publish.sh` and `issue-fetcher.sh`
122
+ still carry their own resolution, and retrofitting them is real work rather than
123
+ a rename - `issue-fetcher.sh` resolves per-account token keys that this helper
124
+ does not model yet. So the file is the shared copy going forward, not a
125
+ consolidation that has already happened, and saying otherwise would describe a
126
+ cleanup nobody did. The leak property itself is asserted on all three callers
127
+ independently in `smoke-analysis-jira.sh`, which is the part that must hold
128
+ whether or not they ever share code.