@mmerterden/multi-agent-pipeline 12.7.0 → 12.9.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 +207 -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/features/review-multi-repo.md +65 -0
- package/pipeline/multi-agent-refs/features/url-enrichment.md +93 -0
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +3 -83
- 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 +50 -62
- package/pipeline/schemas/prefs.schema.json +6 -0
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/agent-guard.py +57 -3
- 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
|
@@ -25,7 +25,50 @@ ATTRIBUTION = re.compile(
|
|
|
25
25
|
r"|claude code <",
|
|
26
26
|
re.IGNORECASE,
|
|
27
27
|
)
|
|
28
|
-
|
|
28
|
+
# Force flags, and ONLY force flags.
|
|
29
|
+
#
|
|
30
|
+
# The previous pattern ended in `-\S*f\S*`, which matches any flag containing an
|
|
31
|
+
# `f` anywhere - so it fired on `--ff-only`, `--follow-tags`, and `--file=...`.
|
|
32
|
+
# Two real false positives followed:
|
|
33
|
+
#
|
|
34
|
+
# git pull --ff-only && git push origin main -> BLOCKED
|
|
35
|
+
# git push --follow-tags origin main -> BLOCKED
|
|
36
|
+
#
|
|
37
|
+
# Neither rewrites history. `--follow-tags` is the normal way to push a release
|
|
38
|
+
# tag with its commit, so the guard blocked a routine release step. Being
|
|
39
|
+
# fail-closed is right for a data-loss guard, but only against the thing it
|
|
40
|
+
# guards; blocking safe commands trains people to work around the hook, which
|
|
41
|
+
# costs more safety than it buys.
|
|
42
|
+
#
|
|
43
|
+
# The short-flag arm is now `-[A-Za-z]*f[A-Za-z]*`: a SINGLE dash followed only by
|
|
44
|
+
# letters, so it still catches `-f`, `-fq`, `-qf`, and never a `--long-flag`
|
|
45
|
+
# (whose second character is `-`, not a letter).
|
|
46
|
+
FORCE = re.compile(
|
|
47
|
+
r"(^|\s)("
|
|
48
|
+
r"--force(-with-lease|-if-includes)?(=\S+)?" # --force, --force-with-lease[=ref], --force-if-includes
|
|
49
|
+
r"|-[A-Za-z]*f[A-Za-z]*" # -f and short clusters containing f
|
|
50
|
+
r")(\s|$)"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Shell separators that end one command and start the next. Used to attribute a
|
|
55
|
+
# flag to the command it actually belongs to.
|
|
56
|
+
SEPARATORS = re.compile(r"&&|\|\||;|\||\n")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def push_segments(cmd: str) -> list:
|
|
60
|
+
"""The sub-commands that invoke `git push`.
|
|
61
|
+
|
|
62
|
+
A flag must be attributed to its own command. Scanning the whole string made
|
|
63
|
+
`git commit -f ... && git push origin main` read as a force-push, because the
|
|
64
|
+
`-f` belonged to the commit. Splitting on shell separators first keeps each
|
|
65
|
+
command's flags with that command.
|
|
66
|
+
|
|
67
|
+
Deliberately coarse: a separator inside a quoted string splits too, which can
|
|
68
|
+
only ever produce MORE segments to inspect, never fewer - the safe direction
|
|
69
|
+
for a guard.
|
|
70
|
+
"""
|
|
71
|
+
return [seg for seg in SEPARATORS.split(cmd) if re.search(r"git\s+push", seg.lower())]
|
|
29
72
|
|
|
30
73
|
|
|
31
74
|
def decide(cmd: str) -> str:
|
|
@@ -35,8 +78,19 @@ def decide(cmd: str) -> str:
|
|
|
35
78
|
if re.search(r"git\s+commit", low) and ATTRIBUTION.search(cmd):
|
|
36
79
|
return "BLOCK_ATTRIB"
|
|
37
80
|
|
|
38
|
-
# Rule 2: force-push to a protected branch.
|
|
39
|
-
|
|
81
|
+
# Rule 2: force-push to a protected branch. Evaluate each push segment on its
|
|
82
|
+
# own so a neighbouring command's flags cannot implicate it.
|
|
83
|
+
for segment in push_segments(cmd):
|
|
84
|
+
verdict = _decide_push(segment)
|
|
85
|
+
if verdict != "OK":
|
|
86
|
+
return verdict
|
|
87
|
+
|
|
88
|
+
return "OK"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _decide_push(cmd: str) -> str:
|
|
92
|
+
"""Verdict for a single `git push ...` command."""
|
|
93
|
+
if FORCE.search(cmd):
|
|
40
94
|
try:
|
|
41
95
|
toks = shlex.split(cmd)
|
|
42
96
|
except Exception:
|
|
@@ -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
|
-
]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"accepted": [
|
|
3
|
-
{
|
|
4
|
-
"severity": "blocking",
|
|
5
|
-
"file": "app/middleware/rate_limit.py",
|
|
6
|
-
"line": 38,
|
|
7
|
-
"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",
|
|
8
|
-
"fix": "Guard the read-modify-write with an asyncio.Lock keyed per IP, or use an atomic counter structure",
|
|
9
|
-
"reviewer": "opus"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
"deferred": [],
|
|
13
|
-
"rejected": [],
|
|
14
|
-
"approved": false
|
|
15
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"focus": "Backend (Python/FastAPI) feature path - verifies stack detection on a non-mobile repo and that Phase 4 catches a concurrency blocker (unsynchronized shared counter) in newly added middleware.",
|
|
3
|
-
"exercises": [
|
|
4
|
-
"Phase 1 stack detection (backend / Python / FastAPI)",
|
|
5
|
-
"Phase 2 TDD ordering (failing rate-limit test before middleware code)",
|
|
6
|
-
"Phase 4 Reviewer-1 (opus) catches a race condition on the shared window counter; Reviewer-2 (sonnet) approves the wiring",
|
|
7
|
-
"Triage keeps the concurrency blocker accepted, no deferrals, not approved"
|
|
8
|
-
],
|
|
9
|
-
"regressionsCaught": [
|
|
10
|
-
"If stack detection misroutes a FastAPI repo to 'frontend' or 'unknown', the stack-match check fails",
|
|
11
|
-
"If planning emits files outside Phase 1 touchedAreas, the subset check fails",
|
|
12
|
-
"If triage stops requiring a reviewer attribution on accepted findings, this fixture fails validate-triage"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "Add per-IP rate limiting to the auth login endpoint",
|
|
3
|
-
"description": "Brute-force attempts on POST /auth/login are unthrottled. Add per-IP rate limiting (sliding window) as FastAPI middleware and wire it into the auth router. Return 429 with Retry-After when the limit is exceeded.",
|
|
4
|
-
"inputType": "free-text",
|
|
5
|
-
"expectedStack": "backend",
|
|
6
|
-
"expectedLanguage": "Python",
|
|
7
|
-
"expectedFramework": "FastAPI",
|
|
8
|
-
"expectedBlockers": 1,
|
|
9
|
-
"expectedDeferrals": 0,
|
|
10
|
-
"baseBranch": "main",
|
|
11
|
-
"taskBranch": "feature/auth-rate-limit"
|
|
12
|
-
}
|
package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"stack": {
|
|
3
|
-
"primary": "frontend",
|
|
4
|
-
"language": "TypeScript 5",
|
|
5
|
-
"framework": "Next.js"
|
|
6
|
-
},
|
|
7
|
-
"touchedAreas": [
|
|
8
|
-
{
|
|
9
|
-
"path": "components/CartBadge.tsx",
|
|
10
|
-
"why": "Reads localStorage during render, diverging from the server-rendered 0"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"path": "hooks/useCart.ts",
|
|
14
|
-
"why": "Exposes the count; needs an SSR-safe initial value with hydration on mount"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"path": "components/CartBadge.test.tsx",
|
|
18
|
-
"why": "Adds a render test asserting no hydration warning and the count appears after mount"
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"risks": [
|
|
22
|
-
{
|
|
23
|
-
"risk": "Other components may also read the same localStorage key during render and reintroduce the mismatch elsewhere",
|
|
24
|
-
"severity": "low",
|
|
25
|
-
"mitigation": "Centralize the SSR-safe read in useCart so callers share one hydration-safe source"
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"summary": "The cart badge reads localStorage during render while the server renders 0, triggering a hydration mismatch and a count flash. Move the read behind an SSR-safe initial value that hydrates on mount, centralized in useCart. Three files; low regression risk if the read stays centralized."
|
|
29
|
-
}
|