@mmerterden/multi-agent-pipeline 15.9.0 → 15.9.1
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,11 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [15.9.1] - 2026-08-20
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **Telemetry logs the GitHub account name, never the git `identity.name`.** The reporter resolved the run's user to `identity.username || identity.name`, and since prefs identities carried no `username`, it fell back to `identity.name` - which can be a full corporate title/brand string, landing verbatim in the usage store. It now resolves to the identity's GitHub username, then the active `gh` account login resolved live, then null; the git `identity.name` is no longer a fallback. Self-registration (`/multi-agent:update` step 5b) resolves the same way.
|
|
23
|
+
|
|
19
24
|
## [15.9.0] - 2026-08-20
|
|
20
25
|
|
|
21
26
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "15.9.
|
|
3
|
+
"version": "15.9.1",
|
|
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",
|
|
@@ -104,7 +104,11 @@ A git clone of the pipeline repo is a maintainer workspace, kept in sync by `/mu
|
|
|
104
104
|
if [ -z "$TOK" ]; then
|
|
105
105
|
EP=$(jq -r '.global.usageLog.endpoint // "https://mmerterden.vercel.app/api/usage/ingest"' "$PREFS" 2>/dev/null)
|
|
106
106
|
REG_EP="${EP%/ingest}/register"
|
|
107
|
-
|
|
107
|
+
# Telemetry identity is the GitHub account name, never the git
|
|
108
|
+
# identity.name (which can carry a corporate title). username -> live
|
|
109
|
+
# gh login -> OS user.
|
|
110
|
+
RUSER=$(jq -r '.global.identities[0].username // empty' "$PREFS" 2>/dev/null)
|
|
111
|
+
[ -z "$RUSER" ] && RUSER=$(gh api user --jq .login 2>/dev/null || echo "")
|
|
108
112
|
[ -z "$RUSER" ] && RUSER="$USER"
|
|
109
113
|
RESP=$(curl -sSL -m 10 -X POST -H "Content-Type: application/json" \
|
|
110
114
|
--data "{\"u\":\"$RUSER\",\"c\":\"$(hostname -s 2>/dev/null || echo unknown)\"}" \
|
|
@@ -452,13 +452,38 @@ function appendErrorLedger(event) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
// Telemetry identity is the GitHub ACCOUNT NAME, never the git identity.name
|
|
456
|
+
// (which can carry a full corporate title / brand text). Order: the username
|
|
457
|
+
// stored on the run's identity (the gh login, from prefs identities), then the
|
|
458
|
+
// active gh account's login resolved live, then null. The corporate `name` is
|
|
459
|
+
// deliberately not a fallback.
|
|
460
|
+
function resolveGithubLogin() {
|
|
461
|
+
try {
|
|
462
|
+
const out = execFileSync("gh", ["api", "user", "--jq", ".login"], {
|
|
463
|
+
encoding: "utf-8",
|
|
464
|
+
timeout: 3000,
|
|
465
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
466
|
+
}).trim();
|
|
467
|
+
if (out) return out;
|
|
468
|
+
} catch {
|
|
469
|
+
/* gh absent or not authenticated - stay silent */
|
|
470
|
+
}
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function resolveUser(state) {
|
|
475
|
+
const fromIdentity = state.identity?.username;
|
|
476
|
+
if (typeof fromIdentity === "string" && fromIdentity.trim()) return fromIdentity.trim();
|
|
477
|
+
return resolveGithubLogin();
|
|
478
|
+
}
|
|
479
|
+
|
|
455
480
|
function buildEvent(state) {
|
|
456
481
|
const spend = trackerSummary(state);
|
|
457
482
|
const ctx = deriveContext(state);
|
|
458
483
|
return {
|
|
459
484
|
id: runId(state),
|
|
460
485
|
t: eventTimestamp(state, spend),
|
|
461
|
-
u: state
|
|
486
|
+
u: resolveUser(state),
|
|
462
487
|
c: commandOf(state),
|
|
463
488
|
m: state.mode || null,
|
|
464
489
|
ap: Boolean(state.autopilot),
|
|
@@ -577,4 +602,4 @@ if (invokedDirectly) {
|
|
|
577
602
|
main().catch(() => process.exit(0));
|
|
578
603
|
}
|
|
579
604
|
|
|
580
|
-
export { endpointAllowed, integrationTags, enabledPlugins, readEnabledPluginNames, trackerSummary, buildEvent };
|
|
605
|
+
export { endpointAllowed, integrationTags, enabledPlugins, readEnabledPluginNames, trackerSummary, buildEvent, resolveUser };
|
|
@@ -1847,16 +1847,88 @@ rules:
|
|
|
1847
1847
|
view callback bypasses that replay and is the finding, wherever it compiles.
|
|
1848
1848
|
|
|
1849
1849
|
- id: UNIT-03
|
|
1850
|
-
title:
|
|
1850
|
+
title: A view model's dependencies are injected properties; init takes only input and output
|
|
1851
1851
|
severity: important
|
|
1852
1852
|
enforcement: lint
|
|
1853
|
-
mechanism: 'custom regex:
|
|
1853
|
+
mechanism: 'custom regex: a unit view model init with a parameter beyond input:/output:, or a dependency resolved inline at a call site'
|
|
1854
1854
|
rationale: flexibility
|
|
1855
1855
|
check: >
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1856
|
+
A unit view model is constructible from its Input and its output sink ALONE - init(input:output:),
|
|
1857
|
+
collapsing to init(output:) when Input is Void. Every OTHER dependency is an @ObservationIgnored
|
|
1858
|
+
property resolved by injection (a keyed inject property wrapper, or the project's equivalent), never an
|
|
1859
|
+
init parameter and never resolved inline at the use site. Tests substitute a double through a
|
|
1860
|
+
task-local overlay or a preview registration - there is no init parameter to thread it through - and
|
|
1861
|
+
observation never tracks a service handle. The finding is an init carrying a dependency parameter
|
|
1862
|
+
(defaulted or not), or a dependency resolved at its call site. Scenes, coordinators and outside-contract
|
|
1863
|
+
answerers keep their resolving default arms; VIEW MODELS do not.
|
|
1864
|
+
|
|
1865
|
+
- id: UNIT-04
|
|
1866
|
+
title: Two typed send doors, never one untyped funnel
|
|
1867
|
+
severity: important
|
|
1868
|
+
enforcement: scan
|
|
1869
|
+
mechanism: 'custom regex: a public/internal send or handle on a view model taking a single action type that both the view and internal callers reach'
|
|
1870
|
+
rationale: flexibility
|
|
1871
|
+
check: >
|
|
1872
|
+
What the VIEW may send (the view-action type) and what the unit sends ITSELF (its own action type)
|
|
1873
|
+
are separate types, carried under one action envelope (a two-case `.view` / `.viewModel` enum) that the
|
|
1874
|
+
reducer switches on. A view physically cannot construct or send an internal action. The finding is one
|
|
1875
|
+
untyped funnel - a single action enum, or a `send`/`handle` entry point, that both the view and the
|
|
1876
|
+
machine's own effects/children reach - which lets a view invoke internal machinery. The outbound
|
|
1877
|
+
output stays a third, separate per-screen vocabulary.
|
|
1878
|
+
|
|
1879
|
+
- id: UNIT-05
|
|
1880
|
+
title: An effect returns the unit's own action and never touches state; loading is derived
|
|
1881
|
+
severity: important
|
|
1882
|
+
enforcement: scan
|
|
1883
|
+
mechanism: 'custom regex: a state write or a loading Bool/counter set inside an effect/task body in a view model'
|
|
1884
|
+
rationale: readability
|
|
1885
|
+
check: >
|
|
1886
|
+
Async work is registered by id and its closure RETURNS the unit's own action, which re-enters through
|
|
1887
|
+
the reducer - the closure cannot read or write state. Loading is DERIVED from the running-effects
|
|
1888
|
+
registry (the set of in-flight ids), never a hand-set Bool or a hand-balanced count that can drift from
|
|
1889
|
+
the truth; dismissing loading cancels the work rather than orphaning it, and a same-id re-run supersedes
|
|
1890
|
+
the one in flight. The finding is an effect body that assigns state, or a `isLoading`/loading counter set
|
|
1891
|
+
by hand instead of derived.
|
|
1892
|
+
|
|
1893
|
+
- id: UNIT-06
|
|
1894
|
+
title: A view holds the erased face, not the concrete unit
|
|
1895
|
+
severity: important
|
|
1896
|
+
enforcement: scan
|
|
1897
|
+
mechanism: 'custom regex: a SwiftUI view storing a concrete view model type instead of the erased face, or calling emit/run/an internal action from view code'
|
|
1898
|
+
rationale: flexibility
|
|
1899
|
+
check: >
|
|
1900
|
+
A view stores the ERASED face - a surface exposing only the projected view state, the view-action
|
|
1901
|
+
door, and bindings - not the concrete unit type. The concrete type's emit, effect-run, and own action
|
|
1902
|
+
are not spellable from view code; the fence is the TYPE, not a naming convention. The finding is a view
|
|
1903
|
+
whose stored model is the concrete unit (so it can reach `emit`/`run`/internal actions), or view code
|
|
1904
|
+
that calls one of those.
|
|
1905
|
+
|
|
1906
|
+
- id: UNIT-07
|
|
1907
|
+
title: Chrome on the unit path is one value-typed notice, not per-screen slots
|
|
1908
|
+
severity: important
|
|
1909
|
+
enforcement: scan
|
|
1910
|
+
mechanism: 'custom regex: a unit view model declaring its own alert/toast/modal slot properties instead of raising the shared value-typed notice'
|
|
1911
|
+
rationale: readability
|
|
1912
|
+
check: >
|
|
1913
|
+
On the unit system a failure or a prompt is one VALUE-typed notice (equatable, codable, closure-free -
|
|
1914
|
+
responses travel back as values through a respond entry point, never a callback stored beside the
|
|
1915
|
+
chrome) that the base escalates up the host chain to whoever renders it. A domain screen does not raise
|
|
1916
|
+
chrome by hand or hold its own alert/toast/modal slot trio (that is the pre-unit shape). The finding is a
|
|
1917
|
+
unit view model carrying per-screen chrome slots or passing a closure through the notice.
|
|
1918
|
+
|
|
1919
|
+
- id: UNIT-08
|
|
1920
|
+
title: View-facing state is projected from machine state, not the same type
|
|
1921
|
+
severity: important
|
|
1922
|
+
enforcement: lint
|
|
1923
|
+
mechanism: 'custom regex: a view reading the durable machine-state type directly instead of the projected view-state'
|
|
1924
|
+
rationale: readability
|
|
1925
|
+
check: >
|
|
1926
|
+
The durable machine state (every field the reducer needs, including bookkeeping the screen never draws)
|
|
1927
|
+
and the view-facing state the view renders are distinct: the view state is a PROJECTION produced by the
|
|
1928
|
+
view model (a typealias to a single machine-state field when that is all the view needs, an earned struct
|
|
1929
|
+
at two). A view reads the view state only, so adding an internal field to the machine state never changes
|
|
1930
|
+
what the view can see or spell. The finding is a view bound directly to the durable machine-state type,
|
|
1931
|
+
or a machine-state field with no reason to exist beyond what the view already renders.
|
|
1860
1932
|
|
|
1861
1933
|
- id: SAFE-03
|
|
1862
1934
|
title: A field reset that must erase the value writes the value before clear()
|