@mmerterden/multi-agent-pipeline 18.0.0 → 19.1.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 +287 -0
- package/README.md +36 -20
- package/README.tr.md +14 -16
- package/docs/adr/0002-instruction-driven-flag.md +1 -0
- package/docs/adr/0005-lazy-phase-docs.md +11 -1
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +1 -0
- package/docs/adr/0010-own-code-graph.md +1 -0
- package/docs/adr/0014-six-phase-consolidation.md +134 -0
- package/docs/adr/README.md +2 -1
- package/docs/architecture.md +37 -38
- package/docs/best-practices.md +1 -1
- package/docs/ecosystem.md +46 -27
- package/docs/engineering.md +1 -1
- package/docs/facts.json +61 -0
- package/docs/features.md +55 -54
- package/docs/performance.md +5 -5
- package/docs/recovery-guide.md +17 -17
- package/docs/token-budget-history.md +3 -1
- package/index.js +2 -2
- package/install/_codex-agents.mjs +1 -1
- package/install/templates/claude-hooks.json +1 -1
- package/install/templates/codex-instructions.md +1 -1
- package/install/templates/copilot-instructions.md +28 -28
- package/manifest.json +234 -216
- package/package.json +2 -2
- package/pipeline/agents/dev-critic.md +7 -7
- package/pipeline/commands/figma-to-swiftui.md +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/analysis/SKILL.md +15 -15
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +7 -7
- package/pipeline/commands/multi-agent/channels/SKILL.md +15 -15
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/graph/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +62 -62
- package/pipeline/commands/multi-agent/language/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/local/SKILL.md +11 -11
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +13 -13
- package/pipeline/commands/multi-agent/log/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/model/SKILL.md +69 -0
- package/pipeline/commands/multi-agent/refactor/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/resume/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/resume-local/SKILL.md +19 -17
- package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/route-off/SKILL.md +36 -0
- package/pipeline/commands/multi-agent/route-on/SKILL.md +74 -0
- package/pipeline/commands/multi-agent/route-status/SKILL.md +56 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/status/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/steer/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/sync/SKILL.md +12 -13
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/lib/credential-inventory.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/model-dispatch.sh +140 -0
- package/pipeline/lib/model-rung.sh +142 -0
- package/pipeline/lib/outbound-gate.mjs +14 -0
- package/pipeline/lib/phase-schema.mjs +88 -0
- package/pipeline/lib/plan-todos.sh +5 -5
- package/pipeline/lib/route-state.sh +161 -0
- package/pipeline/lib/run-paths.sh +2 -2
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +6 -6
- package/pipeline/multi-agent-refs/_input-parser.md +1 -1
- package/pipeline/multi-agent-refs/analysis/evidence.md +2 -11
- package/pipeline/multi-agent-refs/analysis/intake.md +7 -7
- package/pipeline/multi-agent-refs/analysis/locked.md +48 -22
- package/pipeline/multi-agent-refs/analysis/redesign.md +1 -1
- package/pipeline/multi-agent-refs/analysis/render.md +10 -10
- package/pipeline/multi-agent-refs/analysis/resolve.md +1 -1
- package/pipeline/multi-agent-refs/analysis/review.md +2 -2
- package/pipeline/multi-agent-refs/analysis/synthesis.md +13 -7
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +9 -9
- package/pipeline/multi-agent-refs/analysis-template.md +19 -19
- package/pipeline/multi-agent-refs/android-guide.md +1 -1
- package/pipeline/multi-agent-refs/audit-guide.md +13 -13
- package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
- package/pipeline/multi-agent-refs/channels/jira.md +3 -3
- package/pipeline/multi-agent-refs/channels/pr.md +4 -4
- package/pipeline/multi-agent-refs/channels/wiki.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +8 -8
- package/pipeline/multi-agent-refs/conventions-defaults.md +2 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +31 -6
- package/pipeline/multi-agent-refs/features/analysis-jira.md +1 -1
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +4 -4
- package/pipeline/multi-agent-refs/features/code-graph.md +5 -5
- package/pipeline/multi-agent-refs/features/design-conformance.md +1 -1
- package/pipeline/multi-agent-refs/features/dev-critic.md +3 -3
- package/pipeline/multi-agent-refs/features/doctor.md +3 -3
- package/pipeline/multi-agent-refs/features/external-context-injection.md +3 -3
- package/pipeline/multi-agent-refs/features/maturity-followup.md +3 -3
- package/pipeline/multi-agent-refs/features/model-fallback.md +41 -5
- package/pipeline/multi-agent-refs/features/plan-todos.md +1 -1
- package/pipeline/multi-agent-refs/features/repo-map.md +1 -1
- package/pipeline/multi-agent-refs/features/review-delta.md +3 -3
- package/pipeline/multi-agent-refs/features/review-multi-repo.md +2 -2
- package/pipeline/multi-agent-refs/features/scope-check.md +4 -4
- package/pipeline/multi-agent-refs/features/skill-conformance.md +2 -2
- package/pipeline/multi-agent-refs/features/stack-skill-routing.md +1 -1
- package/pipeline/multi-agent-refs/features/url-enrichment.md +1 -1
- package/pipeline/multi-agent-refs/features/verify-by-test.md +4 -4
- package/pipeline/multi-agent-refs/features/visual-evidence.md +19 -19
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +6 -6
- package/pipeline/multi-agent-refs/issue-jira-triad.md +10 -10
- package/pipeline/multi-agent-refs/knowledge.md +11 -11
- package/pipeline/multi-agent-refs/multi-repo-integration-build.md +13 -13
- package/pipeline/multi-agent-refs/payload-contracts.md +8 -8
- package/pipeline/multi-agent-refs/phases/log-format.md +10 -10
- package/pipeline/multi-agent-refs/phases/modes.md +30 -30
- package/pipeline/multi-agent-refs/phases/operations.md +8 -8
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +24 -24
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +599 -0
- package/pipeline/multi-agent-refs/phases/{phase-3-dev.md → phase-2-dev.md} +129 -49
- package/pipeline/multi-agent-refs/phases/{phase-4-review.md → phase-3-review.md} +225 -107
- package/pipeline/multi-agent-refs/phases/{phase-6-commit.md → phase-4-commit.md} +23 -23
- package/pipeline/multi-agent-refs/phases/{phase-7-report.md → phase-5-report.md} +29 -29
- package/pipeline/multi-agent-refs/phases.md +44 -48
- package/pipeline/multi-agent-refs/picker-contract.md +1 -1
- package/pipeline/multi-agent-refs/progress-contract.md +6 -6
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/rules.md +7 -7
- package/pipeline/multi-agent-refs/swiftui-guide.md +2 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +31 -32
- package/pipeline/multi-agent-refs/wiki-capture.md +14 -14
- package/pipeline/preferences-template.json +9 -1
- package/pipeline/rules/figma-pipeline.md +8 -8
- package/pipeline/rules/outside-the-pipeline.md +1 -1
- package/pipeline/schemas/agent-state.schema.json +50 -50
- package/pipeline/schemas/analysis-output.schema.json +3 -3
- package/pipeline/schemas/analysis-spec.schema.json +2 -2
- package/pipeline/schemas/autopilot-config.schema.json +1 -1
- package/pipeline/schemas/code-graph.schema.json +1 -1
- package/pipeline/schemas/criteria-manifest.schema.json +1 -1
- package/pipeline/schemas/dev-critic-output.schema.json +1 -1
- package/pipeline/schemas/diff-risk.schema.json +1 -1
- package/pipeline/schemas/figma-project-config.schema.json +1 -1
- package/pipeline/schemas/migrations/prefs-2.4.0-to-2.5.0.mjs +2 -2
- package/pipeline/schemas/migrations/prefs-2.6.0-to-2.7.0.mjs +31 -0
- package/pipeline/schemas/migrations/state-2.1.0-to-2.2.0.mjs +129 -0
- package/pipeline/schemas/phases.json +105 -0
- package/pipeline/schemas/plan-todos.schema.json +5 -5
- package/pipeline/schemas/planning-output.schema.json +1 -1
- package/pipeline/schemas/prefs.schema.json +102 -58
- package/pipeline/schemas/reviewer-output.schema.json +3 -3
- package/pipeline/schemas/route-config.schema.json +74 -0
- package/pipeline/schemas/scope-check.schema.json +1 -1
- package/pipeline/schemas/secret-patterns.json +124 -0
- package/pipeline/schemas/test-gap.schema.json +1 -1
- package/pipeline/schemas/token-budget.json +12 -18
- package/pipeline/schemas/triage-output.schema.json +6 -6
- package/pipeline/scripts/README.md +3 -3
- package/pipeline/scripts/_code-graph.mjs +2 -2
- package/pipeline/scripts/_run-paths.mjs +2 -2
- package/pipeline/scripts/_smoke-root.sh +1 -1
- package/pipeline/scripts/aggregate-metrics.mjs +1 -1
- package/pipeline/scripts/build-references.mjs +2 -2
- package/pipeline/scripts/bulk-read.sh +10 -1
- package/pipeline/scripts/capture-flush.sh +8 -8
- package/pipeline/scripts/capture-resume.sh +3 -3
- package/pipeline/scripts/classify-plan-safety.mjs +1 -1
- package/pipeline/scripts/cost-table.json +8 -1
- package/pipeline/scripts/diff-explain.mjs +1 -1
- package/pipeline/scripts/doctor.mjs +3 -3
- package/pipeline/scripts/gc-abandoned.sh +3 -3
- package/pipeline/scripts/gc-tmp.sh +1 -1
- package/pipeline/scripts/gc-worktrees.sh +1 -1
- package/pipeline/scripts/gen-facts.mjs +280 -0
- package/pipeline/scripts/gen-mode-dispatch.mjs +32 -37
- package/pipeline/scripts/gen-ref-toc.mjs +1 -1
- package/pipeline/scripts/graph-report.mjs +1 -1
- package/pipeline/scripts/jira-attach.sh +1 -1
- package/pipeline/scripts/learn-from-transcripts.mjs +1 -1
- package/pipeline/scripts/learning-curve.mjs +2 -2
- package/pipeline/scripts/log-metric.sh +17 -4
- package/pipeline/scripts/memory-save.sh +1 -1
- package/pipeline/scripts/migrate-prefs.mjs +22 -5
- package/pipeline/scripts/phase-banner.sh +20 -20
- package/pipeline/scripts/phase-tracker.sh +12 -12
- package/pipeline/scripts/plan-coverage-gate.mjs +2 -2
- package/pipeline/scripts/pre-commit-check.sh +30 -1
- package/pipeline/scripts/render-agent-log-cost.sh +1 -1
- package/pipeline/scripts/render-work-summary.sh +3 -3
- package/pipeline/scripts/review-file-filter.mjs +1 -1
- package/pipeline/scripts/run-aggregator.mjs +13 -6
- package/pipeline/scripts/run-metrics.mjs +1 -1
- package/pipeline/scripts/runs-index.mjs +11 -1
- package/pipeline/scripts/scan-skills.sh +26 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +6 -6
- package/pipeline/scripts/smoke-schema-validation.sh +26 -7
- package/pipeline/scripts/token-budget-report.mjs +13 -2
- package/pipeline/scripts/triage-memory.mjs +2 -2
- package/pipeline/scripts/validate-analysis-doc.mjs +274 -43
- package/pipeline/scripts/validate-planning.mjs +1 -1
- package/pipeline/scripts/validate-reviewer.mjs +1 -1
- package/pipeline/scripts/validate-state.mjs +45 -5
- package/pipeline/scripts/validate-triage.mjs +3 -3
- package/pipeline/scripts/verify-citations.mjs +1 -1
- package/pipeline/scripts/worktree-finalize.sh +5 -5
- package/pipeline/scripts/write-state.mjs +32 -0
- package/pipeline/skills/.skill-manifest.json +38 -22
- package/pipeline/skills/.skills-index.json +49 -5
- package/pipeline/skills/shared/README.md +10 -6
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -8
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +81 -82
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +14 -14
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +25 -23
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +6 -6
- package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +71 -0
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-resume-local/SKILL.md +7 -7
- package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +39 -0
- package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +76 -0
- package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +59 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +6 -5
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +1 -1
- package/pipeline/skills/shared/external/signal-community/SKILL.md +8 -1
- package/pipeline/skills/skills-index.md +8 -4
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +0 -263
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +0 -344
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +0 -182
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# model-rung.sh - read or flip the top model rung, and keep cost pricing with it.
|
|
3
|
+
#
|
|
4
|
+
# Backs /multi-agent:model. Two preference keys move as ONE write:
|
|
5
|
+
# prefs.global.modelFallback.fableEnabled the rung itself
|
|
6
|
+
# prefs.global.costBudget.pricingModel what the ledger prices against
|
|
7
|
+
#
|
|
8
|
+
# They were always meant to move together - model-fallback.md says so in prose -
|
|
9
|
+
# but nothing enforced it, so a hand edit that flipped one left the estimate
|
|
10
|
+
# pricing every call at a rung that was not in play. With the rung off and
|
|
11
|
+
# pricing still `fable`, the budget ceiling trips early and triggers a downgrade
|
|
12
|
+
# nothing needed. One write, or none.
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# model-rung.sh report current state (exit 0)
|
|
16
|
+
# model-rung.sh on enable the fable rung
|
|
17
|
+
# model-rung.sh off disable it
|
|
18
|
+
#
|
|
19
|
+
# Exit codes:
|
|
20
|
+
# 0 - reported or applied
|
|
21
|
+
# 2 - bad argument
|
|
22
|
+
# 3 - preferences file missing or unparseable (nothing written)
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
PREFS="${MULTI_AGENT_PREFS:-$HOME/.claude/multi-agent-preferences.json}"
|
|
27
|
+
ACTION="${1:-report}"
|
|
28
|
+
|
|
29
|
+
case "$ACTION" in
|
|
30
|
+
report | on | off) ;;
|
|
31
|
+
*)
|
|
32
|
+
echo "usage: model-rung.sh [on|off]" >&2
|
|
33
|
+
exit 2
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
# A missing `jq` must not look like a missing setting. Without this the reads
|
|
38
|
+
# below return empty, the command reports the rung as already off, and a user
|
|
39
|
+
# who asked to turn it off is told it was never on.
|
|
40
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
41
|
+
echo "model-rung: jq not found - cannot read or write preferences (install: brew install jq)" >&2
|
|
42
|
+
exit 3
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ ! -f "$PREFS" ]; then
|
|
46
|
+
echo "model-rung: preferences not found at $PREFS - run /multi-agent:setup first" >&2
|
|
47
|
+
exit 3
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if ! jq -e . "$PREFS" >/dev/null 2>&1; then
|
|
51
|
+
echo "model-rung: $PREFS is not valid JSON - refusing to write over it" >&2
|
|
52
|
+
exit 3
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
CUR=$(jq -r '.global.modelFallback.fableEnabled // false' "$PREFS")
|
|
56
|
+
PRICING=$(jq -r '.global.costBudget.pricingModel // "fable"' "$PREFS")
|
|
57
|
+
|
|
58
|
+
host_note() {
|
|
59
|
+
# Which host this is decides whether the switch is a switch or a status line.
|
|
60
|
+
# Saying "enabled" on a host where the rung means something else entirely is
|
|
61
|
+
# the surprise this block exists to avoid.
|
|
62
|
+
case "${MULTI_AGENT_HOST:-claude}" in
|
|
63
|
+
claude)
|
|
64
|
+
echo "Claude Code: the fable rung is Fable 5. This switch is live here."
|
|
65
|
+
;;
|
|
66
|
+
copilot)
|
|
67
|
+
echo "Copilot CLI: Fable 5 is not offered; its personas never sat on this rung."
|
|
68
|
+
echo "This setting is recorded but changes nothing on this host."
|
|
69
|
+
;;
|
|
70
|
+
codex)
|
|
71
|
+
echo "Codex CLI: the fable rung means gpt-5.6 @ xhigh, a different model on a"
|
|
72
|
+
echo "different account. This switch deliberately does NOT touch it - a knob"
|
|
73
|
+
echo "named after an Anthropic model must not silently retune a Codex run."
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
echo "Unknown host '${MULTI_AGENT_HOST}'; reporting the stored value only."
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if [ "$ACTION" = "report" ]; then
|
|
82
|
+
echo "fable rung: $CUR"
|
|
83
|
+
echo "cost pricing: $PRICING"
|
|
84
|
+
if [ "$CUR" = "true" ] && [ "$PRICING" != "fable" ]; then
|
|
85
|
+
echo "MISMATCH: rung is on but the ledger prices against '$PRICING' - run 'model on' to realign"
|
|
86
|
+
elif [ "$CUR" != "true" ] && [ "$PRICING" = "fable" ]; then
|
|
87
|
+
echo "MISMATCH: rung is off but the ledger still prices against fable - this trips"
|
|
88
|
+
echo " the budget ceiling early. Run 'model off' to realign."
|
|
89
|
+
fi
|
|
90
|
+
echo ""
|
|
91
|
+
host_note
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ "$ACTION" = "on" ]; then
|
|
96
|
+
WANT=true
|
|
97
|
+
WANT_PRICING=fable
|
|
98
|
+
else
|
|
99
|
+
WANT=false
|
|
100
|
+
WANT_PRICING=opus
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
TMP=$(mktemp) || { echo "model-rung: mktemp failed" >&2; exit 3; }
|
|
104
|
+
trap 'rm -f "$TMP"' EXIT
|
|
105
|
+
|
|
106
|
+
# Both keys in one filter. A two-step write can leave the pair half-applied if
|
|
107
|
+
# the second step fails, which is the exact state this command exists to prevent.
|
|
108
|
+
if ! jq --argjson want "$WANT" --arg pricing "$WANT_PRICING" '
|
|
109
|
+
.global.modelFallback //= {}
|
|
110
|
+
| .global.modelFallback.fableEnabled = $want
|
|
111
|
+
| .global.costBudget //= {}
|
|
112
|
+
| .global.costBudget.pricingModel = $pricing
|
|
113
|
+
' "$PREFS" > "$TMP"; then
|
|
114
|
+
echo "model-rung: jq failed - preferences left untouched" >&2
|
|
115
|
+
exit 3
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
if ! jq -e . "$TMP" >/dev/null 2>&1; then
|
|
119
|
+
echo "model-rung: produced invalid JSON - preferences left untouched" >&2
|
|
120
|
+
exit 3
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
mv "$TMP" "$PREFS"
|
|
124
|
+
trap - EXIT
|
|
125
|
+
|
|
126
|
+
echo "fable rung: $CUR -> $WANT"
|
|
127
|
+
echo "cost pricing: $PRICING -> $WANT_PRICING"
|
|
128
|
+
echo ""
|
|
129
|
+
|
|
130
|
+
if [ "$WANT" = "false" ]; then
|
|
131
|
+
# Said at the moment of the change rather than left in a doc: the panel
|
|
132
|
+
# shrinking is the part people discover later and read as a bug.
|
|
133
|
+
echo "Phase 3 reviewer panel: 3 models -> 2 (opus + sonnet)."
|
|
134
|
+
echo "Reviewer 1 lands on opus, which Reviewer 2 already holds, and dispatching"
|
|
135
|
+
echo "one model twice is not cross-model review. consensus.reviewerCount records 2."
|
|
136
|
+
echo "Triage also runs on opus, so it shares a model with Reviewer 1 - the Phase 3"
|
|
137
|
+
echo "Step 3 anonymisation requirement is not optional while the rung is off."
|
|
138
|
+
echo ""
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
host_note
|
|
142
|
+
exit 0
|
|
@@ -68,6 +68,20 @@ export const RULES = [
|
|
|
68
68
|
{ name: "anthropic-key", certain: true, re: /\bsk-ant-[A-Za-z0-9_-]{32,}\b/g },
|
|
69
69
|
{ name: "figma-token", certain: true, re: /\bfig[a-z]_[A-Za-z0-9_-]{20,}\b/g },
|
|
70
70
|
{ name: "npm-token", certain: true, re: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
71
|
+
// This list and the one in scripts/pre-commit-check.sh cover the same
|
|
72
|
+
// provider set. smoke-secret-parity.sh holds them to it by running a fake
|
|
73
|
+
// token of each shape through both, rather than comparing the regexes: a
|
|
74
|
+
// pattern that matches in one dialect and not the other reads as agreement
|
|
75
|
+
// and is not.
|
|
76
|
+
{ name: "gitlab-pat", certain: true, re: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
77
|
+
{ name: "stripe-key", certain: true, re: /\b[sr]k_live_[A-Za-z0-9]{20,}\b/g },
|
|
78
|
+
{ name: "perplexity-key", certain: true, re: /\bpplx-[A-Za-z0-9]{32,}\b/g },
|
|
79
|
+
{ name: "huggingface-token", certain: true, re: /\bhf_[A-Za-z0-9]{30,}\b/g },
|
|
80
|
+
{
|
|
81
|
+
name: "service-account-json",
|
|
82
|
+
certain: true,
|
|
83
|
+
re: /"type"\s*:\s*"service_account"/g,
|
|
84
|
+
},
|
|
71
85
|
{
|
|
72
86
|
name: "jwt",
|
|
73
87
|
certain: true,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file phase-schema.mjs - read a phase number that may have been written
|
|
3
|
+
* under either phase vocabulary.
|
|
4
|
+
*
|
|
5
|
+
* metrics.jsonl is append-only and v19.0.0 renumbered the phases, so the same
|
|
6
|
+
* field means two different things depending on when the line was written:
|
|
7
|
+
* `phase: 3` is Dev in a pre-v19 row and Review in a post-v19 one. Rows from
|
|
8
|
+
* v19.0.0 on carry `phaseSchema: 2`; rows without the field are generation 1.
|
|
9
|
+
*
|
|
10
|
+
* Every aggregator that compares a phase number to a literal, or prints a
|
|
11
|
+
* phase name, goes through here. Comparing directly is the bug this module
|
|
12
|
+
* exists to prevent - it does not throw, it silently attributes work to the
|
|
13
|
+
* wrong phase and the roll-up still looks plausible.
|
|
14
|
+
*
|
|
15
|
+
* @module pipeline/lib/phase-schema
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
19
|
+
|
|
20
|
+
const CONTRACT = JSON.parse(
|
|
21
|
+
readFileSync(new URL("../schemas/phases.json", import.meta.url), "utf8"),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const CURRENT_SCHEMA = CONTRACT.phaseSchema;
|
|
25
|
+
|
|
26
|
+
/** Generation 1 number -> current number, derived from each phase's `was`. */
|
|
27
|
+
const GEN1_TO_CURRENT = new Map();
|
|
28
|
+
for (const p of CONTRACT.phases) {
|
|
29
|
+
for (const old of p.was) GEN1_TO_CURRENT.set(Number(old), p.id);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const CURRENT_NAMES = new Map(CONTRACT.phases.map((p) => [p.id, p.name]));
|
|
33
|
+
const GEN1_NAMES = new Map(
|
|
34
|
+
Object.entries(CONTRACT.legacyPhaseNames)
|
|
35
|
+
.filter(([k]) => /^\d+$/.test(k))
|
|
36
|
+
.map(([k, v]) => [Number(k), v]),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Which vocabulary a metrics row was written under.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} row - a parsed metrics.jsonl line
|
|
43
|
+
* @returns {number} - 1 or 2
|
|
44
|
+
*/
|
|
45
|
+
export function schemaOf(row) {
|
|
46
|
+
const s = Number(row?.phaseSchema);
|
|
47
|
+
return Number.isInteger(s) && s > 0 ? s : 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A row's phase expressed in the CURRENT vocabulary, so rows from both
|
|
52
|
+
* generations can be counted together.
|
|
53
|
+
*
|
|
54
|
+
* @param {string|number} phase
|
|
55
|
+
* @param {number} schema - from schemaOf()
|
|
56
|
+
* @returns {number|null} - null when the value is not a phase number at all
|
|
57
|
+
*/
|
|
58
|
+
export function toCurrentPhase(phase, schema) {
|
|
59
|
+
const n = Number(phase);
|
|
60
|
+
if (!Number.isInteger(n)) return null;
|
|
61
|
+
if (schema >= CURRENT_SCHEMA) return CURRENT_NAMES.has(n) ? n : null;
|
|
62
|
+
return GEN1_TO_CURRENT.has(n) ? GEN1_TO_CURRENT.get(n) : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The name the phase number carried WHEN IT WAS WRITTEN. A historical row is
|
|
67
|
+
* labelled with its own vocabulary's name; relabelling it with today's name
|
|
68
|
+
* would make the archive disagree with the log it came from.
|
|
69
|
+
*
|
|
70
|
+
* @param {string|number} phase
|
|
71
|
+
* @param {number} schema
|
|
72
|
+
* @returns {string}
|
|
73
|
+
*/
|
|
74
|
+
export function phaseNameAsWritten(phase, schema) {
|
|
75
|
+
const n = Number(phase);
|
|
76
|
+
const table = schema >= CURRENT_SCHEMA ? CURRENT_NAMES : GEN1_NAMES;
|
|
77
|
+
return table.get(n) ?? `Phase ${phase}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Convenience for the common shape: a row in, its current-vocabulary phase out.
|
|
82
|
+
*
|
|
83
|
+
* @param {object} row
|
|
84
|
+
* @returns {number|null}
|
|
85
|
+
*/
|
|
86
|
+
export function rowPhase(row) {
|
|
87
|
+
return toCurrentPhase(row?.phase, schemaOf(row));
|
|
88
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# plan-todos.sh - manage the Phase
|
|
3
|
+
# plan-todos.sh - manage the Phase 1 plan as a live Todo list.
|
|
4
4
|
#
|
|
5
|
-
# The Phase
|
|
5
|
+
# The Phase 1 plan is broken into a live, reviewable Todo list that updates
|
|
6
6
|
# step-by-step as Phase 3 works through it.
|
|
7
7
|
#
|
|
8
8
|
# State lives in `agent-state.json` under `.plan.todos[]` per
|
|
9
9
|
# pipeline/schemas/plan-todos.schema.json. Phase 2 (Planning) emits the
|
|
10
10
|
# initial plan; Phase 3 (Dev) iterates step-by-step; Phase 4 (Review)
|
|
11
|
-
# inspects completion; Phase
|
|
11
|
+
# inspects completion; Phase 5 (Report) renders the rollup.
|
|
12
12
|
#
|
|
13
13
|
# Subcommands:
|
|
14
14
|
# init <task-id> [title] Initialize with empty todos[] (Phase 2 usually pipes the JSON instead)
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# skip <task-id> <todo-id> <reason> Mark skipped (counts as satisfied for dependents)
|
|
20
20
|
# next <task-id> Print next pending todo (deps-respecting); empty if none
|
|
21
21
|
# list <task-id> Render Markdown checklist
|
|
22
|
-
# status <task-id> Print one-line summary: "
|
|
22
|
+
# status <task-id> Print one-line summary: "2/5 done, 1 in progress"
|
|
23
23
|
# show <task-id> Print the full plan JSON
|
|
24
24
|
#
|
|
25
25
|
# Exit codes:
|
|
@@ -117,7 +117,7 @@ do_set() {
|
|
|
117
117
|
fi
|
|
118
118
|
# A planning-output document is accepted directly and converted here.
|
|
119
119
|
#
|
|
120
|
-
# The conversion used to live as a jq blob inside phase-
|
|
120
|
+
# The conversion used to live as a jq blob inside phase-1-plan.md, which
|
|
121
121
|
# made the mapping from `tasks[]` to `todos[]` a thing two files defined - and
|
|
122
122
|
# the phase doc was the copy nothing tested. Accepting both shapes costs four
|
|
123
123
|
# lines and removes the second definition.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# route-state.sh - read and write prefs.global.modelRouting.
|
|
3
|
+
#
|
|
4
|
+
# Backs /multi-agent:route-on, route-off and route-status. Ships disabled and
|
|
5
|
+
# stays that way until someone turns it on; while `enabled` is false nothing in
|
|
6
|
+
# this file changes one dispatch.
|
|
7
|
+
#
|
|
8
|
+
# The `off` contract is borrowed from autopilot-off and for the same reason: it
|
|
9
|
+
# clears the ON STATE, never the rules. Turning routing back on must not re-ask
|
|
10
|
+
# for a configuration the user already gave.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# route-state.sh status print the current policy
|
|
14
|
+
# route-state.sh on [--strategy=S] [--scope=a,b]
|
|
15
|
+
# route-state.sh off
|
|
16
|
+
# route-state.sh set-rules <file.json> replace rules[] from a JSON array
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 - done
|
|
20
|
+
# 2 - bad usage
|
|
21
|
+
# 3 - preferences missing/unparseable, or a scope value this refuses
|
|
22
|
+
|
|
23
|
+
set -uo pipefail
|
|
24
|
+
|
|
25
|
+
PREFS="${MULTI_AGENT_PREFS:-$HOME/.claude/multi-agent-preferences.json}"
|
|
26
|
+
|
|
27
|
+
# The scope values this accepts, spelled once. `host-session` is NOT here and
|
|
28
|
+
# must never be added: it would mean rewriting the host's base URL, which routes
|
|
29
|
+
# the user's whole session - work that has nothing to do with this pipeline
|
|
30
|
+
# included - through a third layer, breaks the subscription's auth model, and
|
|
31
|
+
# silently changes which model answers. The schema encodes the same list so a
|
|
32
|
+
# hand-edited preferences file is rejected too, rather than only this entry point.
|
|
33
|
+
ALLOWED_SCOPE="subagent bulk-read research"
|
|
34
|
+
|
|
35
|
+
die() { echo "route-state: $1" >&2; exit "${2:-3}"; }
|
|
36
|
+
|
|
37
|
+
# A missing `jq` must not look like routing being off. Every read below would
|
|
38
|
+
# come back empty, `route-status` would report disabled, and a user with rules
|
|
39
|
+
# armed would be told there are none.
|
|
40
|
+
command -v jq >/dev/null 2>&1 || die "jq not found - cannot read or write preferences (install: brew install jq)"
|
|
41
|
+
[ -f "$PREFS" ] || die "preferences not found at $PREFS - run /multi-agent:setup first"
|
|
42
|
+
jq -e . "$PREFS" >/dev/null 2>&1 || die "$PREFS is not valid JSON - refusing to write over it"
|
|
43
|
+
|
|
44
|
+
ACTION="${1:-status}"; shift 2>/dev/null || true
|
|
45
|
+
|
|
46
|
+
write_prefs() {
|
|
47
|
+
local filter="$1"; shift
|
|
48
|
+
local tmp
|
|
49
|
+
tmp=$(mktemp) || die "mktemp failed"
|
|
50
|
+
if ! jq "$@" "$filter" "$PREFS" > "$tmp"; then rm -f "$tmp"; die "jq failed - preferences left untouched"; fi
|
|
51
|
+
jq -e . "$tmp" >/dev/null 2>&1 || { rm -f "$tmp"; die "produced invalid JSON - preferences left untouched"; }
|
|
52
|
+
mv "$tmp" "$PREFS"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case "$ACTION" in
|
|
56
|
+
status)
|
|
57
|
+
ENABLED=$(jq -r '.global.modelRouting.enabled // false' "$PREFS")
|
|
58
|
+
STRATEGY=$(jq -r '.global.modelRouting.strategy // "manual"' "$PREFS")
|
|
59
|
+
SCOPE=$(jq -r '(.global.modelRouting.scope // ["subagent"]) | join(", ")' "$PREFS")
|
|
60
|
+
NRULES=$(jq -r '(.global.modelRouting.rules // []) | length' "$PREFS")
|
|
61
|
+
CEIL=$(jq -r '.global.modelRouting.budgetCeilingUsd // "none"' "$PREFS")
|
|
62
|
+
REC=$(jq -r '.global.modelRouting.recordDecisions // true' "$PREFS")
|
|
63
|
+
|
|
64
|
+
echo "routing: $ENABLED"
|
|
65
|
+
echo "strategy: $STRATEGY"
|
|
66
|
+
echo "scope: $SCOPE"
|
|
67
|
+
echo "rules: $NRULES"
|
|
68
|
+
echo "ceiling: $CEIL"
|
|
69
|
+
echo "decisions: $REC"
|
|
70
|
+
echo ""
|
|
71
|
+
|
|
72
|
+
if [ "$ENABLED" != "true" ]; then
|
|
73
|
+
echo "Disabled. Rules are stored but read by nothing; dispatch is unchanged."
|
|
74
|
+
[ "$NRULES" -gt 0 ] && echo "route-on turns these $NRULES rule(s) back on without re-asking."
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [ "$NRULES" -eq 0 ]; then
|
|
79
|
+
echo "Armed with no rules. Nothing matches, so nothing is routed - this is a"
|
|
80
|
+
echo "configuration state, not an error."
|
|
81
|
+
else
|
|
82
|
+
jq -r '(.global.modelRouting.rules // [])[] |
|
|
83
|
+
" when " + ([.when | to_entries[] | "\(.key)=\(.value)"] | join(" ")) +
|
|
84
|
+
" -> " + (.prefer | join(" > "))' "$PREFS"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
echo ""
|
|
88
|
+
# The limit is printed every time rather than documented once, because the
|
|
89
|
+
# question it answers ("routing is on, why is the reviewer still on Opus")
|
|
90
|
+
# otherwise arrives days later as a bug report.
|
|
91
|
+
echo "Honest limit: on Claude Code a subagent cannot be sent to a non-Anthropic"
|
|
92
|
+
echo "model - subagent dispatch belongs to the host. Phase 1/2/3 personas stay"
|
|
93
|
+
echo "inside the Anthropic ladder whatever the rules say. External providers apply"
|
|
94
|
+
echo "only where this pipeline makes the call itself (bulk-read, research)."
|
|
95
|
+
;;
|
|
96
|
+
|
|
97
|
+
on)
|
|
98
|
+
STRATEGY="manual"; SCOPE_ARG=""
|
|
99
|
+
for arg in "$@"; do
|
|
100
|
+
case "$arg" in
|
|
101
|
+
--strategy=*) STRATEGY="${arg#*=}" ;;
|
|
102
|
+
--scope=*) SCOPE_ARG="${arg#*=}" ;;
|
|
103
|
+
*) die "unknown option: $arg" 2 ;;
|
|
104
|
+
esac
|
|
105
|
+
done
|
|
106
|
+
case "$STRATEGY" in
|
|
107
|
+
manual|task-fit|cost-ceiling) ;;
|
|
108
|
+
*) die "strategy must be manual, task-fit or cost-ceiling (got '$STRATEGY')" 2 ;;
|
|
109
|
+
esac
|
|
110
|
+
|
|
111
|
+
if [ -n "$SCOPE_ARG" ]; then
|
|
112
|
+
SCOPE_JSON="[]"
|
|
113
|
+
IFS=',' read -r -a parts <<< "$SCOPE_ARG"
|
|
114
|
+
for one in "${parts[@]}"; do
|
|
115
|
+
one="${one// /}"
|
|
116
|
+
# shellcheck disable=SC2076
|
|
117
|
+
case " $ALLOWED_SCOPE " in
|
|
118
|
+
*" $one "*) ;;
|
|
119
|
+
*) die "scope '$one' is not allowed (allowed: $ALLOWED_SCOPE). 'host-session' is refused by design: it would route the whole session, not this pipeline's calls." ;;
|
|
120
|
+
esac
|
|
121
|
+
SCOPE_JSON=$(jq -c --arg s "$one" '. + [$s]' <<< "$SCOPE_JSON")
|
|
122
|
+
done
|
|
123
|
+
write_prefs '
|
|
124
|
+
.global.modelRouting //= {}
|
|
125
|
+
| .global.modelRouting.enabled = true
|
|
126
|
+
| .global.modelRouting.strategy = $st
|
|
127
|
+
| .global.modelRouting.scope = ($sc | fromjson)
|
|
128
|
+
' --arg st "$STRATEGY" --arg sc "$SCOPE_JSON"
|
|
129
|
+
else
|
|
130
|
+
write_prefs '
|
|
131
|
+
.global.modelRouting //= {}
|
|
132
|
+
| .global.modelRouting.enabled = true
|
|
133
|
+
| .global.modelRouting.strategy = $st
|
|
134
|
+
| .global.modelRouting.scope //= ["subagent"]
|
|
135
|
+
' --arg st "$STRATEGY"
|
|
136
|
+
fi
|
|
137
|
+
echo "routing enabled (strategy: $STRATEGY)"
|
|
138
|
+
exec "$0" status
|
|
139
|
+
;;
|
|
140
|
+
|
|
141
|
+
off)
|
|
142
|
+
# Rules survive on purpose. Clearing them here would make route-off a
|
|
143
|
+
# destructive action wearing the name of a toggle.
|
|
144
|
+
write_prefs '.global.modelRouting //= {} | .global.modelRouting.enabled = false'
|
|
145
|
+
echo "routing disabled. Rules kept - route-on restores this configuration as it is."
|
|
146
|
+
;;
|
|
147
|
+
|
|
148
|
+
set-rules)
|
|
149
|
+
FILE="${1:-}"
|
|
150
|
+
[ -n "$FILE" ] || die "set-rules needs a JSON file containing an array of rules" 2
|
|
151
|
+
[ -f "$FILE" ] || die "no such file: $FILE" 2
|
|
152
|
+
jq -e 'type == "array"' "$FILE" >/dev/null 2>&1 || die "$FILE must contain a JSON ARRAY of rules" 2
|
|
153
|
+
write_prefs '.global.modelRouting //= {} | .global.modelRouting.rules = $r' --slurpfile _ignore /dev/null --argjson r "$(cat "$FILE")"
|
|
154
|
+
echo "rules replaced: $(jq -r 'length' "$FILE")"
|
|
155
|
+
;;
|
|
156
|
+
|
|
157
|
+
*)
|
|
158
|
+
echo "usage: route-state.sh [status|on|off|set-rules <file>]" >&2
|
|
159
|
+
exit 2
|
|
160
|
+
;;
|
|
161
|
+
esac
|
|
@@ -40,7 +40,7 @@ ma_logs_root() {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
# ma_is_run_dir <dir> -> 0 when the directory holds at least one run marker
|
|
43
|
-
# Phase
|
|
43
|
+
# Phase 4 removes the worktree and salvages the run's files into `artifacts/`
|
|
44
44
|
# inside the same run directory, so a shipped run keeps its state one level
|
|
45
45
|
# deeper. A reader that only looks at the top level reports it as stateless.
|
|
46
46
|
MA_ARTIFACTS_SUBDIR=artifacts
|
|
@@ -242,7 +242,7 @@ ma_resolve_run_file() {
|
|
|
242
242
|
printf '%s\n' "$dir/$filename"
|
|
243
243
|
return 0
|
|
244
244
|
fi
|
|
245
|
-
# The salvaged copy Phase
|
|
245
|
+
# The salvaged copy Phase 4 leaves behind.
|
|
246
246
|
if [ -f "$dir/$MA_ARTIFACTS_SUBDIR/$filename" ]; then
|
|
247
247
|
printf '%s\n' "$dir/$MA_ARTIFACTS_SUBDIR/$filename"
|
|
248
248
|
return 0
|
|
@@ -12,7 +12,7 @@ First step of every `multi-agent` flow **that touches a remote provider**.
|
|
|
12
12
|
> question, no token lookup. Detection: `freetext` flow first fetches local
|
|
13
13
|
> repos (`repo-cache.sh local "$HOME"`); if the picker resolves to local-only
|
|
14
14
|
> repos, account-picker is bypassed and the state file's `accountId` is left
|
|
15
|
-
> `null` with `tokens={}`. Phases
|
|
15
|
+
> `null` with `tokens={}`. Phases 4/5 read these as "local-only" signals.
|
|
16
16
|
|
|
17
17
|
> **Language**: see `picker-contract.md` + `rules.md` Language Application matrix.
|
|
18
18
|
|
|
@@ -63,14 +63,14 @@ Selects extra repos the pipeline may touch beyond the primary repo(s) - typica
|
|
|
63
63
|
entry, plus any selected `extras[]` that is not being given a worktree. The
|
|
64
64
|
phases that consume them run hours later and have no access to the picker's
|
|
65
65
|
return value, so a result that is not written here is a result nothing can
|
|
66
|
-
read. Phase
|
|
66
|
+
read. Phase 3's platform-parity cross-check reads `state.siblings[]` as the
|
|
67
67
|
fourth of its four counterpart sources, after `--with`,
|
|
68
68
|
`prefs.projects[<slug>].counterpartRoots[]` and the primary checkout's sibling
|
|
69
69
|
directories (`platform-parity.md`), so a submodule or a hand-picked repo that
|
|
70
70
|
is not written here is a candidate the check can never see.
|
|
71
71
|
|
|
72
72
|
Resolve each entry's `stack` from its local checkout, with the marker table
|
|
73
|
-
in `phases/phase-1-
|
|
73
|
+
in `phases/phase-1-plan.md` Step 2 (`.xcodeproj` / `Package.swift` →
|
|
74
74
|
`ios`, `build.gradle(.kts)` → `android`, and so on). No checkout, or no
|
|
75
75
|
marker matched → `stack: "unknown"` and `root: null`. Never infer a stack
|
|
76
76
|
from the repo NAME: `my-app-android` is a naming convention, not a marker,
|
|
@@ -142,14 +142,14 @@ When `MULTI_AGENT_AUTOPILOT=1`:
|
|
|
142
142
|
|
|
143
143
|
## Pipeline contract for read-only siblings
|
|
144
144
|
|
|
145
|
-
When a read-only sibling is present (e.g. a vendored SDK checkout), Phase
|
|
145
|
+
When a read-only sibling is present (e.g. a vendored SDK checkout), Phase 2 (Dev) MUST:
|
|
146
146
|
- Treat it as **read-only context** - code may be read for understanding, never edited or committed.
|
|
147
147
|
- Prefer solving the task in the primary repo(s) (e.g. by wrapping/extending in consumer code) rather than patching the sibling.
|
|
148
|
-
- Surface this constraint in the Phase
|
|
148
|
+
- Surface this constraint in the Phase 2 plan output so reviewers know why a workaround was chosen.
|
|
149
149
|
|
|
150
150
|
A **counterpart app repo** is a legitimate read-only sibling: the same product's
|
|
151
|
-
other mobile platform, added here so Phase
|
|
152
|
-
Phase
|
|
151
|
+
other mobile platform, added here so Phase 3 can compare the change against it.
|
|
152
|
+
Phase 2 treats it exactly like any other sibling - read, never edited. Phase 3
|
|
153
153
|
additionally runs the parity cross-check over it
|
|
154
154
|
(`$HOME/.claude/multi-agent-refs/platform-parity.md`), which is also read-only.
|
|
155
155
|
Adding one is worthwhile when the feature exists on both platforms and their
|
|
@@ -35,7 +35,7 @@ The top-level `multi-agent` command classifies user arguments per the rules belo
|
|
|
35
35
|
|
|
36
36
|
¹ Account picker is **skipped** when the resolved primary repo is local-only
|
|
37
37
|
(`provider="local"`). No token lookup, no provider auth - the pipeline
|
|
38
|
-
proceeds with `accountId=null` and Phases
|
|
38
|
+
proceeds with `accountId=null` and Phases 4/5 honor the local-only mode.
|
|
39
39
|
|
|
40
40
|
² Free-text flow merges `github`+`bitbucket`+`local` repo caches into a
|
|
41
41
|
single picker list; `(local)` rows come from `repo-cache.sh local "$HOME"`.
|
|
@@ -164,7 +164,7 @@ The index answers "does this component exist and is it bound". It does not answe
|
|
|
164
164
|
|
|
165
165
|
Output: `state.analysisSpec.evidence.variantMatrix[<component>] = { axis, allValues[], usedValues[] }`, which fills Section 6.X.
|
|
166
166
|
|
|
167
|
-
Why it lives here and nowhere later: Locked
|
|
167
|
+
Why it lives here and nowhere later: Locked 29 forbids Figma access from Phase 2 onward, so an axis not captured now cannot be recovered - Section 13.6 Preview and Section 15.2 Snapshot would then be validated against a subset nobody could check.
|
|
168
168
|
|
|
169
169
|
A component with no variant axis (a plain component, not a set) records `axis: null` and is not a finding.
|
|
170
170
|
|
|
@@ -202,7 +202,7 @@ Phase 2 Section 20 Risks reads this list and emits one open question per entry.
|
|
|
202
202
|
|
|
203
203
|
**Fallback source**: when `confidence == "none"` AND `evidence.standards[]` does not contain an explicit rule for that field, the renderer reads `$HOME/.claude/multi-agent-refs/conventions-defaults.md` and applies the platform default.
|
|
204
204
|
|
|
205
|
-
**Caching (Locked
|
|
205
|
+
**Caching (Locked 26)**: compute `evidence_digest = sha256(featureName || sorted(platforms) || options.redesign || hash(evidence.repoEvidence) || hash(evidence.conventions))`. `options.redesign` is a digest input: without it a redesign within a day of a normal run on the same feature reuses that cache, skips Phase 1b, and renders an empty current-behaviour table every redesign check then passes over. Cache key on disk: `/tmp/multi-agent-analysis-cache/<digest>.json` with mtime <= 24h. Cache hit skips Phase 1b and Phase 1c. `--no-cache` flag forces re-run.
|
|
206
206
|
|
|
207
207
|
Phase 1d is deliberately absent from the digest inputs. Community signal changes by the hour, so folding it in would produce a new digest on every run, invalidate the cache every time, and re-run the two expensive repo phases the cache exists to skip. The consequence is worth stating plainly: a cache hit reuses yesterday's signal rows. That is the correct trade for an advisory tier, and `--no-cache` is the way to refresh them.
|
|
208
208
|
|
|
@@ -225,12 +225,3 @@ The tier boundary is not advisory. A signal row that reaches Section 4 (business
|
|
|
225
225
|
|
|
226
226
|
Per source: reachable and answered, reachable and empty, or unreachable. All three are recorded; only the third produces a `fetchErrors[]` entry, and none of them halts.
|
|
227
227
|
|
|
228
|
-
**Lite mode auto-detection (Locked 25, v9.1.0 scoring)**: at the end of Phase 1c, evaluate three signals and score each:
|
|
229
|
-
|
|
230
|
-
| Signal | True condition | Score |
|
|
231
|
-
|---|---|---|
|
|
232
|
-
| Confluence spec body lines | < 100 | 1 |
|
|
233
|
-
| Figma frames count | <= 1 | 1 |
|
|
234
|
-
| Repo evidence direct-match count | >= 8 | 1 |
|
|
235
|
-
|
|
236
|
-
`liteModeAuto = (totalScore >= 2)`. Two of three signals true is enough; the v8.12.0..v9.0.x AND-threshold (all three) forced too many small features into Full mode when one signal was marginal (e.g. a tiny spec with 2 Figma frames). User overrides via `--full` or `--lite` always win over scoring.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Intake (analysis Phase 0)
|
|
2
2
|
|
|
3
|
-
> The picker chain that fills `state.analysisSpec.*` before any fetch runs. Loaded by `/multi-agent:analysis`. Pipeline Phase 1 does NOT load this file: in a pipeline run the account, project, repos and task already came from the orchestrator's own Phase 0, and only the source and coverage batches below are asked (see `phase-1-
|
|
3
|
+
> The picker chain that fills `state.analysisSpec.*` before any fetch runs. Loaded by `/multi-agent:analysis`. Pipeline Phase 1 does NOT load this file: in a pipeline run the account, project, repos and task already came from the orchestrator's own Phase 0, and only the source and coverage batches below are asked (see `phase-1-plan.md` Step 4).
|
|
4
4
|
|
|
5
5
|
### Phase 0 - Intake
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ Result: `state.analysisSpec.featureName` (state key kept for backward compatibil
|
|
|
19
19
|
|
|
20
20
|
#### Step 1b - Analysis profile
|
|
21
21
|
|
|
22
|
-
Asked once, immediately after the analysis name and before anything is fetched, because the profile decides which template the whole run renders against (Locked
|
|
22
|
+
Asked once, immediately after the analysis name and before anything is fetched, because the profile decides which template the whole run renders against (Locked 31). Never re-asked mid-run.
|
|
23
23
|
|
|
24
24
|
Read the available profiles from `prefs.global.analysisProfiles` (default `["global", "corporate"]`). When only one is available, auto-resolve and print the breadcrumb with the resolution noted rather than asking a question whose answer is already settled.
|
|
25
25
|
|
|
@@ -36,7 +36,7 @@ options:
|
|
|
36
36
|
|
|
37
37
|
Result: `state.analysisSpec.profile` (`global` | `corporate`). Empty submit re-asks; an empty answer does not imply the default (`feedback_no-inferred-defaults-from-empty-answer`).
|
|
38
38
|
|
|
39
|
-
The profile changes nothing about intake, fetching, repo evidence or convention extraction - those are shared. It selects the template at Phase 3 and switches the omission rule for the corporate backbone (Locked
|
|
39
|
+
The profile changes nothing about intake, fetching, repo evidence or convention extraction - those are shared. It selects the template at Phase 3 and switches the omission rule for the corporate backbone (Locked 32).
|
|
40
40
|
|
|
41
41
|
**Corporate profile bindings.** Publication targets and house terminology are read from `prefs.global.analysisProfile.corporate` when present: `confluenceSpaceKey`, `confluenceParentPageId`, `titleFormat`, `titlePrefix`, `apiSpecCommand` and a `glossary` map. The key set is closed in `prefs.schema.json`, so a typo is caught by `validate-prefs.mjs` rather than silently ignored at emit time. They are deployment configuration, not part of the shipped template: an unconfigured corporate run still renders the full document and asks for the destination at Phase 3.5 like any other run.
|
|
42
42
|
|
|
@@ -69,13 +69,13 @@ Empty submit → re-ask. Result: `state.analysisSpec.platforms[]`.
|
|
|
69
69
|
`web` is the canonical id; the pre-rename `frontend` is still read back (older state,
|
|
70
70
|
`:stack frontend`, the `ai-frontend-toolkit` plugin id) and normalises to `web`.
|
|
71
71
|
|
|
72
|
-
**`No platform yet` is a real answer, not a cancel** (Locked
|
|
72
|
+
**`No platform yet` is a real answer, not a cancel** (Locked 34). It leaves
|
|
73
73
|
`platforms[]` empty, skips Step 4 entirely, and the run continues: evidence is still
|
|
74
74
|
fetched from every declared source, and the document renders every layer that does not
|
|
75
75
|
need a target repository. Only the development layer and the Pass B projection drop,
|
|
76
76
|
and Section 20 records that they await a repo selection.
|
|
77
77
|
|
|
78
|
-
**The channel split is then derived from the evidence, not abandoned** (Locked
|
|
78
|
+
**The channel split is then derived from the evidence, not abandoned** (Locked 34,
|
|
79
79
|
which carries the reasoning). After evidence collection, classify the run's channels
|
|
80
80
|
from what the sources say:
|
|
81
81
|
|
|
@@ -135,7 +135,7 @@ tool call, and an improvised retry separates the spec sources from each other.
|
|
|
135
135
|
- **Batch 2/2 - what constrains it**: Swagger, Standards, Firebase.
|
|
136
136
|
|
|
137
137
|
**Development-layer questions are gated on a platform being selected.** With
|
|
138
|
-
`platforms[]` empty that layer does not render (Locked
|
|
138
|
+
`platforms[]` empty that layer does not render (Locked 34), so a question feeding only
|
|
139
139
|
it spends attention on an answer nothing consumes.
|
|
140
140
|
|
|
141
141
|
Two questions feed only that layer and are therefore skipped:
|
|
@@ -270,7 +270,7 @@ layer; `options.a11yDepth` gates the Section 16 VoiceOver / TalkBack walkthrough
|
|
|
270
270
|
does not. Both default to the lighter choice so the doc stays lean unless the user opts in.
|
|
271
271
|
|
|
272
272
|
`options.redesign` gates Sections 4.5, 4.6 and 9.5 and loads `analysis/redesign.md`, read
|
|
273
|
-
on no other run (Locked
|
|
273
|
+
on no other run (Locked 36).
|
|
274
274
|
|
|
275
275
|
#### Step 5b - Repo-evidence collector (automatic, no prompt)
|
|
276
276
|
|