feat(qa): auto-expire + test fix (v1.2.55)#18
Merged
Conversation
The test was mocking `@ai-sdk/mcp/stdio` but the source imports from `@ai-sdk/mcp/mcp-stdio`. Path mismatch meant the mock never applied and vitest tried to resolve the real subpath — which isn't in the root node_modules (the dep lives in container/agent-runner/package.json) — failing all 4 tests in the file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QA autopilot proposals that never got [✓ Merge] or [✕ Close] tapped were piling up indefinitely — three were sitting in the chat for 9-48h before this landed, each pinning a stale worktree + branch. - Add `expiresAt` field + `'expired'` resolution to QaProposal, with a shared QA_PROPOSAL_TTL_MS constant (48h). Producer now stamps expiresAt = createdAt + TTL on every new proposal. - scripts/qa/expire-proposals.ts — scans data/qa-proposals/*.json, finds unresolved proposals past their expiry, removes the worktree + local branch + (if pushed) the remote branch, marks the record `resolution: 'expired'`, and posts a Telegram digest summarizing what was cleaned up. Idempotent: resolved proposals are skipped on subsequent runs. Supports QA_EXPIRE_DRY_RUN=1 for observation-only runs and QA_EXPIRE_DISABLED=1 for maintenance windows. - Legacy proposals missing `expiresAt` derive one from createdAt + TTL so pre-upgrade records don't linger. - com.nanoclaw.qa-expire-proposals.plist — launchd agent that runs `npm run qa:expire-proposals` every 3600s (install with `launchctl load`, same pattern as qa-monitor). - Consumer `resolutionLabel` in qa-approval.ts shows `expired ⏰` when a proposal got cleaned up by the cron. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two tasks, small diff:
1.
test(mcp-bridge)— fix mock path mismatch (cd69566)Mock was
@ai-sdk/mcp/stdio, source imports@ai-sdk/mcp/mcp-stdio. Mismatched path → mock never applied → vitest tried to resolve for real → 4 tests failed on every host build (the dep lives incontainer/agent-runner/package.json, not root). One-line fix.2.
feat(qa)— auto-expire stuck proposals after 48h (97900a2)Three QA proposals had been sitting in Telegram for up to 9 hours, each pinning a stale worktree + branch. This adds a cron that cleans them up.
QaProposalgetsexpiresAt+'expired'resolution, with sharedQA_PROPOSAL_TTL_MS = 48h. Producer stampsexpiresAt = createdAt + TTLon new proposals.scripts/qa/expire-proposals.ts— scansdata/qa-proposals/*.json, expires unresolved ones past their TTL, removes worktree + local + remote branch, marksresolution: 'expired'. Posts a Telegram digest summarizing what was cleaned up. Idempotent. SupportsQA_EXPIRE_DRY_RUN=1andQA_EXPIRE_DISABLED=1.expiresAtderive one fromcreatedAt + TTLso pre-upgrade records don't linger.scripts/qa/com.nanoclaw.qa-expire-proposals.plist— launchd agent,StartInterval=3600(hourly). Install withcp scripts/qa/com.nanoclaw.qa-expire-proposals.plist ~/Library/LaunchAgents/ && launchctl load ~/Library/LaunchAgents/com.nanoclaw.qa-expire-proposals.plist.resolutionLabelinqa-approval.tsshowsexpired ⏰for auto-cleaned proposals.Test plan
npm run buildclean at v1.2.55npx vitest run src/llm/mcp-bridge.test.ts— 4/4 pass (was 0/4)expiresAt: 0→ expired correctly. Second run =scanned=1 expired=0(idempotent).QA_EXPIRE_DRY_RUN=1.Known pre-existing follow-up
src/index.test.tsfails to load because itsvi.mock('./channels/registry.js')doesn't exportregisterChannel— an issue that predates this PR. Full suite: 1611/1611 individual tests pass; only the module-load error remains. Small separate PR worth doing.🤖 Generated with Claude Code