@mmerterden/multi-agent-pipeline 12.7.0 → 12.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 +126 -0
- package/install/_common.mjs +48 -0
- package/install/_dev-only-files.mjs +125 -5
- package/install/claude.mjs +14 -8
- package/install/copilot.mjs +5 -8
- package/package.json +17 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/lib/credential-store.sh +20 -0
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -0
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +49 -4
- package/pipeline/schemas/prefs.schema.json +6 -0
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/audit-log.sh +25 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
- package/pipeline/eval/golden-tasks/README.md +0 -65
- package/pipeline/eval/intent-cases.json +0 -40
- package/pipeline/eval/run-metrics-fixture.json +0 -93
- package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
- package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
- package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
- package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
- package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
- package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
- package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
- package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
- package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
- package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
- package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
- package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
- package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
- package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
- package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
- package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
- package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
- package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
- package/pipeline/eval/triage/README.md +0 -54
- package/pipeline/scripts/benchmark-phase-0.sh +0 -128
- package/pipeline/scripts/check-md-links.mjs +0 -88
- package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
- package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
- package/pipeline/scripts/eval-intent.mjs +0 -107
- package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
- package/pipeline/scripts/eval-triage.mjs +0 -171
- package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
- package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
- package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
- package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
- package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
- package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
- package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
- package/pipeline/scripts/lint-skills.mjs +0 -154
- package/pipeline/scripts/run-smokes.mjs +0 -130
- package/pipeline/scripts/scorecard.mjs +0 -258
- package/pipeline/scripts/smoke-add-detail.sh +0 -137
- package/pipeline/scripts/smoke-agent-guard.sh +0 -74
- package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
- package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
- package/pipeline/scripts/smoke-ask-choice.sh +0 -42
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
- package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
- package/pipeline/scripts/smoke-changelog-version.sh +0 -47
- package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
- package/pipeline/scripts/smoke-channels-flow.sh +0 -130
- package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
- package/pipeline/scripts/smoke-clarify.sh +0 -148
- package/pipeline/scripts/smoke-command-inventory.sh +0 -81
- package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
- package/pipeline/scripts/smoke-community-gates.sh +0 -75
- package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
- package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
- package/pipeline/scripts/smoke-context-budget.sh +0 -72
- package/pipeline/scripts/smoke-cost-budget.sh +0 -70
- package/pipeline/scripts/smoke-cost-summary.sh +0 -139
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
- package/pipeline/scripts/smoke-description-tr.sh +0 -82
- package/pipeline/scripts/smoke-dev-critic.sh +0 -144
- package/pipeline/scripts/smoke-diff-explain.sh +0 -147
- package/pipeline/scripts/smoke-diff-risk.sh +0 -190
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
- package/pipeline/scripts/smoke-eval-live.sh +0 -136
- package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
- package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
- package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
- package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
- package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
- package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
- package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
- package/pipeline/scripts/smoke-generate-issue.sh +0 -120
- package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
- package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
- package/pipeline/scripts/smoke-install-layout.sh +0 -248
- package/pipeline/scripts/smoke-intent-guard.sh +0 -86
- package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
- package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
- package/pipeline/scripts/smoke-keychain.sh +0 -158
- package/pipeline/scripts/smoke-language-axis.sh +0 -109
- package/pipeline/scripts/smoke-learning-curve.sh +0 -61
- package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
- package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
- package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
- package/pipeline/scripts/smoke-md-links.sh +0 -8
- package/pipeline/scripts/smoke-md2confluence.sh +0 -126
- package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
- package/pipeline/scripts/smoke-migrate-state.sh +0 -102
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
- package/pipeline/scripts/smoke-model-fallback.sh +0 -89
- package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
- package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
- package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
- package/pipeline/scripts/smoke-pack-contents.sh +0 -140
- package/pipeline/scripts/smoke-pat-audit.sh +0 -128
- package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
- package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
- package/pipeline/scripts/smoke-phase-banner.sh +0 -101
- package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
- package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
- package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
- package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
- package/pipeline/scripts/smoke-plan-safety.sh +0 -139
- package/pipeline/scripts/smoke-plan-todos.sh +0 -196
- package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
- package/pipeline/scripts/smoke-pre-commit.sh +0 -170
- package/pipeline/scripts/smoke-pref-migration.sh +0 -226
- package/pipeline/scripts/smoke-prefs-language.sh +0 -134
- package/pipeline/scripts/smoke-progress-contract.sh +0 -127
- package/pipeline/scripts/smoke-prune-logs.sh +0 -137
- package/pipeline/scripts/smoke-purge.sh +0 -138
- package/pipeline/scripts/smoke-push-retry.sh +0 -75
- package/pipeline/scripts/smoke-repo-map.sh +0 -300
- package/pipeline/scripts/smoke-review-readiness.sh +0 -92
- package/pipeline/scripts/smoke-review-watch.sh +0 -146
- package/pipeline/scripts/smoke-routines.sh +0 -84
- package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
- package/pipeline/scripts/smoke-run-metrics.sh +0 -50
- package/pipeline/scripts/smoke-search.sh +0 -187
- package/pipeline/scripts/smoke-shadow-git.sh +0 -224
- package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
- package/pipeline/scripts/smoke-skill-language.sh +0 -83
- package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
- package/pipeline/scripts/smoke-skill-scan.sh +0 -198
- package/pipeline/scripts/smoke-source-parity.sh +0 -85
- package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
- package/pipeline/scripts/smoke-sync-parity.sh +0 -92
- package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
- package/pipeline/scripts/smoke-telemetry.sh +0 -147
- package/pipeline/scripts/smoke-test-gap.sh +0 -183
- package/pipeline/scripts/smoke-token-budget.sh +0 -67
- package/pipeline/scripts/smoke-token-preflight.sh +0 -82
- package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
- package/pipeline/scripts/smoke-triage-memory.sh +0 -174
- package/pipeline/scripts/smoke-update-check.sh +0 -135
- package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
- package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
- package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
- package/pipeline/scripts/smoke-validator-gates.sh +0 -164
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
- package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
- package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
- package/pipeline/scripts/smoke-work-summary.sh +0 -163
- package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
- package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
- package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
- package/pipeline/scripts/smoke-write-state.sh +0 -159
- package/pipeline/scripts/sync-parity-check.sh +0 -135
- package/pipeline/scripts/test-gap-rules/android.json +0 -25
- package/pipeline/scripts/test-gap-rules/ios.json +0 -34
- package/pipeline/scripts/test-gap-rules/node.json +0 -29
- package/pipeline/scripts/test-gap-rules/python.json +0 -25
- package/pipeline/scripts/validate-schemas.mjs +0 -88
|
@@ -112,11 +112,16 @@ Cost ledger: `phase-4.repo_map_emitted bytes=N budget=B cache_hit=true|false` -
|
|
|
112
112
|
|
|
113
113
|
Before dispatching reviewers, run the deterministic diff risk scorer and inject the top-N files into each reviewer's prompt as a priority hint. **Advisory only - never gates the pipeline.** Heuristic, zero LLM, runs in well under a second.
|
|
114
114
|
|
|
115
|
+
Score the diff **once, in full** (no `--top`): Steps 1.76/1.77 need every scored file (a shrinking test file ranked 20th; whether ANY file is high-stakes). The top-N hint is derived from that report, not a second git walk.
|
|
116
|
+
|
|
115
117
|
```bash
|
|
116
|
-
|
|
118
|
+
RISK_FULL=$(node pipeline/scripts/diff-risk-score.mjs \
|
|
117
119
|
--base "$BASE_BRANCH" --head HEAD \
|
|
118
|
-
--task-id "$TASK_ID"
|
|
119
|
-
echo "$
|
|
120
|
+
--task-id "$TASK_ID" 2>/dev/null)
|
|
121
|
+
echo "$RISK_FULL" | node pipeline/scripts/validate-diff-risk.mjs - >/dev/null 2>&1 || RISK_FULL=""
|
|
122
|
+
|
|
123
|
+
# Priority hint for the reviewer prompts: top 5 of the full report.
|
|
124
|
+
RISK_JSON=$([ -n "$RISK_FULL" ] && jq -c '.files |= (sort_by(-.score) | .[:5])' <<< "$RISK_FULL" || echo "")
|
|
120
125
|
```
|
|
121
126
|
|
|
122
127
|
**Signals & weights** (see `pipeline/schemas/diff-risk.schema.json`):
|
|
@@ -151,6 +156,35 @@ pipeline/scripts/log-metric.sh "$TASK_ID" 4 review.diff_risk \
|
|
|
151
156
|
|
|
152
157
|
**Opt-out**: `prefs.global.diffRiskAdvisory = false` skips this step entirely (no script invocation, no priority block injection). Default `true` because the cost is bounded and the signal-to-noise has been measured against the golden-task fixture set.
|
|
153
158
|
|
|
159
|
+
#### Step 1.76 - Test-integrity gate (produces BLOCKING findings)
|
|
160
|
+
|
|
161
|
+
Step 1.75 uses `test_lines_removed` as an advisory hint only - too weak for what it detects: a suite made green by deleting tests instead of fixing code. This turns the signal into blocking findings triage must adjudicate. Pure function of the full report, no git, no LLM.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
TEST_INTEGRITY_JSON=$(printf '%s' "$RISK_FULL" | node pipeline/scripts/test-integrity-gate.mjs 2>/dev/null || echo "")
|
|
165
|
+
TI_COUNT=$(jq -r '.count // 0' <<< "${TEST_INTEGRITY_JSON:-{\}}" 2>/dev/null || echo 0)
|
|
166
|
+
[ "$TI_COUNT" -gt 0 ] && pipeline/scripts/log-metric.sh "$TASK_ID" 4 review.test_integrity findings="$TI_COUNT"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`findings[]` are reviewer-shaped (`test_integrity`, `blocking`), so they merge into the reviewer findings at Step 3.0 and need no triage-prompt or `validate-triage.mjs` change. Triage keeps each blocking unless the removal is justified per the immutable-test rule (spec changed AND commit body names the test) → `deferred[]`.
|
|
170
|
+
|
|
171
|
+
The gate never blocks the phase; it *emits* blocking findings. Empty or unreadable input yields zero findings and the phase continues. Feed it the FULL report - `--top` hides a shrinking test file below the cut. **No opt-out**: a run that can switch off its own anti-reward-hacking control cannot be trusted to report a pass.
|
|
172
|
+
|
|
173
|
+
#### Step 1.77 - Reviewer scope (cost gate)
|
|
174
|
+
|
|
175
|
+
On a trivial diff every reviewer agrees and the extra models plus triage are paid for nothing. Reviewer count comes from the same report, no LLM.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
SCOPE_JSON=$(printf '%s' "$RISK_FULL" | node pipeline/scripts/review-scope.mjs 2>/dev/null \
|
|
179
|
+
|| echo '{"scope":"full","reason":"no risk report - failing safe"}')
|
|
180
|
+
REVIEW_SCOPE=$(jq -r '.scope // "full"' <<< "$SCOPE_JSON")
|
|
181
|
+
pipeline/scripts/log-metric.sh "$TASK_ID" 4 review.scope scope="$REVIEW_SCOPE"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`single` (Reviewer 1 only) requires **all** of: churn <= 20 lines, `totals.max_score` < 3.0, and no `security_path` / `migration` / `public_api` / `no_test_change` / `test_lines_removed` on any file. Anything else → `full`.
|
|
185
|
+
|
|
186
|
+
Fails safe in one direction only: empty report, parse failure or validator rejection all resolve to `full`, because skipping a reviewer trades coverage for cost. `consensus.reviewerCount` records what actually ran, so a single-reviewer run never reads as cross-model agreement. Opt-out: `prefs.global.reviewScopeGate = false` forces `full`.
|
|
187
|
+
|
|
154
188
|
#### Step 1.8 - Figma visual-fidelity context (when task carries a Figma reference)
|
|
155
189
|
|
|
156
190
|
When `state.evidence.figma[]` is non-empty, the reviewer subagents MUST receive the captured screenshot URLs / paths and the canonical-component name (from `state.evidence.figma[i].screenshotUrl` and `state.evidence.figma[i].codeConnectSnippets[0].componentName` when present) so they can compare visual fidelity. Pass them inline in each reviewer prompt under a `## Figma evidence` block, one row per frame.
|
|
@@ -182,6 +216,8 @@ Phase 4 sends the same diff to every reviewer and then to triage, so the diff is
|
|
|
182
216
|
|
|
183
217
|
Launch Agent instances **in parallel** using the shared `code-reviewer` subagent definition (`~/.claude/agents/code-reviewer.md`). The reviewer set is determined by the host CLI - GPT-5.4 is only available on Copilot CLI, so Claude Code skips that reviewer and runs a 2-model parallel review; Copilot CLI runs all three.
|
|
184
218
|
|
|
219
|
+
**Scope from Step 1.77.** `$REVIEW_SCOPE == "single"` → dispatch **Reviewer 1 only**, and skip Step 2.5 + 3.6 (both no-ops with one reviewer). `"full"` (default + fail-safe) → the whole set below. Either way record the count in `consensus.reviewerCount`.
|
|
220
|
+
|
|
185
221
|
| Reviewer | subagent_type | Model | Focus | Skills Referenced | Where it runs |
|
|
186
222
|
| ---------- | ----------------- | ------------------- | --------------------------------- | --------------------------------------------- | -------------------- |
|
|
187
223
|
| Reviewer 1 | `code-reviewer` | `claude-fable-5` (Claude Code) / `claude-opus-4-8` (Copilot CLI) | Deep security + architecture | `api-security-best-practices`, `architecture` | Both CLIs |
|
|
@@ -259,9 +295,18 @@ Exit 0 = valid. Exit 2 = contradiction (approved=true with blocking findings) -
|
|
|
259
295
|
|
|
260
296
|
Opt-in empirical layer: when `prefs.global.verifyByTest.enabled` is `true`, accepted blocking findings additionally go through Step 3.7 (verify-by-test), which tries to reproduce each one with a minimal failing test before the Phase 3 rework loop fires. Full wiring: `$HOME/.claude/multi-agent-refs/features/verify-by-test.md`.
|
|
261
297
|
|
|
298
|
+
##### 3.0 Merge the deterministic findings in
|
|
299
|
+
|
|
300
|
+
Append the Step 1.76 test-integrity findings to the reviewer findings before counting, so they are adjudicated like any reviewer finding rather than resolved by triage never seeing them:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
MERGED=$(jq -s '.[0] + (.[1].findings // [])' \
|
|
304
|
+
<(printf '%s' "$REVIEWER_FINDINGS_JSON") <(printf '%s' "${TEST_INTEGRITY_JSON:-{\}}"))
|
|
305
|
+
```
|
|
306
|
+
|
|
262
307
|
##### 3.1 Short-circuit: no findings
|
|
263
308
|
|
|
264
|
-
If merged findings `length === 0`, **skip triage**: write empty result `{"accepted": [], "deferred": [], "rejected": [], "approved": true}`, log, proceed to Phase 5.
|
|
309
|
+
If **merged** findings `length === 0`, **skip triage**: write empty result `{"accepted": [], "deferred": [], "rejected": [], "approved": true}`, log, proceed to Phase 5. Note this is the merged count from 3.0: a run with zero reviewer findings but a non-empty test-integrity set must NOT short-circuit.
|
|
265
310
|
|
|
266
311
|
##### 3.2 Launch triage agent
|
|
267
312
|
|
|
@@ -960,6 +960,12 @@
|
|
|
960
960
|
"default": true,
|
|
961
961
|
"description": "v8.3+ - Phase 4 Step 1.75 advisory diff risk scoring. When enabled, `pipeline/scripts/diff-risk-score.mjs` runs before reviewer dispatch and the top-N risk-ranked files are injected into each reviewer's prompt as a priority hint (security paths, public API surfaces, untested source changes, schema migrations). Heuristic, deterministic, no LLM cost. Default ON - the run is sub-second and never gates the pipeline. Flip to false to skip the script and the prompt injection entirely."
|
|
962
962
|
},
|
|
963
|
+
"reviewScopeGate": {
|
|
964
|
+
"type": "boolean",
|
|
965
|
+
"default": true,
|
|
966
|
+
"description": "v12.8+ - Phase 4 Step 1.77 reviewer-scope gate. Decides the reviewer count from the deterministic diff-risk report via `pipeline/scripts/review-scope.mjs`: a diff under 20 lines of churn with max_score < 3.0 and no security_path / migration / public_api / no_test_change / test_lines_removed signal runs ONE reviewer instead of the full CLI-aware set (2 on Claude Code, 3 on Copilot CLI). No LLM. Fails safe in one direction only - any error, empty report or validator rejection resolves to the full set, because skipping a reviewer trades coverage for cost. Set false to force the full set on every diff.",
|
|
967
|
+
"$comment": "Shipped inert for a release: the script existed, was unit- and smoke-tested, and no phase doc referenced it, so every diff paid for the full reviewer set. Wired in Step 1.77; smoke-gate-wiring.sh keeps it reachable."
|
|
968
|
+
},
|
|
963
969
|
"priorArtEnrichment": {
|
|
964
970
|
"type": "object",
|
|
965
971
|
"additionalProperties": false,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# _smoke-root.sh - resolve the pipeline tree for a smoke that must run in BOTH
|
|
4
|
+
# the repo checkout and an end-user install.
|
|
5
|
+
#
|
|
6
|
+
# Why this exists: the repo and an install have different shapes.
|
|
7
|
+
#
|
|
8
|
+
# repo checkout install (~/.claude or ~/.copilot)
|
|
9
|
+
# ------------------ --------------------------------
|
|
10
|
+
# pipeline/scripts/ scripts/
|
|
11
|
+
# pipeline/schemas/ schemas/
|
|
12
|
+
# pipeline/multi-agent-refs/ multi-agent-refs/
|
|
13
|
+
# pipeline/commands/ commands/
|
|
14
|
+
# package.json (absent)
|
|
15
|
+
#
|
|
16
|
+
# A smoke that hardcodes `REPO_ROOT="$(dirname "$0")/../.."` and then reads
|
|
17
|
+
# `$REPO_ROOT/pipeline/...` resolves to `$HOME/pipeline/...` once installed, so
|
|
18
|
+
# every path check fails. Whoever ran it sees a red report and concludes the
|
|
19
|
+
# install is broken. Source this file instead and read the MA_* variables.
|
|
20
|
+
#
|
|
21
|
+
# Usage (from any script in pipeline/scripts/):
|
|
22
|
+
# . "$(dirname "${BASH_SOURCE[0]}")/_smoke-root.sh"
|
|
23
|
+
# grep -q needle "$MA_REFS/phases/phase-4-review.md"
|
|
24
|
+
#
|
|
25
|
+
# Exports:
|
|
26
|
+
# MA_LAYOUT "repo" | "install"
|
|
27
|
+
# MA_ROOT repo root, or the install root (~/.claude)
|
|
28
|
+
# MA_SCRIPTS directory holding this script
|
|
29
|
+
# MA_SCHEMAS JSON schemas
|
|
30
|
+
# MA_REFS multi-agent-refs
|
|
31
|
+
# MA_COMMANDS slash-command tree
|
|
32
|
+
# MA_SKILLS skills tree
|
|
33
|
+
#
|
|
34
|
+
# Every exported directory is verified to exist; a missing one is left empty so
|
|
35
|
+
# the caller can skip that check rather than report a false failure.
|
|
36
|
+
|
|
37
|
+
MA_SCRIPTS="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
38
|
+
|
|
39
|
+
# The repo is the only layout with pipeline/ beside package.json. Checking both
|
|
40
|
+
# rules out an install that happens to sit next to an unrelated pipeline/ dir.
|
|
41
|
+
_ma_candidate="$(cd "$MA_SCRIPTS/../.." && pwd)"
|
|
42
|
+
if [ -f "$_ma_candidate/package.json" ] && [ -d "$_ma_candidate/pipeline" ]; then
|
|
43
|
+
MA_LAYOUT="repo"
|
|
44
|
+
MA_ROOT="$_ma_candidate"
|
|
45
|
+
_ma_base="$_ma_candidate/pipeline"
|
|
46
|
+
else
|
|
47
|
+
MA_LAYOUT="install"
|
|
48
|
+
MA_ROOT="$(cd "$MA_SCRIPTS/.." && pwd)"
|
|
49
|
+
_ma_base="$MA_ROOT"
|
|
50
|
+
fi
|
|
51
|
+
unset _ma_candidate
|
|
52
|
+
|
|
53
|
+
_ma_dir() { [ -d "$1" ] && echo "$1" || echo ""; }
|
|
54
|
+
|
|
55
|
+
MA_SCHEMAS="$(_ma_dir "$_ma_base/schemas")"
|
|
56
|
+
MA_REFS="$(_ma_dir "$_ma_base/multi-agent-refs")"
|
|
57
|
+
MA_COMMANDS="$(_ma_dir "$_ma_base/commands")"
|
|
58
|
+
MA_SKILLS="$(_ma_dir "$_ma_base/skills")"
|
|
59
|
+
unset _ma_base
|
|
60
|
+
|
|
61
|
+
export MA_LAYOUT MA_ROOT MA_SCRIPTS MA_SCHEMAS MA_REFS MA_COMMANDS MA_SKILLS
|
|
@@ -80,3 +80,28 @@ LINE="{\"ts\":\"${TS}\",\"event\":\"$(json_escape "$EVENT")\",\"service\":\"$(js
|
|
|
80
80
|
LINE="${LINE}}"
|
|
81
81
|
|
|
82
82
|
echo "$LINE" >> "$AUDIT_FILE" 2>/dev/null || true
|
|
83
|
+
|
|
84
|
+
# Opportunistic rotation. audit-log-rotate.sh documents a launchd/cron install,
|
|
85
|
+
# but nothing ever told a user to set that up, so an unrotated trail would grow
|
|
86
|
+
# without bound now that credential-store.sh writes an event on every PAT lookup.
|
|
87
|
+
# Self-triggering removes the operator step: check a cheap size threshold and hand
|
|
88
|
+
# off to the real rotator, which is atomic against concurrent appenders (it moves
|
|
89
|
+
# the live inode first). Silent and non-blocking - telemetry never fails a run.
|
|
90
|
+
ROTATE_AT_BYTES="${AUDIT_ROTATE_AT_BYTES:-1048576}"
|
|
91
|
+
_audit_size() {
|
|
92
|
+
case "$(uname -s)" in
|
|
93
|
+
Darwin|*BSD*) stat -f %z "$1" 2>/dev/null || echo 0 ;;
|
|
94
|
+
*) stat -c %s "$1" 2>/dev/null || echo 0 ;;
|
|
95
|
+
esac
|
|
96
|
+
}
|
|
97
|
+
if [ "${AUDIT_SELF_ROTATE:-1}" = "1" ] && [ -f "$AUDIT_FILE" ]; then
|
|
98
|
+
SIZE="$(_audit_size "$AUDIT_FILE")"
|
|
99
|
+
if [ "${SIZE:-0}" -ge "$ROTATE_AT_BYTES" ]; then
|
|
100
|
+
ROTATOR="$(dirname "${BASH_SOURCE[0]:-$0}")/audit-log-rotate.sh"
|
|
101
|
+
# The rotator derives AUDIT_FILE from AUDIT_DIR, so pass the DIRECTORY.
|
|
102
|
+
# Handing it AUDIT_FILE would leave it rotating the default path while a
|
|
103
|
+
# caller-overridden trail kept growing.
|
|
104
|
+
[ -f "$ROTATOR" ] &&
|
|
105
|
+
AUDIT_DIR="$(dirname "$AUDIT_FILE")" bash "$ROTATOR" >/dev/null 2>&1 || true
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
@@ -190,22 +190,36 @@ fi
|
|
|
190
190
|
# (the orchestrator dispatches per the doc), so lock the CONTRACT here against
|
|
191
191
|
# drift across the phase doc, the schema, and the consensus block.
|
|
192
192
|
echo "→ reviewer-count contract (Claude=2, Copilot=3)"
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
# `/multi-agent:update` runs this smoke on the user's machine, where the tree is
|
|
194
|
+
# ~/.claude/{schemas,multi-agent-refs} and NOT <root>/pipeline/*. Resolving the
|
|
195
|
+
# root by hand here reported three phantom failures in an install, which reads as
|
|
196
|
+
# a broken installation. _smoke-root.sh handles both layouts.
|
|
197
|
+
# shellcheck source=pipeline/scripts/_smoke-root.sh
|
|
198
|
+
. "$(dirname "${BASH_SOURCE[0]}")/_smoke-root.sh"
|
|
199
|
+
P4="${MA_REFS:+$MA_REFS/phases/phase-4-review.md}"
|
|
200
|
+
REVSCHEMA="${MA_SCHEMAS:+$MA_SCHEMAS/reviewer-output.schema.json}"
|
|
201
|
+
TRSCHEMA="${MA_SCHEMAS:+$MA_SCHEMAS/triage-output.schema.json}"
|
|
202
|
+
|
|
203
|
+
if [ -z "$P4" ] || [ ! -f "$P4" ]; then
|
|
204
|
+
echo " ↷ SKIP: phase-4-review.md not present in this $MA_LAYOUT layout"
|
|
205
|
+
elif grep -qiE "Claude Code (dispatches|=|:) ?2|2-model" "$P4" && grep -qiE "Copilot CLI (dispatches|=|:) ?3|3-model" "$P4"; then
|
|
198
206
|
pass "phase-4-review declares Claude=2 / Copilot=3 reviewers"
|
|
199
207
|
else
|
|
200
208
|
fail "phase-4-review does not declare the CLI-aware reviewer count"
|
|
201
209
|
fi
|
|
202
|
-
|
|
210
|
+
|
|
211
|
+
if [ -z "$REVSCHEMA" ] || [ ! -f "$REVSCHEMA" ]; then
|
|
212
|
+
echo " ↷ SKIP: reviewer-output.schema.json not present in this $MA_LAYOUT layout"
|
|
213
|
+
elif grep -qi "Copilot CLI" "$REVSCHEMA" && grep -qi "Claude" "$REVSCHEMA"; then
|
|
203
214
|
pass "reviewer-output schema notes the CLI-aware reviewer set"
|
|
204
215
|
else
|
|
205
216
|
fail "reviewer-output schema missing the CLI-aware note"
|
|
206
217
|
fi
|
|
218
|
+
|
|
207
219
|
# consensus.reviewerCount must accommodate both 2 and 3 (min 1, no max < 3)
|
|
208
|
-
if
|
|
220
|
+
if [ -z "$TRSCHEMA" ] || [ ! -f "$TRSCHEMA" ]; then
|
|
221
|
+
echo " ↷ SKIP: triage-output.schema.json not present in this $MA_LAYOUT layout"
|
|
222
|
+
elif grep -q '"reviewerCount"' "$TRSCHEMA" && grep -qi "Claude Code = 2, Copilot CLI = 3" "$TRSCHEMA"; then
|
|
209
223
|
pass "triage consensus.reviewerCount documents 2 (Claude) and 3 (Copilot)"
|
|
210
224
|
else
|
|
211
225
|
fail "triage consensus.reviewerCount does not document the 2/3 split"
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"stack": {
|
|
3
|
-
"primary": "ios",
|
|
4
|
-
"language": "Swift 6",
|
|
5
|
-
"framework": "SwiftUI"
|
|
6
|
-
},
|
|
7
|
-
"touchedAreas": [
|
|
8
|
-
{
|
|
9
|
-
"path": "Sources/Auth/LoginView.swift",
|
|
10
|
-
"why": "Hardcoded Color.white yerine DesignSystem.Color.Primary.onBackground kullanacak"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"path": "Sources/Auth/LoginViewTests.swift",
|
|
14
|
-
"why": "Dark mode snapshot + ViewInspector contrast assertion eklenecek"
|
|
15
|
-
}
|
|
16
|
-
],
|
|
17
|
-
"risks": [
|
|
18
|
-
{
|
|
19
|
-
"risk": "LoginView başka ekran tarafından preview alanı olarak kullanılıyor olabilir - token değişimi preview snapshot'larını kırabilir",
|
|
20
|
-
"severity": "low",
|
|
21
|
-
"mitigation": "Preview varyantlarını snapshot'lar ile koruyarak incremental değiştir"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"summary": "LoginView'de primary button rengi hardcoded Color.white. Token sistemi ile değiştirilip light+dark mode için ayrık snapshot testleri eklenecek. Değişim iki dosya ile sınırlı, regresyon riski düşük."
|
|
25
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"approach": "TDD-first. Önce dark-mode contrast için snapshot + ViewInspector testi yazılır (RED), sonra LoginView'de hardcoded renk token çağrısıyla değiştirilir (GREEN). Refactor: aynı dosyada ikinci hardcoded renk varsa tek geçişte temizlenir.",
|
|
3
|
-
"tasks": [
|
|
4
|
-
{
|
|
5
|
-
"id": "T1",
|
|
6
|
-
"title": "Add failing dark mode snapshot + contrast ViewInspector test",
|
|
7
|
-
"type": "test",
|
|
8
|
-
"files": ["Sources/Auth/LoginViewTests.swift"],
|
|
9
|
-
"dependsOn": [],
|
|
10
|
-
"acceptanceCriteria": [
|
|
11
|
-
"Snapshot test for dark mode renders primary button",
|
|
12
|
-
"ViewInspector asserts foreground color != Color.white when env.colorScheme == .dark"
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"id": "T2",
|
|
17
|
-
"title": "Replace hardcoded Color.white with DesignSystem token",
|
|
18
|
-
"type": "code",
|
|
19
|
-
"files": ["Sources/Auth/LoginView.swift"],
|
|
20
|
-
"dependsOn": ["T1"],
|
|
21
|
-
"acceptanceCriteria": [
|
|
22
|
-
"No Color(red:..., green:..., blue:...) or Color.white remains in LoginView",
|
|
23
|
-
"Primary button uses DesignSystem.Color.Primary.onBackground",
|
|
24
|
-
"All snapshot + inspector tests pass"
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"approved": true,
|
|
29
|
-
"userFeedback": null
|
|
30
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"reviewer": "opus",
|
|
4
|
-
"findings": [
|
|
5
|
-
{
|
|
6
|
-
"severity": "blocking",
|
|
7
|
-
"file": "Sources/Auth/LoginView.swift",
|
|
8
|
-
"line": 42,
|
|
9
|
-
"issue": "Hardcoded .font(.system(size: 14)) kaldı - aynı fix sırasında typography token'ına da bağlanmalı",
|
|
10
|
-
"fix": "Replace with .typographyStyle(.body1) - same pattern as the color token migration"
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
"approved": false
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"reviewer": "sonnet",
|
|
17
|
-
"findings": [],
|
|
18
|
-
"approved": true
|
|
19
|
-
}
|
|
20
|
-
]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"accepted": [
|
|
3
|
-
{
|
|
4
|
-
"severity": "blocking",
|
|
5
|
-
"file": "Sources/Auth/LoginView.swift",
|
|
6
|
-
"line": 42,
|
|
7
|
-
"issue": "Hardcoded .font(.system(size: 14)) kaldı - aynı fix sırasında typography token'ına da bağlanmalı",
|
|
8
|
-
"fix": "Replace with .typographyStyle(.body1) - same pattern as the color token migration",
|
|
9
|
-
"reviewer": "opus"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
"deferred": [],
|
|
13
|
-
"rejected": [],
|
|
14
|
-
"approved": false
|
|
15
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"focus": "SwiftUI bugfix path - verifies that Phase 4 picks up an in-scope but adjacent blocker (token drift) even when the primary issue is resolved.",
|
|
3
|
-
"exercises": [
|
|
4
|
-
"Phase 1 stack detection (iOS / Swift 6 / SwiftUI)",
|
|
5
|
-
"Phase 2 TDD ordering (test before code)",
|
|
6
|
-
"Phase 4 Reviewer-1 catches adjacent token drift; Reviewer-2 approves base fix",
|
|
7
|
-
"Triage keeps the blocker accepted, no deferrals, not approved"
|
|
8
|
-
],
|
|
9
|
-
"regressionsCaught": [
|
|
10
|
-
"If stack detection drops SwiftUI from the framework field, this fixture fails the analysis schema check",
|
|
11
|
-
"If planning schema drops the 'dependsOn' field, T2 loses its order guarantee",
|
|
12
|
-
"If Phase 4 no longer tolerates Reviewer-1 blocker + Reviewer-2 approval (approved divergence), triage fixture fails"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "LoginView dark mode contrast fix",
|
|
3
|
-
"description": "Dark mode'da primary button text'i zemin rengiyle düşük kontrast veriyor. Tokens switch'i çalışmıyor çünkü LoginView hardcoded Color.white kullanıyor. Design tokens'a geri bağla.",
|
|
4
|
-
"inputType": "free-text",
|
|
5
|
-
"expectedStack": "ios",
|
|
6
|
-
"expectedLanguage": "Swift",
|
|
7
|
-
"expectedFramework": "SwiftUI",
|
|
8
|
-
"expectedBlockers": 1,
|
|
9
|
-
"expectedDeferrals": 0,
|
|
10
|
-
"baseBranch": "develop",
|
|
11
|
-
"taskBranch": "bugfix/loginview-dark-mode-contrast"
|
|
12
|
-
}
|
package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"stack": {
|
|
3
|
-
"primary": "android",
|
|
4
|
-
"language": "Kotlin 2.1",
|
|
5
|
-
"framework": "Jetpack Compose"
|
|
6
|
-
},
|
|
7
|
-
"touchedAreas": [
|
|
8
|
-
{
|
|
9
|
-
"path": "feature/orders/src/main/java/com/example/orders/OrdersScreen.kt",
|
|
10
|
-
"why": "PullToRefreshBox UI eklenecek, ViewModel.refresh() tetiklenecek"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"path": "feature/orders/src/main/java/com/example/orders/OrdersViewModel.kt",
|
|
14
|
-
"why": "isRefreshing state flow eklenecek - PullToRefreshBox'ın state binding'i için"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"path": "feature/orders/src/test/java/com/example/orders/OrdersScreenTest.kt",
|
|
18
|
-
"why": "Pull-to-refresh compose testi eklenecek"
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"risks": [
|
|
22
|
-
{
|
|
23
|
-
"risk": "Material3 PullToRefreshBox henüz beta API - 1.3.x'de signature değişti",
|
|
24
|
-
"severity": "medium",
|
|
25
|
-
"mitigation": "Existing libs.versions.toml'de material3 sürümünü teyit et, gerekirse bump et"
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"summary": "OrdersScreen Compose ekranına Material3 pull-to-refresh eklenir. ViewModel'de zaten refresh() var; UI binding + isRefreshing state + Compose testi yeterli. Material3 API surface'i beta olduğu için version check gerekir, risk orta."
|
|
29
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"approach": "ViewModel state genişletilir (isRefreshing StateFlow), UI PullToRefreshBox'a bağlanır, Compose test yazılır. TDD: önce Compose testi refresh davranışını doğrulamalı (RED), sonra ViewModel + UI bağlanır.",
|
|
3
|
-
"tasks": [
|
|
4
|
-
{
|
|
5
|
-
"id": "T1",
|
|
6
|
-
"title": "Add failing Compose test for pull-to-refresh trigger",
|
|
7
|
-
"type": "test",
|
|
8
|
-
"files": ["feature/orders/src/test/java/com/example/orders/OrdersScreenTest.kt"],
|
|
9
|
-
"dependsOn": [],
|
|
10
|
-
"acceptanceCriteria": [
|
|
11
|
-
"Test performTouchInput { swipeDown() } on OrdersScreen",
|
|
12
|
-
"Asserts ViewModel.refresh() is called",
|
|
13
|
-
"Test initially fails because PullToRefreshBox is not wired"
|
|
14
|
-
]
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"id": "T2",
|
|
18
|
-
"title": "Add isRefreshing StateFlow to OrdersViewModel",
|
|
19
|
-
"type": "code",
|
|
20
|
-
"files": ["feature/orders/src/main/java/com/example/orders/OrdersViewModel.kt"],
|
|
21
|
-
"dependsOn": ["T1"],
|
|
22
|
-
"acceptanceCriteria": [
|
|
23
|
-
"isRefreshing: StateFlow<Boolean> exposed",
|
|
24
|
-
"refresh() sets isRefreshing = true, then false on completion"
|
|
25
|
-
]
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"id": "T3",
|
|
29
|
-
"title": "Wire PullToRefreshBox in OrdersScreen",
|
|
30
|
-
"type": "code",
|
|
31
|
-
"files": ["feature/orders/src/main/java/com/example/orders/OrdersScreen.kt"],
|
|
32
|
-
"dependsOn": ["T2"],
|
|
33
|
-
"acceptanceCriteria": [
|
|
34
|
-
"PullToRefreshBox wraps the list",
|
|
35
|
-
"isRefreshing bound from ViewModel state",
|
|
36
|
-
"onRefresh calls viewModel::refresh",
|
|
37
|
-
"Compose test from T1 passes"
|
|
38
|
-
]
|
|
39
|
-
}
|
|
40
|
-
],
|
|
41
|
-
"approved": true,
|
|
42
|
-
"userFeedback": null
|
|
43
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"reviewer": "opus",
|
|
4
|
-
"findings": [
|
|
5
|
-
{
|
|
6
|
-
"severity": "blocking",
|
|
7
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersViewModel.kt",
|
|
8
|
-
"line": 28,
|
|
9
|
-
"issue": "refresh() içinde isRefreshing = false finally bloğunda değil - exception fırlatıldığında spinner sonsuza dek takılır",
|
|
10
|
-
"fix": "Wrap the body in try/finally and set _isRefreshing.value = false in finally"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"severity": "important",
|
|
14
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersScreen.kt",
|
|
15
|
-
"line": 0,
|
|
16
|
-
"issue": "Yeni modifier chain lazılarla LazyColumn semantics test matcher'larını kırabilir - Compose stability endişesi var",
|
|
17
|
-
"fix": "Measure recomposition count with Compose Compiler Reports before merging"
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
"approved": false
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"reviewer": "sonnet",
|
|
24
|
-
"findings": [
|
|
25
|
-
{
|
|
26
|
-
"severity": "important",
|
|
27
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersScreen.kt",
|
|
28
|
-
"line": 54,
|
|
29
|
-
"issue": "PullToRefreshState hoisted yerine composable içinde tutuluyor - configuration change'de state kaybı",
|
|
30
|
-
"fix": "Use rememberSaveable or hoist the state up to ViewModel"
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
"approved": false
|
|
34
|
-
}
|
|
35
|
-
]
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"accepted": [
|
|
3
|
-
{
|
|
4
|
-
"severity": "blocking",
|
|
5
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersViewModel.kt",
|
|
6
|
-
"line": 28,
|
|
7
|
-
"issue": "refresh() içinde isRefreshing = false finally bloğunda değil - exception fırlatıldığında spinner sonsuza dek takılır",
|
|
8
|
-
"fix": "Wrap the body in try/finally and set _isRefreshing.value = false in finally",
|
|
9
|
-
"reviewer": "opus"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"severity": "important",
|
|
13
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersScreen.kt",
|
|
14
|
-
"line": 54,
|
|
15
|
-
"issue": "PullToRefreshState hoisted yerine composable içinde tutuluyor - configuration change'de state kaybı",
|
|
16
|
-
"fix": "Use rememberSaveable or hoist the state up to ViewModel",
|
|
17
|
-
"reviewer": "sonnet"
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
"deferred": [
|
|
21
|
-
{
|
|
22
|
-
"finding": {
|
|
23
|
-
"severity": "important",
|
|
24
|
-
"file": "feature/orders/src/main/java/com/example/orders/OrdersScreen.kt",
|
|
25
|
-
"line": 0,
|
|
26
|
-
"issue": "Yeni modifier chain lazılarla LazyColumn semantics test matcher'larını kırabilir - Compose stability endişesi var",
|
|
27
|
-
"fix": "Measure recomposition count with Compose Compiler Reports before merging",
|
|
28
|
-
"reviewer": "opus"
|
|
29
|
-
},
|
|
30
|
-
"reason": "Out of scope for this task - recomposition audit is a performance sweep, not a correctness gate for adding pull-to-refresh"
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
"rejected": [],
|
|
34
|
-
"approved": false
|
|
35
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"focus": "Android/Compose feature path with mixed-verdict triage - one finding deferred out-of-scope, two accepted (one blocker + one important cross-reviewer match).",
|
|
3
|
-
"exercises": [
|
|
4
|
-
"Phase 1 stack detection (android / Kotlin 2.1 / Jetpack Compose)",
|
|
5
|
-
"Phase 2 3-task decomposition with clear dependency chain (T1 test → T2 VM → T3 UI)",
|
|
6
|
-
"Phase 4 Reviewer-1 + Reviewer-2 surface overlapping concerns at different lines",
|
|
7
|
-
"Triage deferred bucket works (important but out-of-scope finding parked, not actioned)"
|
|
8
|
-
],
|
|
9
|
-
"regressionsCaught": [
|
|
10
|
-
"If triage schema removes the 'deferred' bucket, this fixture fails",
|
|
11
|
-
"If reviewer enum drops a required reviewer, accepted[] loses its reviewer attribution and schema fails",
|
|
12
|
-
"If planning schema drops 'dependsOn', T1→T2→T3 ordering becomes unverifiable"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "Add pull-to-refresh on OrdersScreen",
|
|
3
|
-
"description": "Compose OrdersScreen'de swipe-to-refresh yok. Material3 PullToRefreshBox eklenmeli, ViewModel refresh() zaten var - UI tarafı bağlanmadı.",
|
|
4
|
-
"inputType": "free-text",
|
|
5
|
-
"expectedStack": "android",
|
|
6
|
-
"expectedLanguage": "Kotlin",
|
|
7
|
-
"expectedFramework": "Jetpack Compose",
|
|
8
|
-
"expectedBlockers": 1,
|
|
9
|
-
"expectedDeferrals": 1,
|
|
10
|
-
"baseBranch": "develop",
|
|
11
|
-
"taskBranch": "feature/orders-pull-to-refresh"
|
|
12
|
-
}
|
package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"stack": {
|
|
3
|
-
"primary": "backend",
|
|
4
|
-
"language": "Python 3.13",
|
|
5
|
-
"framework": "FastAPI"
|
|
6
|
-
},
|
|
7
|
-
"touchedAreas": [
|
|
8
|
-
{
|
|
9
|
-
"path": "app/middleware/rate_limit.py",
|
|
10
|
-
"why": "New sliding-window per-IP limiter implemented as ASGI middleware"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"path": "app/api/auth.py",
|
|
14
|
-
"why": "Login router registers the limiter and returns 429 + Retry-After on breach"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"path": "tests/test_rate_limit.py",
|
|
18
|
-
"why": "Covers allowed-under-limit, blocked-over-limit, and window reset"
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"risks": [
|
|
22
|
-
{
|
|
23
|
-
"risk": "An in-memory counter does not share state across Gunicorn/Uvicorn workers, so the limit is per-process not per-cluster",
|
|
24
|
-
"severity": "medium",
|
|
25
|
-
"mitigation": "Document the single-process assumption now; back it with Redis when the service scales horizontally"
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"summary": "Brute-force on POST /auth/login is unthrottled. Add a sliding-window per-IP limiter as middleware, wire it into the auth router, and return 429 with Retry-After. Scope is three files; the main caveat is multi-worker state sharing, deferred to a Redis-backed follow-up."
|
|
29
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"approach": "TDD-first. Write a failing test that fires N+1 requests from one IP and asserts the last one returns 429 with Retry-After (RED). Implement the sliding-window limiter middleware (GREEN). Wire it into the auth router and assert allowed traffic still passes (refactor + integration).",
|
|
3
|
-
"tasks": [
|
|
4
|
-
{
|
|
5
|
-
"id": "T1",
|
|
6
|
-
"title": "Add failing rate-limit tests (under limit, over limit, window reset)",
|
|
7
|
-
"type": "test",
|
|
8
|
-
"files": ["tests/test_rate_limit.py"],
|
|
9
|
-
"dependsOn": [],
|
|
10
|
-
"acceptanceCriteria": [
|
|
11
|
-
"N requests under the limit all return 200",
|
|
12
|
-
"The N+1th request within the window returns 429 with a Retry-After header",
|
|
13
|
-
"After the window elapses, requests are allowed again"
|
|
14
|
-
]
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"id": "T2",
|
|
18
|
-
"title": "Implement sliding-window per-IP limiter middleware",
|
|
19
|
-
"type": "code",
|
|
20
|
-
"files": ["app/middleware/rate_limit.py"],
|
|
21
|
-
"dependsOn": ["T1"],
|
|
22
|
-
"acceptanceCriteria": [
|
|
23
|
-
"Tracks request timestamps per client IP in a sliding window",
|
|
24
|
-
"Emits 429 with Retry-After once the threshold is exceeded",
|
|
25
|
-
"Window bookkeeping is safe under concurrent requests"
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"id": "T3",
|
|
30
|
-
"title": "Register the limiter on the auth login route",
|
|
31
|
-
"type": "code",
|
|
32
|
-
"files": ["app/api/auth.py"],
|
|
33
|
-
"dependsOn": ["T2"],
|
|
34
|
-
"acceptanceCriteria": [
|
|
35
|
-
"POST /auth/login passes through the limiter",
|
|
36
|
-
"Normal login traffic under the limit is unaffected"
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
],
|
|
40
|
-
"approved": true,
|
|
41
|
-
"userFeedback": null
|
|
42
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"reviewer": "opus",
|
|
4
|
-
"findings": [
|
|
5
|
-
{
|
|
6
|
-
"severity": "blocking",
|
|
7
|
-
"file": "app/middleware/rate_limit.py",
|
|
8
|
-
"line": 38,
|
|
9
|
-
"issue": "The per-IP window dict is read-modified-written without a lock; under concurrent requests two coroutines can both pass the check and under-count, letting bursts through",
|
|
10
|
-
"fix": "Guard the read-modify-write with an asyncio.Lock keyed per IP, or use an atomic counter structure"
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
"approved": false
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"reviewer": "sonnet",
|
|
17
|
-
"findings": [],
|
|
18
|
-
"approved": true
|
|
19
|
-
}
|
|
20
|
-
]
|