amicus 4.4.1 → 4.5.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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +130 -0
  3. package/README.md +15 -2
  4. package/bin/amicus.js +10 -0
  5. package/docs/ROADMAP.md +36 -10
  6. package/docs/configuration.md +24 -0
  7. package/docs/council.md +59 -0
  8. package/docs/schemas.md +1 -0
  9. package/docs/usage.md +151 -1
  10. package/electron/workspace-ui/workspace-app.js +39 -17
  11. package/electron/workspace-ui/workspace-panels.js +76 -18
  12. package/electron/workspace-ui/workspace-render.js +10 -0
  13. package/package.json +1 -1
  14. package/schemas/council-run-live.schema.json +1 -1
  15. package/schemas/council-run.schema.json +14 -0
  16. package/schemas/error.schema.json +1 -1
  17. package/schemas/event.schema.json +1 -1
  18. package/schemas/pack.schema.json +30 -0
  19. package/schemas/progress.schema.json +1 -1
  20. package/schemas/run-live.schema.json +1 -1
  21. package/schemas/run.schema.json +2 -1
  22. package/schemas/wave-live.schema.json +1 -1
  23. package/schemas/wave.schema.json +2 -1
  24. package/skills/second-opinion/SKILL.md +5 -0
  25. package/src/cli-handlers-council-run.js +51 -8
  26. package/src/cli-handlers-pack.js +238 -0
  27. package/src/cli-handlers-run.js +36 -8
  28. package/src/cli-handlers-template.js +53 -0
  29. package/src/cli.js +64 -3
  30. package/src/council/findings.js +4 -41
  31. package/src/council/presets-cli.js +23 -11
  32. package/src/council/run-stages.js +12 -9
  33. package/src/council/run-state.js +17 -0
  34. package/src/council/run.js +1 -1
  35. package/src/headless.js +18 -14
  36. package/src/mcp-council-run.js +108 -4
  37. package/src/mcp-server.js +203 -7
  38. package/src/mcp-tools.js +15 -5
  39. package/src/pack/pack-cli.js +38 -0
  40. package/src/pack/pack-forward.js +96 -0
  41. package/src/pack/pack-resolve.js +297 -0
  42. package/src/pack/pack-store.js +130 -0
  43. package/src/pack/pack-validate.js +113 -0
  44. package/src/sidecar/fanout.js +21 -4
  45. package/src/sidecar/progress.js +34 -0
  46. package/src/sidecar/start.js +5 -4
  47. package/src/sidecar/workspace-auto-open.js +69 -0
  48. package/src/sidecar/workspace-window.js +46 -1
  49. package/src/template/apply.js +88 -0
  50. package/src/template/render.js +86 -0
  51. package/src/template/store.js +106 -0
  52. package/src/utils/config.js +65 -25
  53. package/src/utils/error-doc.js +5 -0
  54. package/src/utils/result-schema-rebuild.js +1 -0
  55. package/src/utils/result-schema.js +8 -2
  56. package/src/workspace/artifact-guard.js +44 -6
  57. package/src/workspace/run-detail.js +6 -0
@@ -96,14 +96,37 @@ function artifactAllowlist(run) {
96
96
  }
97
97
  }
98
98
 
99
+ // ⚠️ Task 18 (RN-1): the collision above is a real run-integrity defect — the run directory
100
+ // physically holds ONE file where two models' artifacts should be, and no renderer trick can
101
+ // recover both. What the renderer CAN stop doing is showing model A's prose under model B's
102
+ // name. Deterministic disambiguation: per colliding sanitized name, sort the RAW models
103
+ // (sorting, not insertion order, is what keeps this reproducible across processes/runs); the
104
+ // first (sorted) keeps the bare sanitized name, the rest get `~2`, `~3`, ... The suffixed
105
+ // names deliberately do not exist on disk — the presence manifest (run-detail.js, via
106
+ // fs.statSync over this same allowlist) marks them absent, so the renderer shows the honest
107
+ // "not written yet" empty state for every model but the first, instead of cross-matching.
108
+ const nameFor = new Map(); // raw model -> its (possibly suffixed) sanitized name
99
109
  for (const m of uniqueModels) {
100
- names.push(`review-${sanitizeName(m)}.md`);
101
- names.push(`judge-${sanitizeName(m)}.md`);
102
- // rebuttal-/revote- are keyed on the same BENCH ALIAS through the same sanitizeName
103
- // (materializeDebate is called with `d.raiser` / the revote leg's model — both aliases).
110
+ let s = sanitizeName(m);
111
+ const collision = collisionModels.get(s);
112
+ if (collision) {
113
+ const sortedRaw = [...collision].sort();
114
+ const index = sortedRaw.indexOf(m);
115
+ if (index > 0) { s = `${s}~${index + 1}`; }
116
+ }
117
+ nameFor.set(m, s);
118
+ }
119
+
120
+ for (const m of uniqueModels) {
121
+ const s = nameFor.get(m);
122
+ names.push(`review-${s}.md`);
123
+ names.push(`judge-${s}.md`);
124
+ // rebuttal-/revote- are keyed on the same BENCH ALIAS through the same (now possibly
125
+ // suffixed) name — materializeDebate is called with `d.raiser` / the revote leg's model
126
+ // (both aliases), so a colliding pair's debate artifacts are disambiguated the same way.
104
127
  if (debated) {
105
- names.push(`rebuttal-${sanitizeName(m)}.md`);
106
- names.push(`revote-${sanitizeName(m)}.md`);
128
+ names.push(`rebuttal-${s}.md`);
129
+ names.push(`revote-${s}.md`);
107
130
  }
108
131
  }
109
132
  // `uniqueModels` already collapsed genuinely-repeated bench entries, so this final Set is
@@ -115,6 +138,21 @@ function artifactAllowlist(run) {
115
138
  sanitized, models: [...models],
116
139
  }));
117
140
  }
141
+ // Consumed by workspace-panels.js (wireLazyPanels' file lists + drillIntoJudge's artifact
142
+ // lookup), which prefers this map over re-deriving names via sanitizeName(model) directly —
143
+ // that re-derivation is exactly what would ignore the suffixing above and misattribute prose.
144
+ // ⚠️ Fix-wave (review finding 1) residual limit this map cannot close: the BARE (unsuffixed)
145
+ // name is still exactly ONE physical file on disk, and its actual bytes belong to whichever
146
+ // colliding model's writer ran LAST — no map can recover which one that was. The guarantee
147
+ // delivered here is narrower than "attribution is fully sound": at most the sorted-first
148
+ // model can still be misattributed under the bare name; artifactCollisions (the run-integrity
149
+ // banner rendered by workspace-app.js's renderBanners) is what covers that residual case.
150
+ list.artifactsByModel = Object.fromEntries(
151
+ [...nameFor].map(([m, s]) => [m, {
152
+ review: `review-${s}.md`, judge: `judge-${s}.md`,
153
+ rebuttal: `rebuttal-${s}.md`, revote: `revote-${s}.md`,
154
+ }]),
155
+ );
118
156
  return list;
119
157
  }
120
158
 
@@ -209,6 +209,12 @@ function getRunDetail(project, runId) {
209
209
  // would otherwise silently misattribute prose. Surfaced here (rather than only inside
210
210
  // the low-level allowlist helper) so the renderer can warn the user directly.
211
211
  artifactCollisions: artifactNames.collisions || [],
212
+ // ⚠️ Task 18 (RN-1): the de-collision map itself (raw model -> {review, judge} filename),
213
+ // built once in artifactAllowlist alongside `collisions` above. `|| null` (absent, not an
214
+ // empty object) so workspace-panels.js can tell "no map at all" (older detail payloads —
215
+ // pre-v4.5 runs, live-doc consumers not yet updated) apart from a real map, and fall back
216
+ // to its legacy sanitizeName(model) computation only in the former case.
217
+ artifactsByModel: artifactNames.artifactsByModel || null,
212
218
  };
213
219
  }
214
220