@mizyoel/mercury-mesh 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.copilot/mcp-config.json +14 -0
  2. package/.copilot/skills/agent-collaboration/SKILL.md +42 -0
  3. package/.copilot/skills/agent-conduct/SKILL.md +24 -0
  4. package/.copilot/skills/architectural-proposals/SKILL.md +151 -0
  5. package/.copilot/skills/ci-validation-gates/SKILL.md +84 -0
  6. package/.copilot/skills/cli-wiring/SKILL.md +47 -0
  7. package/.copilot/skills/client-compatibility/SKILL.md +89 -0
  8. package/.copilot/skills/cross-mesh/SKILL.md +114 -0
  9. package/.copilot/skills/distributed-mesh/SKILL.md +287 -0
  10. package/.copilot/skills/distributed-mesh/mesh.json.example +30 -0
  11. package/.copilot/skills/distributed-mesh/sync-mesh.ps1 +111 -0
  12. package/.copilot/skills/distributed-mesh/sync-mesh.sh +104 -0
  13. package/.copilot/skills/docs-standards/SKILL.md +71 -0
  14. package/.copilot/skills/economy-mode/SKILL.md +101 -0
  15. package/.copilot/skills/external-comms/SKILL.md +331 -0
  16. package/.copilot/skills/gh-auth-isolation/SKILL.md +183 -0
  17. package/.copilot/skills/git-workflow/SKILL.md +206 -0
  18. package/.copilot/skills/github-multi-account/SKILL.md +95 -0
  19. package/.copilot/skills/history-hygiene/SKILL.md +36 -0
  20. package/.copilot/skills/humanizer/SKILL.md +107 -0
  21. package/.copilot/skills/init-mode/SKILL.md +101 -0
  22. package/.copilot/skills/mesh-conventions/SKILL.md +69 -0
  23. package/.copilot/skills/model-selection/SKILL.md +139 -0
  24. package/.copilot/skills/nap/SKILL.md +24 -0
  25. package/.copilot/skills/personal-mesh/SKILL.md +57 -0
  26. package/.copilot/skills/project-conventions/SKILL.md +56 -0
  27. package/.copilot/skills/release-process/SKILL.md +435 -0
  28. package/.copilot/skills/reskill/SKILL.md +92 -0
  29. package/.copilot/skills/reviewer-protocol/SKILL.md +79 -0
  30. package/.copilot/skills/secret-handling/SKILL.md +200 -0
  31. package/.copilot/skills/session-recovery/SKILL.md +155 -0
  32. package/.copilot/skills/test-discipline/SKILL.md +37 -0
  33. package/.copilot/skills/windows-compatibility/SKILL.md +74 -0
  34. package/.github/agents/mercury-mesh.agent.md +1732 -0
  35. package/.mesh/manifesto.md +66 -0
  36. package/.mesh/templates/casting/Futurama.json +10 -0
  37. package/.mesh/templates/casting-history.json +4 -0
  38. package/.mesh/templates/casting-policy.json +37 -0
  39. package/.mesh/templates/casting-reference.md +104 -0
  40. package/.mesh/templates/casting-registry.json +3 -0
  41. package/.mesh/templates/ceremonies.md +41 -0
  42. package/.mesh/templates/charter.md +56 -0
  43. package/.mesh/templates/constraint-tracking.md +38 -0
  44. package/.mesh/templates/cooperative-rate-limiting.md +229 -0
  45. package/.mesh/templates/copilot-instructions.md +50 -0
  46. package/.mesh/templates/department-backlog.md +15 -0
  47. package/.mesh/templates/department-charter.md +27 -0
  48. package/.mesh/templates/department-state.json +19 -0
  49. package/.mesh/templates/history.md +10 -0
  50. package/.mesh/templates/identity/now.md +9 -0
  51. package/.mesh/templates/identity/wisdom.md +15 -0
  52. package/.mesh/templates/interface-contract.md +26 -0
  53. package/.mesh/templates/issue-lifecycle.md +421 -0
  54. package/.mesh/templates/keda-scaler.md +166 -0
  55. package/.mesh/templates/machine-capabilities.md +77 -0
  56. package/.mesh/templates/mcp-config.md +90 -0
  57. package/.mesh/templates/mercury-mesh.agent.md +1732 -0
  58. package/.mesh/templates/multi-agent-format.md +28 -0
  59. package/.mesh/templates/orchestration-log.md +27 -0
  60. package/.mesh/templates/org-autonomy-spec.md +152 -0
  61. package/.mesh/templates/org-backlog-from-triage.js +199 -0
  62. package/.mesh/templates/org-runtime-reconcile.js +364 -0
  63. package/.mesh/templates/org-seed-runtime.js +238 -0
  64. package/.mesh/templates/org-status.js +193 -0
  65. package/.mesh/templates/org-structure.json +38 -0
  66. package/.mesh/templates/package.json +3 -0
  67. package/.mesh/templates/plugin-marketplace.md +49 -0
  68. package/.mesh/templates/ralph-circuit-breaker.md +313 -0
  69. package/.mesh/templates/ralph-triage.js +844 -0
  70. package/.mesh/templates/raw-agent-output.md +37 -0
  71. package/.mesh/templates/roster.md +60 -0
  72. package/.mesh/templates/routing.md +78 -0
  73. package/.mesh/templates/run-output.md +50 -0
  74. package/.mesh/templates/schedule.json +64 -0
  75. package/.mesh/templates/scribe-charter.md +119 -0
  76. package/.mesh/templates/skill.md +24 -0
  77. package/.mesh/templates/skills/agent-collaboration/SKILL.md +42 -0
  78. package/.mesh/templates/skills/agent-conduct/SKILL.md +24 -0
  79. package/.mesh/templates/skills/architectural-proposals/SKILL.md +151 -0
  80. package/.mesh/templates/skills/ci-validation-gates/SKILL.md +84 -0
  81. package/.mesh/templates/skills/cli-wiring/SKILL.md +47 -0
  82. package/.mesh/templates/skills/client-compatibility/SKILL.md +89 -0
  83. package/.mesh/templates/skills/cross-mesh/SKILL.md +114 -0
  84. package/.mesh/templates/skills/distributed-mesh/SKILL.md +287 -0
  85. package/.mesh/templates/skills/distributed-mesh/mesh.json.example +30 -0
  86. package/.mesh/templates/skills/distributed-mesh/sync-mesh.ps1 +111 -0
  87. package/.mesh/templates/skills/distributed-mesh/sync-mesh.sh +104 -0
  88. package/.mesh/templates/skills/docs-standards/SKILL.md +71 -0
  89. package/.mesh/templates/skills/economy-mode/SKILL.md +101 -0
  90. package/.mesh/templates/skills/external-comms/SKILL.md +331 -0
  91. package/.mesh/templates/skills/gh-auth-isolation/SKILL.md +183 -0
  92. package/.mesh/templates/skills/git-workflow/SKILL.md +204 -0
  93. package/.mesh/templates/skills/github-multi-account/SKILL.md +95 -0
  94. package/.mesh/templates/skills/history-hygiene/SKILL.md +36 -0
  95. package/.mesh/templates/skills/humanizer/SKILL.md +107 -0
  96. package/.mesh/templates/skills/init-mode/SKILL.md +101 -0
  97. package/.mesh/templates/skills/mesh-conventions/SKILL.md +69 -0
  98. package/.mesh/templates/skills/model-selection/SKILL.md +139 -0
  99. package/.mesh/templates/skills/nap/SKILL.md +24 -0
  100. package/.mesh/templates/skills/personal-mesh/SKILL.md +57 -0
  101. package/.mesh/templates/skills/project-conventions/SKILL.md +56 -0
  102. package/.mesh/templates/skills/release-process/SKILL.md +435 -0
  103. package/.mesh/templates/skills/reskill/SKILL.md +92 -0
  104. package/.mesh/templates/skills/reviewer-protocol/SKILL.md +79 -0
  105. package/.mesh/templates/skills/secret-handling/SKILL.md +200 -0
  106. package/.mesh/templates/skills/session-recovery/SKILL.md +155 -0
  107. package/.mesh/templates/skills/test-discipline/SKILL.md +37 -0
  108. package/.mesh/templates/skills/windows-compatibility/SKILL.md +74 -0
  109. package/.mesh/templates/workflows/mesh-ci.yml +24 -0
  110. package/.mesh/templates/workflows/mesh-docs.yml +54 -0
  111. package/.mesh/templates/workflows/mesh-heartbeat.yml +237 -0
  112. package/.mesh/templates/workflows/mesh-insider-release.yml +61 -0
  113. package/.mesh/templates/workflows/mesh-issue-assign.yml +243 -0
  114. package/.mesh/templates/workflows/mesh-label-enforce.yml +181 -0
  115. package/.mesh/templates/workflows/mesh-preview.yml +55 -0
  116. package/.mesh/templates/workflows/mesh-promote.yml +120 -0
  117. package/.mesh/templates/workflows/mesh-release.yml +77 -0
  118. package/.mesh/templates/workflows/mesh-triage.yml +383 -0
  119. package/.mesh/templates/workflows/sync-mesh-labels.yml +204 -0
  120. package/README.md +640 -0
  121. package/bin/mercury-mesh.cjs +317 -0
  122. package/docs/brand-language.md +287 -0
  123. package/docs/commander-onboarding.md +462 -0
  124. package/docs/mercury-mesh-runtime-rename-impact.md +148 -0
  125. package/docs/persona-manifesto.md +114 -0
  126. package/docs/scenarios/client-compatibility.md +59 -0
  127. package/index.cjs +41 -0
  128. package/package.json +43 -0
@@ -0,0 +1,28 @@
1
+ # Multi-Agent Artifact Format
2
+
3
+ When multiple agents contribute to a final artifact (document, analysis, design), use this format. The assembled result must include:
4
+
5
+ - Termination condition
6
+ - Constraint budgets (if active)
7
+ - Reviewer verdicts (if any)
8
+ - Raw agent outputs appendix
9
+
10
+ ## Assembly Structure
11
+
12
+ The assembled result goes at the top. Below it, include:
13
+
14
+ ```
15
+ ## APPENDIX: RAW AGENT OUTPUTS
16
+
17
+ ### {Name} ({Role}) — Raw Output
18
+ {Paste agent's verbatim response here, unedited}
19
+
20
+ ### {Name} ({Role}) — Raw Output
21
+ {Paste agent's verbatim response here, unedited}
22
+ ```
23
+
24
+ ## Appendix Rules
25
+
26
+ This appendix is for diagnostic integrity. Do not edit, summarize, or polish the raw outputs. The Coordinator may not rewrite raw agent outputs; it may only paste them verbatim and assemble the final artifact above.
27
+
28
+ See `.mesh/templates/run-output.md` for the complete output format template.
@@ -0,0 +1,27 @@
1
+ # Orchestration Log Entry
2
+
3
+ > One file per agent spawn. Saved to `.mesh/orchestration-log/{timestamp}-{agent-name}.md`
4
+
5
+ ---
6
+
7
+ ### {timestamp} — {task summary}
8
+
9
+ | Field | Value |
10
+ |-------|-------|
11
+ | **Agent routed** | {Name} ({Role}) |
12
+ | **Why chosen** | {Routing rationale — what in the request matched this agent} |
13
+ | **Mode** | {`background` / `sync`} |
14
+ | **Why this mode** | {Brief reason — e.g., "No hard data dependencies" or "User needs to approve architecture"} |
15
+ | **Files authorized to read** | {Exact file paths the agent was told to read} |
16
+ | **File(s) agent must produce** | {Exact file paths the agent is expected to create or modify} |
17
+ | **Outcome** | {Completed / Rejected by {Reviewer} / Escalated} |
18
+
19
+ ---
20
+
21
+ ## Rules
22
+
23
+ 1. **One file per agent spawn.** Named `{timestamp}-{agent-name}.md`.
24
+ 2. **Log BEFORE spawning.** The entry must exist before the agent runs.
25
+ 3. **Update outcome AFTER the agent completes.** Fill in the Outcome field.
26
+ 4. **Never delete or edit past entries.** Append-only.
27
+ 5. **If a reviewer rejects work,** log the rejection as a new entry with the revision agent.
@@ -0,0 +1,152 @@
1
+ # Org Autonomy Runtime Spec
2
+
3
+ This spec defines the first minimal runtime for concurrent, semi-autonomous departments in Org Mode.
4
+
5
+ ## Design Goal
6
+
7
+ Departments should work concurrently and with local initiative, while remaining inside Mercury Mesh's coordinator-led control plane.
8
+
9
+ The coordinator remains the global control plane.
10
+ Department leads act as local schedulers inside an approved authority envelope.
11
+ Department members execute leased work packets in parallel.
12
+
13
+ ## Core Principle
14
+
15
+ **Autonomy is delegated, not sovereign.**
16
+
17
+ - The coordinator still owns spawning, synthesis, and escalation.
18
+ - Department leads may decompose and prioritize local work.
19
+ - Members may claim queued work only within department scope.
20
+ - Human tiers, E-Stop, and lifecycle status always override department autonomy.
21
+
22
+ ## Required Runtime Files
23
+
24
+ When `orgMode: true`, create:
25
+
26
+ ```text
27
+ .mesh/org/
28
+ structure.json
29
+ contracts/
30
+ {contract-name}.md
31
+ {department-id}/
32
+ charter.md
33
+ backlog.md
34
+ state.json
35
+ ```
36
+
37
+ ## structure.json Schema
38
+
39
+ Each department entry should include:
40
+
41
+ - `id`
42
+ - `name`
43
+ - `lead`
44
+ - `members`
45
+ - `domain`
46
+ - `routingKeywords`
47
+ - `leadStyle`
48
+ - `authority.canDecideLocally`
49
+ - `authority.mustEscalate`
50
+ - `runtime.autonomyMode`
51
+ - `runtime.maxParallelism`
52
+ - `runtime.claimLeaseMinutes`
53
+ - `runtime.heartbeatMinutes`
54
+ - `runtime.backlogPath`
55
+ - `runtime.statePath`
56
+ - `runtime.contracts`
57
+
58
+ ## Work Item Lifecycle
59
+
60
+ Every backlog item moves through these states:
61
+
62
+ 1. `queued`
63
+ 2. `claimed`
64
+ 3. `in_progress`
65
+ 4. `blocked`
66
+ 5. `review`
67
+ 6. `done`
68
+
69
+ Each claimed item must record:
70
+
71
+ - `workItemId`
72
+ - `claimedBy`
73
+ - `claimedAt`
74
+ - `leaseExpiresAt`
75
+ - `status`
76
+ - `dependsOn`
77
+ - `outputs`
78
+
79
+ ## Claim And Lease Protocol
80
+
81
+ - A member may claim only work that is in its department domain and not already claimed.
82
+ - Claims expire after `claimLeaseMinutes`.
83
+ - Expired claims are re-queued by Ralph or the coordinator if `requeueExpiredClaims` is true.
84
+ - Members must heartbeat before lease expiry when long-running work is still active.
85
+ - Shadow agents may inspect backlog items but may not claim execution work.
86
+
87
+ ## Department Scheduler Loop
88
+
89
+ The minimal autonomous loop is:
90
+
91
+ 1. Coordinator routes a mission to the relevant department lead.
92
+ 2. Lead decomposes it into work packets and writes them to `backlog.md`.
93
+ 3. Coordinator spawns eligible members against independent packets.
94
+ 4. Members execute in parallel under lease.
95
+ 5. Lead reviews probationary or blocked outputs.
96
+ 6. Scribe records outcomes; Ralph requeues stale work.
97
+
98
+ This preserves the coordinator's spawn ownership while allowing local department initiative.
99
+
100
+ ## Cross-Department Work
101
+
102
+ Cross-department work is **contract-first**.
103
+
104
+ - Before departments run in parallel, define an interface contract in `.mesh/org/contracts/`.
105
+ - Contracts define producer, consumer, inputs, outputs, invariants, and version.
106
+ - Departments may proceed concurrently against the current contract version.
107
+ - Contract changes trigger a lead alignment round.
108
+
109
+ ## Local Authority
110
+
111
+ Department leads may decide locally on:
112
+
113
+ - internal decomposition
114
+ - packet assignment
115
+ - implementation conventions inside the department
116
+ - test strategy inside the department
117
+
118
+ Department leads must escalate:
119
+
120
+ - architecture changes affecting another department
121
+ - contract changes
122
+ - compute policy exceptions
123
+ - roster changes
124
+ - destructive actions outside department scope
125
+
126
+ ## Safety Gates
127
+
128
+ Autonomy is suspended when:
129
+
130
+ - `halted: true`
131
+ - `.mesh/HALT` exists
132
+ - the user lacks required human tier
133
+ - the acting agent is `shadow`
134
+ - department parallelism limit is reached
135
+ - required contract or dependency is unresolved
136
+
137
+ ## Minimal Rollout
138
+
139
+ Phase 1 in this repo implements:
140
+
141
+ - org runtime config defaults
142
+ - a structure template with runtime fields
143
+ - department backlog and state templates
144
+ - contract template
145
+ - coordinator rules for delegated department scheduling
146
+
147
+ Future phases can add:
148
+
149
+ - automated claim reconciliation
150
+ - department heartbeats
151
+ - Ralph-driven backlog pickup cycles
152
+ - metrics and compute budgeting per department
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const RUNTIME_DIR_CANDIDATES = ['.mesh', '.mercury'];
8
+
9
+ function defaultRuntimeDir() {
10
+ return RUNTIME_DIR_CANDIDATES.find((candidate) => fs.existsSync(candidate)) || '.mesh';
11
+ }
12
+
13
+ function runtimeDirName(runtimeDir) {
14
+ return path.basename(path.resolve(runtimeDir));
15
+ }
16
+
17
+ function parseArgs(argv) {
18
+ const options = {
19
+ meshDir: defaultRuntimeDir(),
20
+ triageFile: 'triage-results.json',
21
+ output: 'org-backlog-results.json',
22
+ apply: false,
23
+ };
24
+
25
+ for (let index = 0; index < argv.length; index += 1) {
26
+ const arg = argv[index];
27
+ if (arg === '--mesh-dir') {
28
+ options.meshDir = argv[index + 1];
29
+ index += 1;
30
+ continue;
31
+ }
32
+ if (arg === '--triage-file') {
33
+ options.triageFile = argv[index + 1];
34
+ index += 1;
35
+ continue;
36
+ }
37
+ if (arg === '--output') {
38
+ options.output = argv[index + 1];
39
+ index += 1;
40
+ continue;
41
+ }
42
+ if (arg === '--apply') {
43
+ options.apply = true;
44
+ continue;
45
+ }
46
+ if (arg === '--help' || arg === '-h') {
47
+ printUsage();
48
+ process.exit(0);
49
+ }
50
+ throw new Error(`Unknown argument: ${arg}`);
51
+ }
52
+
53
+ return options;
54
+ }
55
+
56
+ function printUsage() {
57
+ console.log('Usage: node <runtime>/org/backlog-from-triage.js [--mesh-dir .mesh] --triage-file triage-results.json --output org-backlog-results.json [--apply]');
58
+ }
59
+
60
+ function readJson(filePath) {
61
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
62
+ }
63
+
64
+ function writeJson(filePath, value) {
65
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
66
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
67
+ }
68
+
69
+ function parseMarkdownTable(content) {
70
+ const lines = content.split(/\r?\n/);
71
+ const rows = [];
72
+
73
+ for (const line of lines) {
74
+ const trimmed = line.trim();
75
+ if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) continue;
76
+ const cells = trimmed.slice(1, -1).split('|').map((cell) => cell.trim());
77
+ if (cells.every((cell) => /^-+$/.test(cell))) continue;
78
+ rows.push(cells);
79
+ }
80
+
81
+ if (rows.length < 2) return null;
82
+ return { headers: rows[0], rows: rows.slice(1) };
83
+ }
84
+
85
+ function locateBacklogTable(lines) {
86
+ let start = -1;
87
+ let end = lines.length;
88
+ for (let index = 0; index < lines.length; index += 1) {
89
+ const line = lines[index].trim();
90
+ if (start === -1 && line.startsWith('| ID |')) {
91
+ start = index;
92
+ continue;
93
+ }
94
+ if (start !== -1 && line !== '' && !line.startsWith('|')) {
95
+ end = index;
96
+ break;
97
+ }
98
+ }
99
+ return start === -1 ? null : { start, end };
100
+ }
101
+
102
+ function serializeMarkdownTable(headers, rows) {
103
+ return [
104
+ `| ${headers.join(' | ')} |`,
105
+ `| ${headers.map(() => '---').join(' | ')} |`,
106
+ ...rows.map((row) => `| ${row.join(' | ')} |`),
107
+ ];
108
+ }
109
+
110
+ function updateBacklogFile(backlogPath, addRows, apply) {
111
+ const existing = fs.readFileSync(backlogPath, 'utf8');
112
+ const lines = existing.split(/\r?\n/);
113
+ const table = parseMarkdownTable(existing);
114
+ const tableIndex = locateBacklogTable(lines);
115
+
116
+ if (!table || !tableIndex) {
117
+ throw new Error(`Backlog table not found in ${backlogPath}`);
118
+ }
119
+
120
+ const headers = table.headers;
121
+ const updatedRows = [...table.rows, ...addRows];
122
+ const renderedTable = serializeMarkdownTable(headers, updatedRows);
123
+ const nextLines = [...lines];
124
+ nextLines.splice(tableIndex.start, tableIndex.end - tableIndex.start, ...renderedTable);
125
+
126
+ if (apply) {
127
+ fs.writeFileSync(backlogPath, `${nextLines.join('\n')}\n`, 'utf8');
128
+ }
129
+ }
130
+
131
+ function main() {
132
+ const options = parseArgs(process.argv.slice(2));
133
+ const meshDir = path.resolve(options.meshDir);
134
+ const runtimeName = runtimeDirName(meshDir);
135
+ const repoRoot = path.dirname(meshDir);
136
+ const configPath = path.join(meshDir, 'config.json');
137
+ const structurePath = path.join(meshDir, 'org', 'structure.json');
138
+ const triagePath = path.resolve(options.triageFile);
139
+
140
+ if (!fs.existsSync(configPath)) throw new Error(`config.json not found: ${configPath}`);
141
+ if (!fs.existsSync(structurePath)) throw new Error(`structure.json not found: ${structurePath}`);
142
+ if (!fs.existsSync(triagePath)) throw new Error(`triage file not found: ${triagePath}`);
143
+
144
+ const config = readJson(configPath);
145
+ if (!config.orgMode) {
146
+ writeJson(path.resolve(options.output), { enabled: false, reason: 'orgMode disabled', created: [] });
147
+ return;
148
+ }
149
+
150
+ const structure = readJson(structurePath);
151
+ const triageResults = readJson(triagePath);
152
+ const departments = new Map((structure.departments || []).map((department) => [department.id, department]));
153
+ const report = { enabled: true, apply: options.apply, generatedAt: new Date().toISOString(), created: [], skipped: [], warnings: [] };
154
+ const rowsByBacklog = new Map();
155
+
156
+ for (const item of triageResults) {
157
+ if (!item.departmentId) {
158
+ report.skipped.push({ issueNumber: item.issueNumber, reason: 'No department assigned' });
159
+ continue;
160
+ }
161
+
162
+ const department = departments.get(item.departmentId);
163
+ if (!department) {
164
+ report.warnings.push(`Unknown department: ${item.departmentId} for issue #${item.issueNumber}`);
165
+ continue;
166
+ }
167
+
168
+ const runtime = department.runtime || {};
169
+ const backlogPath = path.resolve(repoRoot, runtime.backlogPath || `${runtimeName}/org/${department.id}/backlog.md`);
170
+ if (!fs.existsSync(backlogPath)) {
171
+ report.warnings.push(`Backlog missing for department ${department.id}: ${path.relative(repoRoot, backlogPath)}`);
172
+ continue;
173
+ }
174
+
175
+ const packetId = `${department.id}-issue-${item.issueNumber}`;
176
+ const existing = fs.readFileSync(backlogPath, 'utf8');
177
+ if (existing.includes(`| ${packetId} |`)) {
178
+ report.skipped.push({ issueNumber: item.issueNumber, reason: `Packet already exists in ${path.relative(repoRoot, backlogPath)}` });
179
+ continue;
180
+ }
181
+
182
+ const title = (item.issueTitle || `Issue #${item.issueNumber}`).replace(/\|/g, '/');
183
+ const assignedTo = item.assignTo || 'unassigned';
184
+ const notes = [`Issue #${item.issueNumber}`, `Routed to ${assignedTo}`, item.source ? `source:${item.source}` : null].filter(Boolean).join('; ');
185
+ const row = [packetId, `[Issue #${item.issueNumber}] ${title}`, 'queued', 'unassigned', '-', '-', '-', notes];
186
+
187
+ if (!rowsByBacklog.has(backlogPath)) rowsByBacklog.set(backlogPath, []);
188
+ rowsByBacklog.get(backlogPath).push(row);
189
+ report.created.push({ issueNumber: item.issueNumber, backlogPath: path.relative(repoRoot, backlogPath), packetId, departmentId: item.departmentId });
190
+ }
191
+
192
+ for (const [backlogPath, rows] of rowsByBacklog.entries()) {
193
+ updateBacklogFile(backlogPath, rows, options.apply);
194
+ }
195
+
196
+ writeJson(path.resolve(options.output), report);
197
+ }
198
+
199
+ main();