@mmerterden/multi-agent-pipeline 16.23.0 → 16.25.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.
@@ -241,11 +241,11 @@ curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application
241
241
  ```
242
242
  4. **Screenshot / image attachments** (only when opted in during preview - Figma render and/or user-pasted images):
243
243
  ```bash
244
- curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "X-Atlassian-Token: no-check" \
245
- -F "file=@/tmp/generate-issue-$$-figma.png" \
246
- "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue/${NEW_KEY}/attachments"
244
+ bash "$HOME/.claude/scripts/jira-attach.sh" "$NEW_KEY" /tmp/generate-issue-$$-figma.png
247
245
  ```
248
246
 
247
+ One uploader, shared with visual evidence: two copies of this call is two places for `X-Atlassian-Token: no-check` to go missing, which fails as a 403 that reads like an auth problem. It prints `<filename>\t<url>` per file.
248
+
249
249
  ### [12/12] Report
250
250
 
251
251
  ```
@@ -93,6 +93,8 @@ Probe order:
93
93
  1. **Tier 1 (Figma MCP)**: check the host serves `mcp__claude_ai_Figma__*` before probing. Absent → set `state.figmaAccess.tier1Unavailable = "host"` and fall through to Tier 2 with no probe, no re-auth retry, no MCP-token question. Present → probe `get_metadata(fileKey, nodeId)` on the first frame; on auth failure run `authenticate` + `complete_authentication` and retry once, and only a *second* failure raises the recreate-or-continue question. Success → `state.figmaAccess.tier = 1`.
94
94
  2. **Tier 2 (Figma REST)**: when Tier 1 fails, resolve the PAT via `~/.claude/lib/credential-store.sh get <logical-key>` where `<logical-key>` = `prefs.global.keychainMapping.figma`. Probe `GET https://api.figma.com/v1/files/{fileKey}/nodes?ids={nodeId}` with header `X-Figma-Token: $TOKEN`. HTTP 200 → `state.figmaAccess.tier = 2`. Token missing / 401 / 403 → fall through.
95
95
  3. **Tier 3 (User-attached screenshot)**: when Tiers 1 + 2 both fail, scan the task payload for inline screenshots or attachments. Present → `state.figmaAccess.tier = 3` and `state.figmaAccess.reviewBlocking = true` (Phase 4 enforces this).
96
+
97
+ Save the issue's image attachments to `$WORKTREE/.pipeline/evidence/` as `state.visualEvidence.before[]`: pre-fix evidence, never re-photographed, and none present is a recorded gap rather than a search. See `$HOME/.claude/multi-agent-refs/features/visual-evidence.md`.
96
98
  4. **Halt**: all three tiers fail → emit a single AskUserQuestion asking the user how to proceed (provide PAT, paste a screenshot, abort). Never proceed with text-derived guesses.
97
99
 
98
100
  Record the cause, not just the downshift. `tier1Unavailable = "host"` means the tier never existed here - routine on Copilot and Codex, where the installer registers only `multi-agent-toolkit`. `"auth"` means it existed and the credential failed, which on Claude Code points at a dead `figma_mcp` token worth surfacing in Phase 7. Conflating them costs two wasted MCP round trips and a question the user cannot act on. On those two hosts a mapped `figma` PAT is the primary path, not a fallback.
@@ -234,6 +234,10 @@ Gated by `prefs.global.devCritic.enabled` (default: `false`). When enabled, afte
234
234
 
235
235
  ---
236
236
 
237
+ #### Step 3.55 - Visual evidence capture (UI changes only)
238
+
239
+ When `state.visualEvidence.required`, capture the fixed state with the build that just went green: `capture-evidence.sh after --task "$TASK_ID" --platform "$PLATFORM" --label <slug>`. Here, not Phase 5, which every autopilot and `--local` entry drops. Exit 4 is a gap, not a failure. See `$HOME/.claude/multi-agent-refs/features/visual-evidence.md`.
240
+
237
241
  #### Step 3.6 - Code-simplifier pass (required diff shrink, before Phase 4 handoff)
238
242
 
239
243
  After the build/test green step and BEFORE Phase 4 handoff, run one diff-shrink round (bloated diffs waste Phase 4 reviewer tokens and unfocus the PR).
@@ -138,6 +138,7 @@ Before or during user testing, run device-level audits via Bash if user requests
138
138
 
139
139
  | Check | When | Command |
140
140
  | ------------------- | ----------------- | ----------------------------------------- |
141
+ | UI flow video | `state.visualEvidence.required` | 3 tiers, cap from `capture-evidence.sh limits` (`visualEvidence.maxVideoSeconds`) - `$HOME/.claude/multi-agent-refs/features/visual-evidence.md` |
141
142
  | Accessibility audit | UI changes | `mcp__multi-agent-toolkit__{ios,android}_accessibility_audit` |
142
143
  | Biometric test | Auth flow changes | ios: `mcp__multi-agent-toolkit__ios_biometric` (android: manual) |
143
144
  | Launch time | Perf-sensitive changes | ios: app-launch instrument · android: `mcp__multi-agent-toolkit__android_launch_time` |
@@ -136,7 +136,7 @@ Branch **deterministically**, no implicit fallback. Read `agent-state.json` and
136
136
 
137
137
  Generate a structured PR description based on task type. The PR body targets **code reviewers** - it should be technical: what changed, why, architecture decisions, how to verify.
138
138
 
139
- Two inputs are read from state and the worktree before writing, not recalled from the conversation: `$WORKTREE/.pipeline/scope-check.json` (Phase 3 Step 3.7) supplies the `## Changes` bullets from `files[].reason` and the "Follow-ups not done in this PR" list under `## Related` from `notDone[]`; `state.diffRisk.signals` (Phase 4 Step 1.75) decides whether the conditional `## Risk and Security` section is required. When a high-stakes signal is present and the section is missing, this step blocks until it is written; a placeholder answer ("TBD") counts as missing.
139
+ Two inputs are read from state and the worktree before writing, not recalled from the conversation: `$WORKTREE/.pipeline/scope-check.json` (Phase 3 Step 3.7) supplies the `## Changes` bullets from `files[].reason` and the "Follow-ups not done in this PR" list under `## Related` from `notDone[]`; `state.diffRisk.signals` (Phase 4 Step 1.75) decides whether the conditional `## Risk and Security` section is required. When a high-stakes signal is present and the section is missing, this step blocks until it is written; a placeholder answer ("TBD") counts as missing. `state.visualEvidence.required` blocks the same way: every required artefact is either attached or carries a recorded reason in `gaps[]` - the gate is against silence, not against an honest "no image on the ticket".
140
140
 
141
141
  **required**: Run all generated text (PR body, commit message) through the `humanizer` skill before posting. This removes AI-generated patterns (inflated language, filler phrases, repetitive structure) and makes the output sound like a developer wrote it.
142
142
 
@@ -272,7 +272,9 @@
272
272
  },
273
273
  "docPath": {
274
274
  "type": "array",
275
- "items": { "type": "string" },
275
+ "items": {
276
+ "type": "string"
277
+ },
276
278
  "description": "One emitted path per selected platform."
277
279
  },
278
280
  "frontMatter": {
@@ -604,7 +606,9 @@
604
606
  },
605
607
  "failing": {
606
608
  "type": "array",
607
- "items": { "type": "string" },
609
+ "items": {
610
+ "type": "string"
611
+ },
608
612
  "description": "Identifiers of tests already failing before this run. Empty on a red status means the output could not be parsed into names: the evidence is then the logPath alone, and Phase 4 reports 'inherited red, not attributable' rather than inventing a set."
609
613
  },
610
614
  "logPath": {
@@ -655,17 +659,28 @@
655
659
  "additionalProperties": true,
656
660
  "description": "Written by Phase 4 Step 3.8 from review-delta.mjs: how this round's accepted findings relate to the previous round's rework mandate. Absent on iteration 1 and on runs from before the delta existed.",
657
661
  "properties": {
658
- "previousIteration": { "type": "integer", "minimum": 1 },
662
+ "previousIteration": {
663
+ "type": "integer",
664
+ "minimum": 1
665
+ },
659
666
  "new": {
660
667
  "type": "array",
661
668
  "items": {
662
669
  "type": "object",
663
670
  "additionalProperties": true,
664
671
  "properties": {
665
- "fingerprint": { "type": "string" },
666
- "severity": { "type": ["string", "null"] },
667
- "file": { "type": ["string", "null"] },
668
- "issue": { "type": ["string", "null"] }
672
+ "fingerprint": {
673
+ "type": "string"
674
+ },
675
+ "severity": {
676
+ "type": ["string", "null"]
677
+ },
678
+ "file": {
679
+ "type": ["string", "null"]
680
+ },
681
+ "issue": {
682
+ "type": ["string", "null"]
683
+ }
669
684
  }
670
685
  }
671
686
  },
@@ -675,10 +690,18 @@
675
690
  "type": "object",
676
691
  "additionalProperties": true,
677
692
  "properties": {
678
- "fingerprint": { "type": "string" },
679
- "severity": { "type": ["string", "null"] },
680
- "file": { "type": ["string", "null"] },
681
- "issue": { "type": ["string", "null"] }
693
+ "fingerprint": {
694
+ "type": "string"
695
+ },
696
+ "severity": {
697
+ "type": ["string", "null"]
698
+ },
699
+ "file": {
700
+ "type": ["string", "null"]
701
+ },
702
+ "issue": {
703
+ "type": ["string", "null"]
704
+ }
682
705
  }
683
706
  }
684
707
  },
@@ -688,10 +711,18 @@
688
711
  "type": "object",
689
712
  "additionalProperties": true,
690
713
  "properties": {
691
- "fingerprint": { "type": "string" },
692
- "severity": { "type": ["string", "null"] },
693
- "file": { "type": ["string", "null"] },
694
- "issue": { "type": ["string", "null"] }
714
+ "fingerprint": {
715
+ "type": "string"
716
+ },
717
+ "severity": {
718
+ "type": ["string", "null"]
719
+ },
720
+ "file": {
721
+ "type": ["string", "null"]
722
+ },
723
+ "issue": {
724
+ "type": ["string", "null"]
725
+ }
695
726
  }
696
727
  }
697
728
  },
@@ -701,10 +732,18 @@
701
732
  "type": "object",
702
733
  "additionalProperties": true,
703
734
  "properties": {
704
- "fingerprint": { "type": "string" },
705
- "severity": { "type": ["string", "null"] },
706
- "file": { "type": ["string", "null"] },
707
- "issue": { "type": ["string", "null"] }
735
+ "fingerprint": {
736
+ "type": "string"
737
+ },
738
+ "severity": {
739
+ "type": ["string", "null"]
740
+ },
741
+ "file": {
742
+ "type": ["string", "null"]
743
+ },
744
+ "issue": {
745
+ "type": ["string", "null"]
746
+ }
708
747
  }
709
748
  }
710
749
  },
@@ -714,24 +753,40 @@
714
753
  "type": "object",
715
754
  "additionalProperties": true,
716
755
  "properties": {
717
- "fingerprint": { "type": "string" },
718
- "severity": { "type": ["string", "null"] },
719
- "file": { "type": ["string", "null"] },
720
- "issue": { "type": ["string", "null"] }
756
+ "fingerprint": {
757
+ "type": "string"
758
+ },
759
+ "severity": {
760
+ "type": ["string", "null"]
761
+ },
762
+ "file": {
763
+ "type": ["string", "null"]
764
+ },
765
+ "issue": {
766
+ "type": ["string", "null"]
767
+ }
721
768
  }
722
769
  }
723
770
  },
724
771
  "recurrence": {
725
772
  "type": "object",
726
- "additionalProperties": { "type": "integer", "minimum": 1 },
773
+ "additionalProperties": {
774
+ "type": "integer",
775
+ "minimum": 1
776
+ },
727
777
  "description": "fingerprint -> consecutive rework cycles the finding has survived."
728
778
  },
729
779
  "plateau": {
730
780
  "type": "boolean",
731
781
  "description": "The still-present set is unchanged from the previous delta and non-empty."
732
782
  },
733
- "tripped": { "type": "boolean" },
734
- "computedAt": { "type": "string", "format": "date-time" }
783
+ "tripped": {
784
+ "type": "boolean"
785
+ },
786
+ "computedAt": {
787
+ "type": "string",
788
+ "format": "date-time"
789
+ }
735
790
  }
736
791
  },
737
792
  "reviewers": {
@@ -768,7 +823,9 @@
768
823
  },
769
824
  "labelToModel": {
770
825
  "type": "object",
771
- "additionalProperties": { "type": "string" },
826
+ "additionalProperties": {
827
+ "type": "string"
828
+ },
772
829
  "description": "Source A|B|C -> model name. \"unknown\" for a reviewer entry that declared no model."
773
830
  }
774
831
  }
@@ -789,25 +846,51 @@
789
846
  "description": "Autopilot circuit-breaker record (refs/features/autopilot-circuit-breaker.md). Written only when a trigger trips: trigger 2 by Phase 4 Step 3.8 (a mandate finding survived identicalFindingCycles rework cycles), trigger 3 by the Phase 3 re-entry hard-kill. /multi-agent:resume clears tripped and keeps counters.",
790
847
  "required": ["tripped"],
791
848
  "properties": {
792
- "tripped": { "type": "boolean" },
793
- "trigger": { "type": ["integer", "null"], "minimum": 1, "maximum": 5 },
794
- "detail": { "type": "string" },
849
+ "tripped": {
850
+ "type": "boolean"
851
+ },
852
+ "trigger": {
853
+ "type": ["integer", "null"],
854
+ "minimum": 1,
855
+ "maximum": 5
856
+ },
857
+ "detail": {
858
+ "type": "string"
859
+ },
795
860
  "checkpoint": {
796
861
  "type": "object",
797
862
  "additionalProperties": false,
798
863
  "properties": {
799
- "phase": { "type": "integer", "minimum": 0, "maximum": 7 },
800
- "step": { "type": "string" },
801
- "iteration": { "type": "integer", "minimum": 1 }
864
+ "phase": {
865
+ "type": "integer",
866
+ "minimum": 0,
867
+ "maximum": 7
868
+ },
869
+ "step": {
870
+ "type": "string"
871
+ },
872
+ "iteration": {
873
+ "type": "integer",
874
+ "minimum": 1
875
+ }
802
876
  }
803
877
  },
804
- "trippedAt": { "type": "string", "format": "date-time" },
878
+ "trippedAt": {
879
+ "type": "string",
880
+ "format": "date-time"
881
+ },
805
882
  "counters": {
806
883
  "type": "object",
807
884
  "additionalProperties": false,
808
885
  "properties": {
809
- "identicalFindingCycles": { "type": "integer", "minimum": 0 },
810
- "reworkCycles": { "type": "integer", "minimum": 0 }
886
+ "identicalFindingCycles": {
887
+ "type": "integer",
888
+ "minimum": 0
889
+ },
890
+ "reworkCycles": {
891
+ "type": "integer",
892
+ "minimum": 0
893
+ }
811
894
  }
812
895
  }
813
896
  }
@@ -817,13 +900,26 @@
817
900
  "additionalProperties": true,
818
901
  "description": "Totals from diff-risk-score.mjs, persisted by Phase 4 Step 1.75 so Phase 6 (PR risk section), Phase 7 and run-metrics.mjs read the same numbers the review scope decision used.",
819
902
  "properties": {
820
- "files": { "type": "integer", "minimum": 0 },
821
- "loc_added": { "type": "integer", "minimum": 0 },
822
- "loc_removed": { "type": "integer", "minimum": 0 },
823
- "max_score": { "type": "number" },
903
+ "files": {
904
+ "type": "integer",
905
+ "minimum": 0
906
+ },
907
+ "loc_added": {
908
+ "type": "integer",
909
+ "minimum": 0
910
+ },
911
+ "loc_removed": {
912
+ "type": "integer",
913
+ "minimum": 0
914
+ },
915
+ "max_score": {
916
+ "type": "number"
917
+ },
824
918
  "signals": {
825
919
  "type": "array",
826
- "items": { "type": "string" },
920
+ "items": {
921
+ "type": "string"
922
+ },
827
923
  "description": "Distinct signal names seen across files (security_path, migration, public_api, no_test_change, test_lines_removed, ...)."
828
924
  }
829
925
  }
@@ -1001,6 +1097,137 @@
1001
1097
  "artifactsPath": {
1002
1098
  "type": ["string", "null"],
1003
1099
  "description": "Directory the worktree's artefacts were salvaged into before removal (agent-state, phase-tracker, triage-output, .pipeline/, build+test logs, review diff). Phase 7 and :resume read from here when worktreePath is gone."
1100
+ },
1101
+ "visualEvidence": {
1102
+ "type": ["object", "null"],
1103
+ "additionalProperties": false,
1104
+ "description": "Before/after screenshots and the UI flow video for a UI change, attached to the Jira issue and rendered in the Phase 7 comment and the PR body. Required decided mechanically from taskType plus the changed-file list. Contract: multi-agent-refs/features/visual-evidence.md.",
1105
+ "properties": {
1106
+ "required": {
1107
+ "type": "boolean"
1108
+ },
1109
+ "requiredBy": {
1110
+ "type": "string",
1111
+ "description": "Which rule made it required, e.g. 'bugfix + ui-file-changed'."
1112
+ },
1113
+ "platform": {
1114
+ "type": "string",
1115
+ "enum": ["ios", "android", "web", "other"]
1116
+ },
1117
+ "before": {
1118
+ "type": "array",
1119
+ "items": {
1120
+ "type": "object",
1121
+ "additionalProperties": true,
1122
+ "properties": {
1123
+ "file": {
1124
+ "type": "string",
1125
+ "description": "Local path under $WORKTREE/.pipeline/evidence/."
1126
+ },
1127
+ "source": {
1128
+ "type": "string",
1129
+ "enum": ["ticket", "capture"]
1130
+ },
1131
+ "capturedAt": {
1132
+ "type": "string",
1133
+ "description": "Phase that produced it, e.g. 'phase-3'."
1134
+ },
1135
+ "jiraFilename": {
1136
+ "type": "string",
1137
+ "description": "Attachment filename the Jira comment references."
1138
+ },
1139
+ "url": {
1140
+ "type": "string",
1141
+ "description": "Jira attachment content URL."
1142
+ }
1143
+ }
1144
+ },
1145
+ "description": "Images taken from the issue's own attachments. The pipeline never rebuilds the pre-fix state to photograph it; no ticket image means no before, recorded as a gap."
1146
+ },
1147
+ "after": {
1148
+ "type": "array",
1149
+ "items": {
1150
+ "type": "object",
1151
+ "additionalProperties": true,
1152
+ "properties": {
1153
+ "file": {
1154
+ "type": "string",
1155
+ "description": "Local path under $WORKTREE/.pipeline/evidence/."
1156
+ },
1157
+ "source": {
1158
+ "type": "string",
1159
+ "enum": ["ticket", "capture"]
1160
+ },
1161
+ "capturedAt": {
1162
+ "type": "string",
1163
+ "description": "Phase that produced it, e.g. 'phase-3'."
1164
+ },
1165
+ "jiraFilename": {
1166
+ "type": "string",
1167
+ "description": "Attachment filename the Jira comment references."
1168
+ },
1169
+ "url": {
1170
+ "type": "string",
1171
+ "description": "Jira attachment content URL."
1172
+ }
1173
+ }
1174
+ },
1175
+ "description": "Captured in Phase 3 after the build+test gate, because Phase 5 is dropped by every autopilot and --local entry."
1176
+ },
1177
+ "videoTier": {
1178
+ "type": ["integer", "null"],
1179
+ "enum": [1, 2, 3, null],
1180
+ "description": "1 = the repo's own UI test target drove the flow, 2 = MCP-driven flow, 3 = no runnable build."
1181
+ },
1182
+ "video": {
1183
+ "type": "object",
1184
+ "additionalProperties": true,
1185
+ "properties": {
1186
+ "file": {
1187
+ "type": "string",
1188
+ "description": "Local path under $WORKTREE/.pipeline/evidence/."
1189
+ },
1190
+ "source": {
1191
+ "type": "string",
1192
+ "enum": ["ticket", "capture"]
1193
+ },
1194
+ "capturedAt": {
1195
+ "type": "string",
1196
+ "description": "Phase that produced it, e.g. 'phase-3'."
1197
+ },
1198
+ "jiraFilename": {
1199
+ "type": "string",
1200
+ "description": "Attachment filename the Jira comment references."
1201
+ },
1202
+ "url": {
1203
+ "type": "string",
1204
+ "description": "Jira attachment content URL."
1205
+ },
1206
+ "seconds": {
1207
+ "type": "number",
1208
+ "description": "Recorded duration; capped at 60."
1209
+ }
1210
+ }
1211
+ },
1212
+ "gaps": {
1213
+ "type": "array",
1214
+ "description": "Every artefact that is required and absent, with its reason. A recorded reason satisfies the Phase 6 blocker; silence does not.",
1215
+ "items": {
1216
+ "type": "object",
1217
+ "additionalProperties": false,
1218
+ "properties": {
1219
+ "what": {
1220
+ "type": "string",
1221
+ "enum": ["before", "after", "video"]
1222
+ },
1223
+ "reason": {
1224
+ "type": "string"
1225
+ }
1226
+ },
1227
+ "required": ["what", "reason"]
1228
+ }
1229
+ }
1230
+ }
1004
1231
  }
1005
1232
  }
1006
1233
  }