@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.
- package/CHANGELOG.md +144 -1
- package/README.md +4 -4
- package/README.tr.md +3 -3
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/install/claude.mjs +17 -0
- package/package.json +4 -4
- package/pipeline/commands/multi-agent/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +93 -0
- package/pipeline/commands/multi-agent/doctor/SKILL.md +78 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/jira/SKILL.md +3 -0
- package/pipeline/commands/multi-agent/log/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +14 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +12 -9
- package/pipeline/commands/multi-agent/update/SKILL.md +12 -0
- package/pipeline/lib/_jira-auth.sh +99 -0
- package/pipeline/lib/analysis-jira-write.sh +203 -0
- package/pipeline/lib/issue-fetcher.sh +138 -9
- package/pipeline/lib/multi-repo-pipeline.sh +8 -0
- package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
- package/pipeline/multi-agent-refs/analysis/intake.md +11 -2
- package/pipeline/multi-agent-refs/analysis/locked.md +1 -0
- package/pipeline/multi-agent-refs/analysis/redesign.md +112 -0
- package/pipeline/multi-agent-refs/analysis/render.md +6 -1
- package/pipeline/multi-agent-refs/analysis/resolve.md +1 -0
- package/pipeline/multi-agent-refs/analysis/review.md +15 -0
- package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +3 -3
- package/pipeline/multi-agent-refs/analysis-template.md +36 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +6 -3
- package/pipeline/multi-agent-refs/features/analysis-jira.md +128 -0
- package/pipeline/multi-agent-refs/features/doctor.md +197 -0
- package/pipeline/multi-agent-refs/features/jira-context.md +101 -0
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -7
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
- package/pipeline/multi-agent-refs/picker-contract.md +35 -0
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/tracker-contract.md +5 -1
- package/pipeline/preferences-template.json +1 -1
- package/pipeline/schemas/agent-state.schema.json +24 -0
- package/pipeline/schemas/analysis-spec.schema.json +336 -95
- package/pipeline/schemas/prefs.schema.json +86 -2
- package/pipeline/scripts/analysis-story-tree.mjs +441 -0
- package/pipeline/scripts/anonymize-findings.mjs +24 -0
- package/pipeline/scripts/build-references.mjs +10 -6
- package/pipeline/scripts/council-view.mjs +144 -0
- package/pipeline/scripts/doctor.mjs +758 -0
- package/pipeline/scripts/phase-tracker.sh +100 -3
- package/pipeline/scripts/scan-agent-config.sh +48 -10
- package/pipeline/scripts/skill-siblings.mjs +42 -9
- package/pipeline/scripts/validate-analysis-doc.mjs +371 -7
- package/pipeline/scripts/validate-analysis.mjs +7 -5
- package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +94 -0
- package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +79 -0
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -0
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +13 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +9 -6
- 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,
|
|
242
|
+
#### Step 5a - Coverage options (opt-in, 3 questions)
|
|
243
243
|
|
|
244
|
-
One `AskUserQuestion` call with
|
|
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
|
|
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
|
-
|
|
|
436
|
+
| Id | Konu | Neden açık | Kime sorulacak | Etkilediği bölüm |
|
|
437
437
|
|---|---|---|---|---|
|
|
438
|
-
|
|
|
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 (
|
|
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.
|