@mmerterden/multi-agent-pipeline 15.10.1 → 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,17 @@ 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
+
19
30
  ## [15.10.1] - 2026-08-21
20
31
 
21
32
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "15.10.1",
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).
@@ -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
 
@@ -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)")
@@ -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