@mmerterden/multi-agent-pipeline 15.10.0 → 15.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,23 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [15.11.0] - 2026-08-21
20
+
21
+ ### Changed
22
+ - **Nothing the pipeline posts carries a decorative glyph any more.** The work summary rendered task marks and a phase strip in checkmarks and hourglasses, the PR review emitter prefixed every finding with a coloured dot and signed it with a robot, and the GitHub issue Progress table was three traffic lights. All of it now reads in words: `[done]` / `[pending]` for tasks, `done · active · failed · skipped · pending` for the phase strip, `done` / `partial` / `pending` for the issue flags, and the severity is the bold label it always was. `channels/jira.md` had banned decorative glyphs in a comment body for releases while the renderers filled it with them; the rule and the emitters now agree, and `smoke-channel-glyphs.sh` holds them to it. The pipeline's own terminal output is deliberately out of scope: a `✓` in a console summary is a UI affordance, not a document somebody reads later.
23
+
24
+ ### Fixed
25
+ - **Jira no longer manufactures smileys the pipeline never typed.** Comments are posted as Jira wiki markup, and Jira's renderer converts `:)` `:D` `;)` and, far more easily hit in technical prose, `(x)` `(/)` `(!)` `(i)` `(y)` `(+)` `(on)` `(*)` into emoticon images at render time. Nothing escaped them. `channels/jira.md` now carries the escaping table and orders it after the markdown conversion and before the POST. This was never something the humanizer could fix: the text is legitimate, and `(x)` in a comparison table renders correctly on GitHub and Confluence - only the Jira conversion knows the target parser.
26
+
27
+ ### Tests
28
+ - `smoke-channel-glyphs.sh` (8 checks) and a glyph assertion in `smoke-work-summary.sh`. Both detect with node's `\p{Extended_Pictographic}` rather than `grep -P`: the first draft of the gate used a PCRE class, reported a clean tree with a checkmark deliberately planted in an emitter, and passed. Under `bash` on a stock macOS `grep` is BSD grep, which has no `-P` at all - it exits 2 with "invalid option", the `2>/dev/null` swallowed the message, and an empty result read as "no glyphs". The gate now opens by proving its own detector fires on a planted glyph before it trusts any verdict, and both gates were re-checked by planting one and watching them go red.
29
+
30
+ ## [15.10.1] - 2026-08-21
31
+
32
+ ### Fixed
33
+ - **`uninstall --all-data` now removes the per-repo memory root.** `~/.claude/memory/multi-agent/` holds the learnings ledger and triage corpus: durable knowledge written in prose about the repos it was collected from, which on a corporate checkout is concrete information about the codebase. It survived even the full-cleanup mode, so "removes everything but your tokens" was not true in the one place a user is most likely to mean it. The default run still keeps it, alongside settings and logs, and both halves of that promise are now tested.
34
+ - **Offloaded tool payloads are reclaimable.** v15.10.0 added `offload-ref.sh` without a way to clean up after it. In worktree modes the payloads die with the worktree, but the `--local` modes write into the real checkout, and because `.multi-agent/refs/` is gitignored the files never appear in `git status` and nothing reclaims them. New `gc-refs.sh`, dispatched as a third phase of `/multi-agent:garbage-collect`, sweeps them with the same contract as the /tmp sweeper: dry-run until `--yes`, a root guard that refuses `/` and `$HOME`, a grace window so a sweep cannot pull a ref out from under a running phase, and node-id matching so a file the user put in that directory survives. `--all` sweeps every checkout under `$HOME`.
35
+
19
36
  ## [15.10.0] - 2026-08-21
20
37
 
21
38
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "15.10.0",
3
+ "version": "15.11.0",
4
4
  "description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -93,12 +93,12 @@ When the preview does NOT render (no state, or pref explicitly off at global lev
93
93
  **Visual flow (TR, pref on + state available):**
94
94
 
95
95
  ```
96
- 📋 Yapılan iş özeti - PROJ-12345
96
+ Yapılan iş özeti - PROJ-12345
97
97
  ──────────────────────────────────────────────────────────
98
98
  {rendered Work Summary block - header + scope + diffstat + review + phases}
99
99
  ──────────────────────────────────────────────────────────
100
100
 
101
- İş özetini gördün. Şimdi nereye rapor gönderelim?
101
+ [done] İş özetini gördün. Şimdi nereye rapor gönderelim?
102
102
  ```
103
103
 
104
104
  Then 3b renders:
@@ -167,7 +167,7 @@ For each selected **content** source, produce one section body. Each section run
167
167
  | Auto-diff | Old enrich Output Template verbatim - Root Cause / Solution / Changed Files / Test Scenarios |
168
168
  | Manuel not | `### Notes` - user-provided paragraph, or LLM-split into subsections if `--message` is plain text and long |
169
169
  | Cost özeti | `### Cost Summary` - per-phase token tally + est. USD table. Source: `phase-tracker.sh` phase status + optional OTel spans. See "Cost summary generation" below. |
170
- | Yapılan iş özeti | `### Work Summary` - executive one-screen summary: task + branch + base + PR, scope delivered (✅/⏳ per Phase 2 task), changed files with +/- counts (capped at 20 rows), review outcome (accepted/deferred/rejected + approved), phase tick strip. Source: `agent-state.json` + `phase-tracker.json` + `git diff --numstat base...HEAD`. See "Work summary generation" below. |
170
+ | Yapılan iş özeti | `### Work Summary` - executive one-screen summary: task + branch + base + PR, scope delivered (done/[pending] per Phase 2 task), changed files with +/- counts (capped at 20 rows), review outcome (accepted/deferred/rejected + approved), phase tick strip. Source: `agent-state.json` + `phase-tracker.json` + `git diff --numstat base...HEAD`. See "Work summary generation" below. |
171
171
 
172
172
  **Output template (aggregated across selected content options):**
173
173
 
@@ -230,8 +230,8 @@ If a cached report exists AND any of the selected channels is PR / Jira / Conflu
230
230
 
231
231
  | Blocker count | Appended line |
232
232
  |---|---|
233
- | `errors > 0` | `### Store compliance` + `⚠️ {N} blocker(s), {M} warning(s) - run /multi-agent:test "store-ready" for the full report.` - per-blocker: `- \`{ruleID}\` - {Apple ITMS / Play ref}: {message}` (capped at 5 rows, rest collapsed to `+N more`) |
234
- | `errors == 0, warnings > 0` | `### Store compliance` + `✅ No blockers. {M} warning(s) - see /multi-agent:test "store-ready" for detail.` |
233
+ | `errors > 0` | `### Store compliance` + `{N} blocker(s), {M} warning(s) - run /multi-agent:test "store-ready" for the full report.` - per-blocker: `- \`{ruleID}\` - {Apple ITMS / Play ref}: {message}` (capped at 5 rows, rest collapsed to `+N more`) |
234
+ | `errors == 0, warnings > 0` | `### Store compliance` + `[done] No blockers. {M} warning(s) - see /multi-agent:test "store-ready" for detail.` |
235
235
  | `errors == 0, warnings == 0` | Skip section entirely - don't pollute PR body |
236
236
 
237
237
  The section references `ruleID` + policy ref from the compliance skill catalogs (`apple-archive-compliance` / `google-play-compliance`) verbatim. No translation - global identifiers stay as-is.
@@ -243,10 +243,10 @@ If no cached report exists, skip silently - this is augmentation, not a gate.
243
243
  Emitted only when `reportContent.workSummary === true` and at least one of (`agent-state.json`, `--branch` flag) is available. The shell adapter is `$HOME/.claude/scripts/render-work-summary.sh <taskId>`:
244
244
 
245
245
  1. **Task header** - `taskId`, `branch`, `baseBranch`, `prNumber` from `agent-state.json` (or explicit flags for post-hoc invocation).
246
- 2. **Scope delivered** - Phase 2 `planTodos[]` / `tasks[]` rendered as `✅` (status=done) or `⏳` (anything else) rows. Task id + title shown; `(deferred - rationale)` appended if the task's `status` is `"deferred"`.
246
+ 2. **Scope delivered** - Phase 2 `planTodos[]` / `tasks[]` rendered as `done` (status=done) or `pending` (anything else) rows. Task id + title shown; `(deferred - rationale)` appended if the task's `status` is `"deferred"`.
247
247
  3. **Changed files** - `git -C $WORKTREE diff --numstat $baseBranch...HEAD`. Shows `` `path` (+add / -del) `` per row, capped at 20 with a `_... +N more files not shown_` footer when exceeded. Total adds/dels + file count in section header.
248
248
  4. **Review outcome** - from `reviewConsensus` (pre-v6.1) or `phases["4"].triage`: `{accepted} accepted · {deferred} deferred · {rejected} rejected · approved={bool}`. Hidden entirely when all three buckets are empty (normal for `--dev` / `dev-autopilot` runs that skip Phase 4).
249
- 5. **Phase tick strip** - single line from the tracker state (`render-work-summary.sh` resolves worktree/artifacts copies, then `$HOME/.claude/logs/multi-agent/{taskId}/tracker-state.json`): `0 Init · 1 Analysis · 2 Planning · 3 Dev · 4 Review · 5 Test · 6 Commit · 7 Report ▶`. Marks: `✅` completed · `▶` in_progress · `❌` failed · `⏭` skipped · `·` pending.
249
+ 5. **Phase tick strip** - single line from the tracker state (`render-work-summary.sh` resolves worktree/artifacts copies, then `$HOME/.claude/logs/multi-agent/{taskId}/tracker-state.json`): `0 Init [done] · 1 Analysis [done] · 2 Planning [done] · 3 Dev [done] · 4 Review [done] · 5 Test skipped · 6 Commit [done] · 7 Report active`. Marks: `done` completed · `active` in_progress · `failed` failed · `skipped` skipped · `·` pending.
250
250
 
251
251
  **Output template:**
252
252
 
@@ -255,8 +255,8 @@ Emitted only when `reportContent.workSummary === true` and at least one of (`age
255
255
  **Task**: `PROJ-1099` | **Branch**: `bugfix/PROJ-1099-dark-mode` | **Base**: `develop` | **PR**: #104
256
256
 
257
257
  #### Scope delivered
258
- - T1 Add failing dark-mode snapshot test
259
- - T2 Replace Color.white with design token
258
+ - [done] T1 Add failing dark-mode snapshot test
259
+ - [done] T2 Replace Color.white with design token
260
260
 
261
261
  #### Changed files (3 files · +48 / -12)
262
262
  - `Sources/Auth/LoginView.swift` (+24 / -8)
@@ -267,7 +267,7 @@ Emitted only when `reportContent.workSummary === true` and at least one of (`age
267
267
  - 1 accepted · 0 deferred · 0 rejected · approved=true
268
268
 
269
269
  #### Phases
270
- - 0 Init · 1 Analysis · 2 Planning · 3 Dev · 4 Review · 5 Test · 6 Commit · 7 Report
270
+ - 0 Init [done] · 1 Analysis [done] · 2 Planning [done] · 3 Dev [done] · 4 Review [done] · 5 Test skipped · 6 Commit [done] · 7 Report active
271
271
  ```
272
272
 
273
273
  **Post-hoc invocation** (task already finished, agent-state.json may be archived): pass `--branch` + `--base-branch` explicitly - the script still emits task header + changed-files + phases sections (scope + review outcome are state-dependent and will be absent if state is gone).
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: "Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) and worktree residue (orphan .worktrees dirs, gitlink index entries) from past runs. Dry-run first; confirms before deleting. Use when scratch files from earlier runs need clearing out."
3
- description-tr: "Geçmiş çalışmalardan kalan /tmp scratch'ini (picker durumu, review diff'leri, kanal payload'ları, analiz taslakları) ve worktree kalıntılarını (sahipsiz .worktrees klasörleri, gitlink index kayıtları) süpürür. Önce dry-run; silmeden önce onay alır."
2
+ description: "Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts), worktree residue (orphan .worktrees dirs, gitlink index entries) and offloaded tool payloads under .multi-agent/refs/ from past runs. Dry-run first; confirms before deleting. Use when scratch files from earlier runs need clearing out."
3
+ description-tr: "Geçmiş çalışmalardan kalan /tmp scratch'ini (picker durumu, review diff'leri, kanal payload'ları, analiz taslakları), worktree kalıntılarını (sahipsiz .worktrees klasörleri, gitlink index kayıtları) ve .multi-agent/refs/ altındaki offload edilmiş araç çıktılarını süpürür. Önce dry-run; silmeden önce onay alır."
4
4
  argument-hint: "[--older-than=<minutes>] [--yes] - dry-run unless confirmed"
5
5
  ---
6
6
 
@@ -72,6 +72,31 @@ deletes nothing until you confirm.
72
72
  - On confirm, re-run with `--yes` and echo its summary line. If a gitlink
73
73
  was unstaged, remind the user the removal still needs a commit.
74
74
 
75
+ 7. **Offloaded payload sweep (third phase)** - when the current directory is a
76
+ git repo, also preview the offloaded tool payloads:
77
+
78
+ ```bash
79
+ bash $HOME/.claude/scripts/gc-refs.sh ${ARGUMENTS}
80
+ ```
81
+
82
+ `offload-ref.sh` parks build logs, diffs and test output under
83
+ `.multi-agent/refs/<node_id>.md`. In worktree modes that directory dies with
84
+ the worktree; in the `--local` modes it lands in the real checkout, and
85
+ because it is gitignored it never shows up in `git status`. Only files
86
+ matching the node-id shape are touched - anything else in that directory is
87
+ left alone, and files inside the grace window are spared so a sweep cannot
88
+ pull a ref out from under a running phase.
89
+
90
+ - Output says `nothing to do` -> skip silently, no question.
91
+ - Otherwise surface a third `AskUserQuestion` (in `outputLanguage`):
92
+ `question`: "Delete the listed offloaded payloads?" (include the count and
93
+ freed size), `header`: "Payloads", options `{ label: "Delete payloads" }` /
94
+ `{ label: "Cancel", description: "Keep everything, change nothing" }`.
95
+ - On confirm, re-run with `--yes` and echo its summary line.
96
+
97
+ Pass `--all` to sweep every checkout under `$HOME` rather than only the
98
+ current repo.
99
+
75
100
  ## Notes
76
101
 
77
102
  - An in-flight analysis keeps its drafts in `/tmp/analysis-<slug>-<timestamp>/`
@@ -79,6 +104,10 @@ deletes nothing until you confirm.
79
104
  `awaiting_output_decision`, pass `--older-than=<minutes>` so fresh drafts are
80
105
  spared, or finish the run first.
81
106
  - The /tmp phase only touches `/tmp` scratch; the worktree phase only touches
82
- the current repo's `.worktrees/` residue and index. Persistent per-task logs
107
+ the current repo's `.worktrees/` residue and index; the payload phase only
108
+ touches node-id files under `.multi-agent/refs/`. Persistent per-task logs
83
109
  live under `~/.claude/logs/multi-agent/` and are cleaned by
84
- `/multi-agent:prune-logs`.
110
+ `/multi-agent:prune-logs`. The per-repo memory stores under
111
+ `~/.claude/memory/multi-agent/` are durable knowledge, not scratch: they are
112
+ removed only by `/multi-agent:uninstall --all-data`, or one entry at a time
113
+ with `learnings-ledger.mjs forget`.
@@ -119,12 +119,7 @@ finding_fingerprint() {
119
119
  render_inline_body() {
120
120
  local sev="$1" issue="$2" fix="$3" rule_id="$4" fingerprint="${5:-}"
121
121
 
122
- local emoji label_word fix_label
123
- case "$sev" in
124
- blocking) emoji="🔴" ;;
125
- important) emoji="🟡" ;;
126
- *) emoji="⚪" ;; # never reached; suggestions never post
127
- esac
122
+ local label_word fix_label
128
123
 
129
124
  if [ "$OUT_LANG" = "tr" ]; then
130
125
  case "$sev" in
@@ -140,14 +135,17 @@ render_inline_body() {
140
135
  fix_label="Suggestion"
141
136
  fi
142
137
 
143
- printf '%s **%s** - %s\n\n' "$emoji" "$label_word" "$issue"
138
+ # The severity is the bold label, not a coloured dot. A review comment is
139
+ # read as technical prose, and the glyph carried no information the word did
140
+ # not already carry.
141
+ printf '**%s** - %s\n\n' "$label_word" "$issue"
144
142
  if [ -n "$fix" ] && [ "$fix" != "null" ]; then
145
143
  printf '**%s:** %s\n\n' "$fix_label" "$fix"
146
144
  fi
147
145
  if [ -n "$rule_id" ] && [ "$rule_id" != "null" ]; then
148
146
  printf '_%s_\n\n' "$rule_id"
149
147
  fi
150
- printf -- '---\n🤖 _Multi-Agent Review · iteration #%s_\n' "$ITERATION"
148
+ printf -- '---\n_Multi-Agent Review · iteration #%s_\n' "$ITERATION"
151
149
  # Dedupe marker - dedupe-style. Re-runs of /multi-agent:review skip a
152
150
  # finding when an existing comment carries the same fingerprint.
153
151
  if [ -n "$fingerprint" ]; then
@@ -152,6 +152,6 @@ Successful POST → URL prepended to `prefs.projects[<project>].confluenceUrls`
152
152
  - Section order is fixed: `overview` → `flow` (cond.) → `technical_details` → `api_contracts` (cond.) → `references` (cond.). Conditional sections may be omitted but never reordered.
153
153
  - Humanizer pass runs **after** body assembly and **before** storage-XML conversion. Tone: formal, stakeholder-readable. No "we are excited", no first-person plural.
154
154
  - Body content language follows `prefs.global.outputLanguage`. Code identifiers, API paths, file paths, type names, JSON keys, and the storage-XML markup stay verbatim. The template (this doc) is English because `promptLanguage="en"` is locked.
155
- - No decorative/emotive emoji or smileys (😊 🙂 🎉 👍 🚀 ✨) anywhere in the page body. Stakeholder-readable technical prose only.
155
+ - No decorative/emotive emoji or smileys ( ) anywhere in the page body. Stakeholder-readable technical prose only.
156
156
  - Adapter failures are non-blocking. 401 / network timeout / parent-not-found → `failed` status, loop continues.
157
157
  - If `--dry-run`, print the assembled storage XML to stdout and return without POST.
@@ -85,7 +85,7 @@ Ref: #<issueNumber>
85
85
 
86
86
  - No "Co-Authored-By: Claude" trailer. No "🤖 Generated with ..." footer. No mention of Claude / Copilot / AI / model names anywhere in the body.
87
87
  - No em-dashes (` - `) in prose. Use ` · ` or `→` per `feedback_no_em_dash.md`.
88
- - No decorative/emotive emoji or smileys (😊 🙂 🎉 👍 🚀 ✨) in the body prose. Only a fixed-template functional mark (e.g. a `✅/⏳` scope tick) is allowed; free text carries none.
88
+ - No decorative/emotive emoji or smileys ( ) in the body prose. Only a fixed-template functional mark (e.g. a `done/pending` scope tick) is allowed; free text carries none.
89
89
  - No HTML entities. Use raw markdown.
90
90
  - No links to internal `/tmp/*` or local-machine paths.
91
91
  - No screenshots embedded as base64 - link to wiki / Figma instead.
@@ -77,6 +77,22 @@ Body markdown is rewritten to Jira wiki markup before POST:
77
77
 
78
78
  The `\|` sequences in the two link/table rows are this table's own escape for a literal pipe - the emitted Jira markup carries `|`, never `\|`.
79
79
 
80
+ ### Emoticon escaping (required)
81
+
82
+ Jira's wiki renderer turns ASCII sequences into emoticon images. This is not something the pipeline writes; Jira manufactures it at render time, which is why a comment can show a smiley nobody typed. Escape every sequence below with a leading backslash before POST:
83
+
84
+ | Sequence | Renders as | Escaped |
85
+ |---|---|---|
86
+ | `:)` `:(` `:P` `:D` `;)` | face icons | `\:)` `\:(` `\:P` `\:D` `\;)` |
87
+ | `(y)` `(n)` | thumbs | `\(y)` `\(n)` |
88
+ | `(i)` `(?)` `(!)` | info / question / warning icons | `\(i)` `\(?)` `\(!)` |
89
+ | `(/)` `(x)` | tick / cross icons | `\(/)` `\(x)` |
90
+ | `(+)` `(-)` | plus / minus icons | `\(+)` `\(-)` |
91
+ | `(on)` `(off)` | lightbulb icons | `\(on)` `\(off)` |
92
+ | `(*)` `(*r)` `(*g)` `(*b)` `(*y)` | star icons | `\(*)` `\(*r)` ... |
93
+
94
+ The parenthesised forms are the ones that actually bite: `(x)` in a comparison table, `(!)` in a caution note, and `(/)` in a path fragment are ordinary technical prose, and each becomes an image. Apply the escape AFTER the markdown conversion above and BEFORE the POST, and skip it inside `{code}` / `{noformat}` blocks, where Jira does not expand emoticons anyway.
95
+
80
96
  Lines outside these patterns pass through verbatim. Multi-paragraph blocks are joined with one blank line.
81
97
 
82
98
  Every row above is load-bearing, including the ones that look cosmetic. The table must cover each construct the section templates in this file actually emit - `##` for the three required headings, `**Given**` / `**When**` / `**Then**` in the test-scenario skeleton, and `1.`-numbered scenarios. A missing row does not degrade gracefully: the "pass through verbatim" fallback POSTs `## Test Senaryoları` and `**Given**` as literal text, so the comment renders with visible `##` and stray asterisks. Single `*bold*` in Markdown means *italic* in Jira wiki - never map `**bold**` to `*bold*` by dropping one asterisk mechanically without checking the source was bold, not italic.
@@ -135,6 +151,6 @@ When the **Wiki** adapter writes pages on the same run AND `prefs.global.wikiToJ
135
151
  - UTF-8 in, UTF-8 out - the body file is UTF-8 and `--data-binary` ships its bytes verbatim. Never round-trip the body through `unicode_escape`, `latin-1`, or any re-encode step, and never hand-roll a Python/curl helper that re-decodes it: that mangles Turkish chars (ç ş ı ö ü ğ) into mojibake (`Çözüm` → `Ãözüm`). Use the `jq --rawfile` + `--data-binary @file` path above as-is. Same rule for the PR / Confluence / Wiki adapters.
136
152
  - Section order is fixed: `summary` → `test_scenarios` → `context_refs`. Never insert sections between them; never reorder.
137
153
  - Humanizer pass runs **after** body assembly and **before** wiki-markup conversion. Tone target: informal but technical. No marketing voice, no "we are excited", no "I have...".
138
- - **No decorative/emotive emoji or smileys** (😊 🙂 🎉 👍 🚀 ✨) anywhere in the comment body. The comment is plain technical prose; emoji do not belong in it.
154
+ - **No decorative glyphs anywhere in the comment body** - neither emotive ( ) nor status glyphs ([done] [pending] failed skipped active ). The comment is plain technical prose. Status is written in words: `[done]` / `[pending]`, and `done · active · failed · skipped · pending` in the phase strip. This applies to every channel, not only Jira - see `channels/README` note in `phase-7-report.md`.
139
155
  - Body content language follows `prefs.global.outputLanguage`. Code identifiers, file paths, type names, branch names, and the wiki-markup syntax stay verbatim. The `promptLanguage="en"` lock means any LLM prompt that produces the body is in English; the body itself is then rendered in the user's language.
140
156
  - Adapter failures are non-blocking - a missing token or 401 returns `skipped`/`failed`, the loop keeps going for PR / Confluence / Wiki.
@@ -32,7 +32,7 @@ Body language follows `prefs.global.outputLanguage`. Both forms below are 1:1 -
32
32
  ### Turkish (outputLanguage = "tr")
33
33
 
34
34
  ```markdown
35
- {severityEmoji} **{severityLabelTr}** - {issueTr}
35
+ **{severityLabelTr}** - {issueTr}
36
36
 
37
37
  **Öneri:** {fixTr}
38
38
 
@@ -40,29 +40,29 @@ Body language follows `prefs.global.outputLanguage`. Both forms below are 1:1 -
40
40
  _apple-archive-compliance / no-force-unwrap - Guideline 2.1.1_
41
41
 
42
42
  ---
43
- 🤖 _Multi-Agent Review · iteration #{iterationNumber}_
43
+ _Multi-Agent Review · iteration #{iterationNumber}_
44
44
  ```
45
45
 
46
46
  Severity labels (TR):
47
- - `blocking` → `🔴 **Blocker**`
48
- - `important` → `🟡 **Önemli**`
47
+ - `blocking` → `**Blocker**`
48
+ - `important` → `**Önemli**`
49
49
 
50
50
  ### English (outputLanguage = "en")
51
51
 
52
52
  ```markdown
53
- {severityEmoji} **{severityLabelEn}** - {issueEn}
53
+ **{severityLabelEn}** - {issueEn}
54
54
 
55
55
  **Suggestion:** {fixEn}
56
56
 
57
57
  {Optional rule reference}
58
58
 
59
59
  ---
60
- 🤖 _Multi-Agent Review · iteration #{iterationNumber}_
60
+ _Multi-Agent Review · iteration #{iterationNumber}_
61
61
  ```
62
62
 
63
63
  Severity labels (EN):
64
- - `blocking` → `🔴 **Blocker**`
65
- - `important` → `🟡 **Important**`
64
+ - `blocking` → `**Blocker**`
65
+ - `important` → `**Important**`
66
66
 
67
67
  ### Anchoring
68
68
 
@@ -63,5 +63,5 @@ Adapter implementation chooses the right git push target + commit message format
63
63
  - Per the `figma-pipeline.md` rules, every component wiki page must include a variant matrix, accessibility identifiers, analytics events, and 3-layer test summary.
64
64
  - Screenshots cover light/dark + LTR/RTL - wiki adapter refuses to commit if any quadrant is missing.
65
65
  - Body content language follows `prefs.global.outputLanguage`. Code identifiers, file paths, type names, design token names, and Markdown formatting stay verbatim across languages (wiki pages are `.md` files - "wiki markup" in this doc set means Jira's dialect, which never appears here). The template (this doc) is English because `promptLanguage="en"` is locked.
66
- - No decorative/emotive emoji or smileys (😊 🙂 🎉 👍 🚀 ✨) in the page prose. Only functional/structural marks a fixed template defines are allowed.
66
+ - No decorative/emotive emoji or smileys ( ) in the page prose. Only functional/structural marks a fixed template defines are allowed.
67
67
  - Autopilot always pauses at the channels menu (per `phase-7-report.md` autopilot contract) - even in autopilot mode the user gets to confirm wiki scope.
@@ -129,7 +129,7 @@ The Progress flag values come from `state.flags`:
129
129
  | `codeConnect` | Code Connect |
130
130
  | `wiki` | Wiki |
131
131
 
132
- Each value is `true` (🟢), `"yellow"` (🟡), or `false`/missing (⚪). Phase 3 / 5 / 6 are responsible for setting these honestly - Phase 7 only mirrors what's already in state.
132
+ Each value renders as `done` (`true`), `partial` (`"yellow"`), or `pending` (`false`/missing). Phase 3 / 5 / 6 are responsible for setting these honestly - Phase 7 only mirrors what's already in state.
133
133
 
134
134
  **Hard rule (per `$HOME/.claude/multi-agent-refs/channels/issue-comment.md`):** never run only one of the two actions. The comment is the human-readable audit; flags are the machine-readable status. Either both or neither.
135
135
 
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env bash
2
+ # gc-refs.sh - remove offloaded tool payloads left behind in a repo checkout.
3
+ #
4
+ # `offload-ref.sh` parks build logs, diffs and test output under
5
+ # <root>/.multi-agent/refs/<node_id>.md so a phase prompt can carry a pointer
6
+ # instead of the whole log. In worktree modes that directory dies with the
7
+ # worktree. In the --local modes there is no worktree: the refs land in the
8
+ # real checkout and nothing ever removes them. They are gitignored, so they are
9
+ # invisible to `git status` and grow without bound.
10
+ #
11
+ # This sweeps them, with the same safety contract as gc-tmp.sh:
12
+ # - dry-run by default: lists what WOULD be removed and the space it frees,
13
+ # and deletes nothing until --yes
14
+ # - root guard: refuses a root that is not a directory, and refuses $HOME
15
+ # and / outright, so a stray GC_REFS_ROOT cannot sweep a home directory
16
+ # - fresh-payload grace: files touched in the last GC_REFS_GRACE_MIN minutes
17
+ # (default 10) are spared, so a sweep during an in-flight run cannot pull a
18
+ # ref out from under the agent that is about to read it
19
+ #
20
+ # Only files matching the node-id shape are touched; anything else a user put
21
+ # in that directory is left alone.
22
+ #
23
+ # Usage:
24
+ # gc-refs.sh # dry-run over the current repo
25
+ # gc-refs.sh --yes # actually delete
26
+ # gc-refs.sh --older-than=1440 # only payloads older than a day
27
+ # gc-refs.sh --all # every repo under GC_REFS_SEARCH (default $HOME)
28
+ #
29
+ # Env:
30
+ # GC_REFS_ROOT override the repo root (default: git toplevel, else $PWD)
31
+ # GC_REFS_SEARCH where --all looks for checkouts (default $HOME)
32
+ # GC_REFS_GRACE_MIN grace window in minutes (default 10, 0 = off)
33
+ #
34
+ # Exit: 0 on success or nothing to do, 2 on usage error or a refused root.
35
+
36
+ set -uo pipefail
37
+
38
+ GRACE_MIN="${GC_REFS_GRACE_MIN:-10}"
39
+ SEARCH_ROOT="${GC_REFS_SEARCH:-$HOME}"
40
+ DELETE=0
41
+ OLDER_MIN=0
42
+ ALL=0
43
+
44
+ usage() {
45
+ grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'
46
+ }
47
+
48
+ for arg in "$@"; do
49
+ case "$arg" in
50
+ --yes | --force) DELETE=1 ;;
51
+ --all) ALL=1 ;;
52
+ --older-than=*)
53
+ OLDER_MIN="${arg#*=}"
54
+ if ! printf '%s' "$OLDER_MIN" | grep -qE '^[0-9]+$'; then
55
+ echo "gc-refs: --older-than needs a whole number of minutes, got: $OLDER_MIN" >&2
56
+ exit 2
57
+ fi
58
+ ;;
59
+ -h | --help)
60
+ usage
61
+ exit 0
62
+ ;;
63
+ *)
64
+ echo "gc-refs: unknown argument: $arg" >&2
65
+ exit 2
66
+ ;;
67
+ esac
68
+ done
69
+
70
+ if ! printf '%s' "$GRACE_MIN" | grep -qE '^[0-9]+$'; then
71
+ echo "gc-refs: GC_REFS_GRACE_MIN needs a whole number of minutes, got: $GRACE_MIN" >&2
72
+ exit 2
73
+ fi
74
+
75
+ # Collect the refs directories to consider.
76
+ REFS_DIRS=""
77
+ if [ "$ALL" -eq 1 ]; then
78
+ if [ ! -d "$SEARCH_ROOT" ]; then
79
+ echo "gc-refs: no search root at $SEARCH_ROOT - nothing to do"
80
+ exit 0
81
+ fi
82
+ # Prune the trees that are large and never hold a pipeline checkout, so a
83
+ # home-wide sweep stays seconds rather than minutes.
84
+ REFS_DIRS=$(find "$SEARCH_ROOT" \
85
+ \( -name node_modules -o -name Pods -o -name .build -o -name DerivedData -o -name .next \) -prune \
86
+ -o -type d -path "*/.multi-agent/refs" -print 2>/dev/null)
87
+ else
88
+ ROOT="${GC_REFS_ROOT:-}"
89
+ if [ -z "$ROOT" ]; then
90
+ ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
91
+ [ -n "$ROOT" ] || ROOT="$PWD"
92
+ fi
93
+ # Root guard: never accept a root that resolves to / or $HOME. A sweep is
94
+ # recursive and a mistyped override should not be able to reach a home dir.
95
+ case "$ROOT" in
96
+ / | "$HOME")
97
+ echo "gc-refs: refusing to sweep '$ROOT' - point GC_REFS_ROOT at a repo checkout" >&2
98
+ exit 2
99
+ ;;
100
+ esac
101
+ [ -d "$ROOT" ] || {
102
+ echo "gc-refs: no directory at $ROOT - nothing to do"
103
+ exit 0
104
+ }
105
+ [ -d "$ROOT/.multi-agent/refs" ] && REFS_DIRS="$ROOT/.multi-agent/refs"
106
+ fi
107
+
108
+ if [ -z "$REFS_DIRS" ]; then
109
+ echo "gc-refs: no offloaded payloads found - nothing to do"
110
+ exit 0
111
+ fi
112
+
113
+ # Match the node-id shape offload-ref.sh writes (p<phase>-<slug>-<8 hex>.md).
114
+ # Anything else in that directory belongs to someone else.
115
+ NODE_GLOB='p*-*-*.md'
116
+
117
+ MATCHES=""
118
+ while IFS= read -r dir; do
119
+ [ -n "$dir" ] || continue
120
+ # `-mmin +0` is not a no-op: it means "older than a minute" and would drop a
121
+ # file written seconds ago. The age filter is applied only when one was asked
122
+ # for; the grace window below is what protects an in-flight payload.
123
+ if [ "$OLDER_MIN" -gt 0 ]; then
124
+ found=$(find "$dir" -maxdepth 1 -type f -name "$NODE_GLOB" -mmin +"$OLDER_MIN" 2>/dev/null)
125
+ else
126
+ found=$(find "$dir" -maxdepth 1 -type f -name "$NODE_GLOB" 2>/dev/null)
127
+ fi
128
+ [ -n "$found" ] || continue
129
+ while IFS= read -r f; do
130
+ [ -n "$f" ] || continue
131
+ # Grace: skip anything touched inside the window.
132
+ if [ "$GRACE_MIN" -gt 0 ] && [ -n "$(find "$f" -mmin -"$GRACE_MIN" 2>/dev/null)" ]; then
133
+ continue
134
+ fi
135
+ MATCHES="$MATCHES$f
136
+ "
137
+ done <<EOF
138
+ $found
139
+ EOF
140
+ done <<EOF
141
+ $REFS_DIRS
142
+ EOF
143
+
144
+ MATCHES=$(printf '%s' "$MATCHES" | grep -v '^$' || true)
145
+ if [ -z "$MATCHES" ]; then
146
+ echo "gc-refs: no offloaded payloads outside the grace window - nothing to do"
147
+ exit 0
148
+ fi
149
+
150
+ COUNT=$(printf '%s\n' "$MATCHES" | grep -c .)
151
+ BYTES=0
152
+ while IFS= read -r f; do
153
+ [ -n "$f" ] || continue
154
+ sz=$(wc -c < "$f" 2>/dev/null | tr -d ' ')
155
+ BYTES=$((BYTES + ${sz:-0}))
156
+ done <<EOF
157
+ $MATCHES
158
+ EOF
159
+ KB=$(((BYTES + 1023) / 1024))
160
+
161
+ if [ "$DELETE" -eq 0 ]; then
162
+ echo "gc-refs: would remove $COUNT offloaded payload(s), freeing ${KB} KB"
163
+ printf '%s\n' "$MATCHES" | sed 's/^/ /'
164
+ echo ""
165
+ echo "Nothing deleted. Re-run with --yes to remove them."
166
+ exit 0
167
+ fi
168
+
169
+ removed=0
170
+ while IFS= read -r f; do
171
+ [ -n "$f" ] || continue
172
+ rm -f "$f" && removed=$((removed + 1))
173
+ done <<EOF
174
+ $MATCHES
175
+ EOF
176
+
177
+ # Drop a refs directory that is now empty; leave one that still holds
178
+ # something we did not put there.
179
+ while IFS= read -r dir; do
180
+ [ -n "$dir" ] || continue
181
+ rmdir "$dir" 2>/dev/null || true
182
+ done <<EOF
183
+ $REFS_DIRS
184
+ EOF
185
+
186
+ echo "gc-refs: removed $removed offloaded payload(s), freed ${KB} KB"
187
+ exit 0
@@ -22,9 +22,9 @@
22
22
  # **Task**: <jiraId> | **Branch**: <branch> | **Base**: <base> | **PR**: #<N>
23
23
  #
24
24
  # #### Scope delivered
25
- # - T1 <title>
26
- # - T2 <title>
27
- # - T3 <title> (deferred - rationale)
25
+ # - [done] T1 <title>
26
+ # - [done] T2 <title>
27
+ # - [pending] T3 <title> (deferred - rationale)
28
28
  #
29
29
  # #### Changed files (<N> files · +<ADD> / -<DEL>)
30
30
  # - `path/a.ext` (+12 / -4)
@@ -34,7 +34,7 @@
34
34
  # - <A> accepted · <D> deferred · <R> rejected · approved=<bool>
35
35
  #
36
36
  # #### Phases
37
- # - 0 Init · 1 Analysis · 2 Planning · 3 Dev · 4 Review · 5 Test · 6 Commit · 7 Report
37
+ # - 0 Init done · 1 Analysis done · 2 Planning done · 3 Dev done · 4 Review done · 5 Test done · 6 Commit done · 7 Report active
38
38
  #
39
39
  # Exit codes: 0 = rendered, 2 = missing state (caller should skip section).
40
40
 
@@ -176,11 +176,11 @@ if [ -n "$TRACKER_FILE" ]; then
176
176
  | map(. as $i |
177
177
  ($labels[$i | tonumber]) as $label |
178
178
  ($ph[$i] // {}) as $p |
179
- (if $p.status == "completed" then ""
180
- elif $p.status == "in_progress" then ""
181
- elif $p.status == "failed" then ""
182
- elif $p.status == "skipped" then ""
183
- else "·" end) as $mark |
179
+ (if $p.status == "completed" then "done"
180
+ elif $p.status == "in_progress" then "active"
181
+ elif $p.status == "failed" then "failed"
182
+ elif $p.status == "skipped" then "skipped"
183
+ else "pending" end) as $mark |
184
184
  "\($label) \($mark)")
185
185
  | join(" · ")
186
186
  ' "$TRACKER_FILE" 2>/dev/null || echo "")
@@ -223,16 +223,22 @@ if [ -n "$DIFF_IN" ]; then
223
223
  fi
224
224
  fi
225
225
 
226
- # Task list with / marks. plan-todos.schema.json's todo items carry
227
- # `.task` (not `.title`/`.subject`) and a status enum of
226
+ # Task list with [done] / [pending] marks. plan-todos.schema.json's todo items
227
+ # carry `.task` (not `.title`/`.subject`) and a status enum of
228
228
  # pending|in_progress|completed|skipped|failed (not "done"/"deferred") -
229
- # the old check here never matched real data, so every todo rendered ⏳.
229
+ # the old check here never matched real data, so every todo rendered pending.
230
+ #
231
+ # The marks are words, not check/hourglass glyphs. This summary is section 1 of
232
+ # the Jira comment, and channels/jira.md bans decorative glyphs in a comment
233
+ # body while this renderer was filling it with them. Words also survive every
234
+ # adapter unchanged: `- [x] ` is rewritten to a numbered list by the Jira
235
+ # markdown->wiki conversion, which would have dropped the distinction entirely.
230
236
  tasks_block=""
231
237
  if [ "$(jq 'length' <<< "$state_tasks")" != "0" ]; then
232
238
  tasks_block=$(jq -r '
233
239
  .[] |
234
240
  "- " +
235
- (if (.status // "pending") == "completed" then " " else " " end) +
241
+ (if (.status // "pending") == "completed" then "[done] " else "[pending] " end) +
236
242
  (.id // "") +
237
243
  (if (.id // "") != "" then " " else "" end) +
238
244
  (.task // "(untitled)")
@@ -31,6 +31,7 @@
31
31
  * Preserved by default, removed with --all-data:
32
32
  * - $HOME/.claude/multi-agent-preferences.json (pipeline settings)
33
33
  * - $HOME/.claude/logs/multi-agent/ (task logs, state, metrics, audit trail)
34
+ * - $HOME/.claude/memory/multi-agent/ (per-repo learnings ledger + triage corpus)
34
35
  *
35
36
  * @module pipeline/scripts/runtime/uninstall
36
37
  */
@@ -635,6 +636,7 @@ export async function main() {
635
636
  console.log(" FULL CLEANUP (--all-data) also removes:");
636
637
  console.log(" - ~/.claude/multi-agent-preferences.json (pipeline settings)");
637
638
  console.log(" - ~/.claude/logs/multi-agent/ (task logs, state, metrics, audit trail)");
639
+ console.log(" - ~/.claude/memory/multi-agent/ (per-repo learnings ledger + triage corpus)");
638
640
  console.log("");
639
641
  }
640
642
  console.log(" PRESERVED (never touched):");
@@ -733,14 +735,22 @@ export async function main() {
733
735
  stripManagedBlock(join(COP, "copilot-instructions.md"));
734
736
  }
735
737
 
736
- // Full cleanup: pipeline settings + the multi-agent log root. Both are
737
- // pipeline-generated data (not user-authored like CLAUDE.md / rules), so
738
- // --all-data removes them; the default run leaves them in place.
738
+ // Full cleanup: pipeline settings, the log root, and the per-repo memory
739
+ // stores. All three are pipeline-generated data (not user-authored like
740
+ // CLAUDE.md / rules), so --all-data removes them; the default run leaves them
741
+ // in place.
742
+ //
743
+ // The memory root was missing here, and it is the one a user is most likely
744
+ // to mean by "remove everything": the learnings ledger and triage corpus hold
745
+ // architectural facts and past review findings written in prose about the
746
+ // repos they were collected from. On a corporate checkout that is concrete
747
+ // information about the codebase, and it survived even --all-data.
739
748
  if (allData && HOME) {
740
749
  console.log("");
741
- console.log(" [Full cleanup] Removing pipeline settings + logs...");
750
+ console.log(" [Full cleanup] Removing pipeline settings, logs + memory...");
742
751
  rmIfExists(join(HOME, ".claude", "multi-agent-preferences.json"));
743
752
  rmIfExists(join(HOME, ".claude", "logs", "multi-agent"));
753
+ rmIfExists(join(HOME, ".claude", "memory", "multi-agent"));
744
754
  }
745
755
 
746
756
  // Legacy adapter-file cleanup (adapters removed in v10.7.0). These blocks
@@ -818,7 +828,7 @@ export async function main() {
818
828
  if (!allData) {
819
829
  console.log("");
820
830
  console.log(
821
- " Kept your pipeline settings + logs. To remove those too, re-run with --all-data.",
831
+ " Kept your pipeline settings, logs + per-repo memory. To remove those too, re-run with --all-data.",
822
832
  );
823
833
  } else {
824
834
  console.log("");
@@ -82,7 +82,7 @@ if [ "$TASK_ID" = "--rewrite" ]; then
82
82
  echo "usage: update-issue-progress.sh --rewrite <body-file>" >&2
83
83
  exit 64
84
84
  fi
85
- rewrite_progress_body "$(build_progress_block "" "" "" "")" "$BODY_IN"
85
+ rewrite_progress_body "$(build_progress_block "pending" "pending" "pending" "pending")" "$BODY_IN"
86
86
  exit 0
87
87
  fi
88
88
 
@@ -122,9 +122,9 @@ F_WIKI=$(jq -r '.flags.wiki // false' "$AGENT_STATE")
122
122
  # Each flag may be: true | false | "yellow" (validated against older impl).
123
123
  glyph_for() {
124
124
  case "$1" in
125
- true) echo "🟢" ;;
126
- yellow) echo "🟡" ;;
127
- *) echo "" ;;
125
+ true) echo "done" ;;
126
+ yellow) echo "partial" ;;
127
+ *) echo "pending" ;;
128
128
  esac
129
129
  }
130
130