@mmerterden/multi-agent-pipeline 16.7.1 → 16.8.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
@@ -26,6 +26,19 @@ Internal file-layout changes that don't affect the slash-command surface are sti
26
26
 
27
27
  - **`smoke-website-deploy-identity.sh` (20 assertions).** It runs the script against throwaway repos with real remotes rather than grepping the doc: the happy path lands and pushes, an exported `GIT_AUTHOR_EMAIL` halts with the commit still local, an unchanged tree makes no empty commit, a matching config is preserved while a wrong name is corrected, and missing or non-repository arguments exit 2. Wiring is asserted separately, since a correct script nothing calls is its own failure mode.
28
28
 
29
+ ## [16.8.0] - 2026-08-25
30
+
31
+ Usage reporting answers the question it was built for, and stops carrying what it never should have.
32
+
33
+ ### Changed
34
+
35
+ - **`usageLog.enabled` now defaults to `true`.** Reporting was opt-in and nothing opted in, so the panel showed three runs across eight days and could not distinguish "nobody uses this" from "the pipe is broken" - the sender swallows every error by design. On-by-default is defensible only because of the change below: the payload carries no repository name, no path, no prompt, no diff and no secret. `usageLog.optOut: true` remains the hard escape hatch, and `enabled: false` still silences the emitter completely.
36
+ - **The event carries the command that ran.** `commandOf()` returned `state.mode`, so every row read `full` or `local` and the one question the panel exists to answer - which commands people actually run - had no answer in the data. It now sends the invoked command; `mode` continues to ship separately.
37
+
38
+ ### Fixed
39
+
40
+ - **The repository name is no longer sent.** `rp` carried the raw project name, which put a corporate repository into the maintainer's database - the exact disclosure this telemetry must not make. It now sends a one-way digest: stable, so runs still group per project, and unreadable back into a name. The panel never needed the name, only the ability to tell one project's runs from another's. The row already stored was redacted in place.
41
+
29
42
  ## [16.7.1] - 2026-08-25
30
43
 
31
44
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "16.7.1",
3
+ "version": "16.8.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",
@@ -1061,8 +1061,8 @@
1061
1061
  "properties": {
1062
1062
  "enabled": {
1063
1063
  "type": "boolean",
1064
- "default": false,
1065
- "description": "Master switch. When false the emitter exits silently and nothing leaves the machine."
1064
+ "default": true,
1065
+ "description": "Master switch, on by default. The payload carries no repository name, path, prompt, diff or secret - only which command ran, how long, how many tokens, and whether it failed - which is what makes on-by-default defensible. Set false, or optOut true, and the emitter exits silently with nothing leaving the machine."
1066
1066
  },
1067
1067
  "optOut": {
1068
1068
  "type": "boolean",
@@ -26,6 +26,7 @@ import { homedir, platform } from "os";
26
26
  import { dirname, join } from "path";
27
27
  import { fileURLToPath, pathToFileURL } from "url";
28
28
  import { execFileSync } from "child_process";
29
+ import { createHash } from "crypto";
29
30
  import { costUsd } from "./_cost.mjs";
30
31
 
31
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -272,13 +273,28 @@ function inputType(state) {
272
273
  return "free-text";
273
274
  }
274
275
 
276
+ // The command that was actually invoked (`/multi-agent:analysis`, `:review`,
277
+ // ...). This used to return `state.mode`, so every row read "full" or "local"
278
+ // and the one question the panel exists to answer - which command did people
279
+ // run - had no answer in the data. `mode` still ships separately as `m`.
275
280
  function commandOf(state) {
281
+ const c = state.command;
282
+ if (typeof c === "string" && c.trim()) return c.trim().slice(0, 60);
276
283
  if (state.mode) return state.mode;
277
284
  if (state.onlyDevelop) return "dev";
278
285
  if (state.localMode) return "local";
279
286
  return "full";
280
287
  }
281
288
 
289
+ // A repository name is the user's, not ours. It put a corporate project into
290
+ // the maintainer's database, which is the disclosure this telemetry must not
291
+ // make - and the panel never needed the name, only the ability to tell one
292
+ // project's runs from another's. A digest does that and cannot be read back.
293
+ function projectDigest(name) {
294
+ if (typeof name !== "string" || !name.trim()) return null;
295
+ return `p${createHash("sha256").update(name.trim()).digest("hex").slice(0, 8)}`;
296
+ }
297
+
282
298
  function resolveTracker(state) {
283
299
  const id = state.taskId || state.shortId;
284
300
  if (id == null) return null;
@@ -490,7 +506,7 @@ function buildEvent(state) {
490
506
  lo: Boolean(state.localMode),
491
507
  dev: Boolean(state.onlyDevelop),
492
508
  in: inputType(state),
493
- rp: state.project || null,
509
+ rp: projectDigest(state.project),
494
510
  ph: typeof state.currentPhase === "number" ? state.currentPhase : null,
495
511
  st: state.status || null,
496
512
  hr: state.haltReason || null,