@urateam/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/assembler.test.d.ts +2 -0
- package/dist/__tests__/assembler.test.d.ts.map +1 -0
- package/dist/__tests__/assembler.test.js +63 -0
- package/dist/__tests__/assembler.test.js.map +1 -0
- package/dist/__tests__/auth-check.test.d.ts +2 -0
- package/dist/__tests__/auth-check.test.d.ts.map +1 -0
- package/dist/__tests__/auth-check.test.js +88 -0
- package/dist/__tests__/auth-check.test.js.map +1 -0
- package/dist/__tests__/auto-merge.test.d.ts +15 -0
- package/dist/__tests__/auto-merge.test.d.ts.map +1 -0
- package/dist/__tests__/auto-merge.test.js +428 -0
- package/dist/__tests__/auto-merge.test.js.map +1 -0
- package/dist/__tests__/bec89-unified-schema.test.d.ts +2 -0
- package/dist/__tests__/bec89-unified-schema.test.d.ts.map +1 -0
- package/dist/__tests__/bec89-unified-schema.test.js +235 -0
- package/dist/__tests__/bec89-unified-schema.test.js.map +1 -0
- package/dist/__tests__/conflict-detector.test.d.ts +2 -0
- package/dist/__tests__/conflict-detector.test.d.ts.map +1 -0
- package/dist/__tests__/conflict-detector.test.js +206 -0
- package/dist/__tests__/conflict-detector.test.js.map +1 -0
- package/dist/__tests__/coordination.test.d.ts +2 -0
- package/dist/__tests__/coordination.test.d.ts.map +1 -0
- package/dist/__tests__/coordination.test.js +257 -0
- package/dist/__tests__/coordination.test.js.map +1 -0
- package/dist/__tests__/db-postgres.test.d.ts +14 -0
- package/dist/__tests__/db-postgres.test.d.ts.map +1 -0
- package/dist/__tests__/db-postgres.test.js +289 -0
- package/dist/__tests__/db-postgres.test.js.map +1 -0
- package/dist/__tests__/db.test.d.ts +2 -0
- package/dist/__tests__/db.test.d.ts.map +1 -0
- package/dist/__tests__/db.test.js +182 -0
- package/dist/__tests__/db.test.js.map +1 -0
- package/dist/__tests__/deep-review.test.d.ts +2 -0
- package/dist/__tests__/deep-review.test.d.ts.map +1 -0
- package/dist/__tests__/deep-review.test.js +322 -0
- package/dist/__tests__/deep-review.test.js.map +1 -0
- package/dist/__tests__/devcontainer.test.d.ts +2 -0
- package/dist/__tests__/devcontainer.test.d.ts.map +1 -0
- package/dist/__tests__/devcontainer.test.js +89 -0
- package/dist/__tests__/devcontainer.test.js.map +1 -0
- package/dist/__tests__/distributed-lock.test.d.ts +18 -0
- package/dist/__tests__/distributed-lock.test.d.ts.map +1 -0
- package/dist/__tests__/distributed-lock.test.js +237 -0
- package/dist/__tests__/distributed-lock.test.js.map +1 -0
- package/dist/__tests__/e2e-pipeline.test.d.ts +25 -0
- package/dist/__tests__/e2e-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-pipeline.test.js +517 -0
- package/dist/__tests__/e2e-pipeline.test.js.map +1 -0
- package/dist/__tests__/error-classifier.test.d.ts +2 -0
- package/dist/__tests__/error-classifier.test.d.ts.map +1 -0
- package/dist/__tests__/error-classifier.test.js +33 -0
- package/dist/__tests__/error-classifier.test.js.map +1 -0
- package/dist/__tests__/executor-integration.test.d.ts +11 -0
- package/dist/__tests__/executor-integration.test.d.ts.map +1 -0
- package/dist/__tests__/executor-integration.test.js +246 -0
- package/dist/__tests__/executor-integration.test.js.map +1 -0
- package/dist/__tests__/executor-issue-id.test.d.ts +13 -0
- package/dist/__tests__/executor-issue-id.test.d.ts.map +1 -0
- package/dist/__tests__/executor-issue-id.test.js +211 -0
- package/dist/__tests__/executor-issue-id.test.js.map +1 -0
- package/dist/__tests__/executor.test.d.ts +2 -0
- package/dist/__tests__/executor.test.d.ts.map +1 -0
- package/dist/__tests__/executor.test.js +164 -0
- package/dist/__tests__/executor.test.js.map +1 -0
- package/dist/__tests__/extract-handoff.test.d.ts +2 -0
- package/dist/__tests__/extract-handoff.test.d.ts.map +1 -0
- package/dist/__tests__/extract-handoff.test.js +131 -0
- package/dist/__tests__/extract-handoff.test.js.map +1 -0
- package/dist/__tests__/fail-on-auto-commit.test.d.ts +2 -0
- package/dist/__tests__/fail-on-auto-commit.test.d.ts.map +1 -0
- package/dist/__tests__/fail-on-auto-commit.test.js +156 -0
- package/dist/__tests__/fail-on-auto-commit.test.js.map +1 -0
- package/dist/__tests__/fixtures/webhook-comment.json +5 -0
- package/dist/__tests__/fixtures/webhook-state-change.json +15 -0
- package/dist/__tests__/force-push-agent-branches.test.d.ts +12 -0
- package/dist/__tests__/force-push-agent-branches.test.d.ts.map +1 -0
- package/dist/__tests__/force-push-agent-branches.test.js +348 -0
- package/dist/__tests__/force-push-agent-branches.test.js.map +1 -0
- package/dist/__tests__/github-webhook.test.d.ts +2 -0
- package/dist/__tests__/github-webhook.test.d.ts.map +1 -0
- package/dist/__tests__/github-webhook.test.js +370 -0
- package/dist/__tests__/github-webhook.test.js.map +1 -0
- package/dist/__tests__/gitlab.test.d.ts +28 -0
- package/dist/__tests__/gitlab.test.d.ts.map +1 -0
- package/dist/__tests__/gitlab.test.js +241 -0
- package/dist/__tests__/gitlab.test.js.map +1 -0
- package/dist/__tests__/integration/auto-commit.test.d.ts +2 -0
- package/dist/__tests__/integration/auto-commit.test.d.ts.map +1 -0
- package/dist/__tests__/integration/auto-commit.test.js +207 -0
- package/dist/__tests__/integration/auto-commit.test.js.map +1 -0
- package/dist/__tests__/integration/bec99-cross-worktree-guard.test.d.ts +10 -0
- package/dist/__tests__/integration/bec99-cross-worktree-guard.test.d.ts.map +1 -0
- package/dist/__tests__/integration/bec99-cross-worktree-guard.test.js +183 -0
- package/dist/__tests__/integration/bec99-cross-worktree-guard.test.js.map +1 -0
- package/dist/__tests__/integration/reproduce-bec99.test.d.ts +32 -0
- package/dist/__tests__/integration/reproduce-bec99.test.d.ts.map +1 -0
- package/dist/__tests__/integration/reproduce-bec99.test.js +243 -0
- package/dist/__tests__/integration/reproduce-bec99.test.js.map +1 -0
- package/dist/__tests__/integration/vitest-changed.test.d.ts +10 -0
- package/dist/__tests__/integration/vitest-changed.test.d.ts.map +1 -0
- package/dist/__tests__/integration/vitest-changed.test.js +128 -0
- package/dist/__tests__/integration/vitest-changed.test.js.map +1 -0
- package/dist/__tests__/license.test.d.ts +2 -0
- package/dist/__tests__/license.test.d.ts.map +1 -0
- package/dist/__tests__/license.test.js +53 -0
- package/dist/__tests__/license.test.js.map +1 -0
- package/dist/__tests__/mcp-resolver.test.d.ts +2 -0
- package/dist/__tests__/mcp-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-resolver.test.js +65 -0
- package/dist/__tests__/mcp-resolver.test.js.map +1 -0
- package/dist/__tests__/migrator.test.d.ts +2 -0
- package/dist/__tests__/migrator.test.d.ts.map +1 -0
- package/dist/__tests__/migrator.test.js +300 -0
- package/dist/__tests__/migrator.test.js.map +1 -0
- package/dist/__tests__/notifier-discord.test.d.ts +2 -0
- package/dist/__tests__/notifier-discord.test.d.ts.map +1 -0
- package/dist/__tests__/notifier-discord.test.js +166 -0
- package/dist/__tests__/notifier-discord.test.js.map +1 -0
- package/dist/__tests__/notifier-slack.test.d.ts +2 -0
- package/dist/__tests__/notifier-slack.test.d.ts.map +1 -0
- package/dist/__tests__/notifier-slack.test.js +157 -0
- package/dist/__tests__/notifier-slack.test.js.map +1 -0
- package/dist/__tests__/notifier.test.d.ts +2 -0
- package/dist/__tests__/notifier.test.d.ts.map +1 -0
- package/dist/__tests__/notifier.test.js +207 -0
- package/dist/__tests__/notifier.test.js.map +1 -0
- package/dist/__tests__/pipeline-config.test.d.ts +2 -0
- package/dist/__tests__/pipeline-config.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-config.test.js +143 -0
- package/dist/__tests__/pipeline-config.test.js.map +1 -0
- package/dist/__tests__/pipeline-runner.test.d.ts +2 -0
- package/dist/__tests__/pipeline-runner.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-runner.test.js +359 -0
- package/dist/__tests__/pipeline-runner.test.js.map +1 -0
- package/dist/__tests__/pm-approvals-n1.repro.test.d.ts +9 -0
- package/dist/__tests__/pm-approvals-n1.repro.test.d.ts.map +1 -0
- package/dist/__tests__/pm-approvals-n1.repro.test.js +175 -0
- package/dist/__tests__/pm-approvals-n1.repro.test.js.map +1 -0
- package/dist/__tests__/pm-approvals.test.d.ts +2 -0
- package/dist/__tests__/pm-approvals.test.d.ts.map +1 -0
- package/dist/__tests__/pm-approvals.test.js +162 -0
- package/dist/__tests__/pm-approvals.test.js.map +1 -0
- package/dist/__tests__/pm-budget.test.d.ts +2 -0
- package/dist/__tests__/pm-budget.test.d.ts.map +1 -0
- package/dist/__tests__/pm-budget.test.js +65 -0
- package/dist/__tests__/pm-budget.test.js.map +1 -0
- package/dist/__tests__/pm-conflict.test.d.ts +2 -0
- package/dist/__tests__/pm-conflict.test.d.ts.map +1 -0
- package/dist/__tests__/pm-conflict.test.js +87 -0
- package/dist/__tests__/pm-conflict.test.js.map +1 -0
- package/dist/__tests__/pm-promote.test.d.ts +2 -0
- package/dist/__tests__/pm-promote.test.d.ts.map +1 -0
- package/dist/__tests__/pm-promote.test.js +82 -0
- package/dist/__tests__/pm-promote.test.js.map +1 -0
- package/dist/__tests__/pm-recover.test.d.ts +2 -0
- package/dist/__tests__/pm-recover.test.d.ts.map +1 -0
- package/dist/__tests__/pm-recover.test.js +100 -0
- package/dist/__tests__/pm-recover.test.js.map +1 -0
- package/dist/__tests__/pm-scheduler.test.d.ts +2 -0
- package/dist/__tests__/pm-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/pm-scheduler.test.js +112 -0
- package/dist/__tests__/pm-scheduler.test.js.map +1 -0
- package/dist/__tests__/pm-slack-interface.test.d.ts +2 -0
- package/dist/__tests__/pm-slack-interface.test.d.ts.map +1 -0
- package/dist/__tests__/pm-slack-interface.test.js +372 -0
- package/dist/__tests__/pm-slack-interface.test.js.map +1 -0
- package/dist/__tests__/pm-slack.test.d.ts +2 -0
- package/dist/__tests__/pm-slack.test.d.ts.map +1 -0
- package/dist/__tests__/pm-slack.test.js +83 -0
- package/dist/__tests__/pm-slack.test.js.map +1 -0
- package/dist/__tests__/pm-triage.test.d.ts +2 -0
- package/dist/__tests__/pm-triage.test.d.ts.map +1 -0
- package/dist/__tests__/pm-triage.test.js +198 -0
- package/dist/__tests__/pm-triage.test.js.map +1 -0
- package/dist/__tests__/pm-types.test.d.ts +2 -0
- package/dist/__tests__/pm-types.test.d.ts.map +1 -0
- package/dist/__tests__/pm-types.test.js +76 -0
- package/dist/__tests__/pm-types.test.js.map +1 -0
- package/dist/__tests__/pr-automerge.test.d.ts +18 -0
- package/dist/__tests__/pr-automerge.test.d.ts.map +1 -0
- package/dist/__tests__/pr-automerge.test.js +645 -0
- package/dist/__tests__/pr-automerge.test.js.map +1 -0
- package/dist/__tests__/pr-description.test.d.ts +2 -0
- package/dist/__tests__/pr-description.test.d.ts.map +1 -0
- package/dist/__tests__/pr-description.test.js +728 -0
- package/dist/__tests__/pr-description.test.js.map +1 -0
- package/dist/__tests__/prompt-injection.test.d.ts +2 -0
- package/dist/__tests__/prompt-injection.test.d.ts.map +1 -0
- package/dist/__tests__/prompt-injection.test.js +446 -0
- package/dist/__tests__/prompt-injection.test.js.map +1 -0
- package/dist/__tests__/ralph-gate.test.d.ts +19 -0
- package/dist/__tests__/ralph-gate.test.d.ts.map +1 -0
- package/dist/__tests__/ralph-gate.test.js +593 -0
- package/dist/__tests__/ralph-gate.test.js.map +1 -0
- package/dist/__tests__/ralph-review-fix-regression.test.d.ts +18 -0
- package/dist/__tests__/ralph-review-fix-regression.test.d.ts.map +1 -0
- package/dist/__tests__/ralph-review-fix-regression.test.js +306 -0
- package/dist/__tests__/ralph-review-fix-regression.test.js.map +1 -0
- package/dist/__tests__/ralph.test.d.ts +2 -0
- package/dist/__tests__/ralph.test.d.ts.map +1 -0
- package/dist/__tests__/ralph.test.js +96 -0
- package/dist/__tests__/ralph.test.js.map +1 -0
- package/dist/__tests__/recover-stuck.test.d.ts +8 -0
- package/dist/__tests__/recover-stuck.test.d.ts.map +1 -0
- package/dist/__tests__/recover-stuck.test.js +399 -0
- package/dist/__tests__/recover-stuck.test.js.map +1 -0
- package/dist/__tests__/repo.test.d.ts +2 -0
- package/dist/__tests__/repo.test.d.ts.map +1 -0
- package/dist/__tests__/repo.test.js +295 -0
- package/dist/__tests__/repo.test.js.map +1 -0
- package/dist/__tests__/repro-bec58-n-plus-one.test.d.ts +2 -0
- package/dist/__tests__/repro-bec58-n-plus-one.test.d.ts.map +1 -0
- package/dist/__tests__/repro-bec58-n-plus-one.test.js +187 -0
- package/dist/__tests__/repro-bec58-n-plus-one.test.js.map +1 -0
- package/dist/__tests__/reproduce-bec113-pagination-warning.test.d.ts +16 -0
- package/dist/__tests__/reproduce-bec113-pagination-warning.test.d.ts.map +1 -0
- package/dist/__tests__/reproduce-bec113-pagination-warning.test.js +226 -0
- package/dist/__tests__/reproduce-bec113-pagination-warning.test.js.map +1 -0
- package/dist/__tests__/reproduce-bec43-updatedat.test.d.ts +2 -0
- package/dist/__tests__/reproduce-bec43-updatedat.test.d.ts.map +1 -0
- package/dist/__tests__/reproduce-bec43-updatedat.test.js +76 -0
- package/dist/__tests__/reproduce-bec43-updatedat.test.js.map +1 -0
- package/dist/__tests__/reproduce-bec48-distributed-race.test.d.ts +18 -0
- package/dist/__tests__/reproduce-bec48-distributed-race.test.d.ts.map +1 -0
- package/dist/__tests__/reproduce-bec48-distributed-race.test.js +178 -0
- package/dist/__tests__/reproduce-bec48-distributed-race.test.js.map +1 -0
- package/dist/__tests__/reproduce-bec62.test.d.ts +2 -0
- package/dist/__tests__/reproduce-bec62.test.d.ts.map +1 -0
- package/dist/__tests__/reproduce-bec62.test.js +86 -0
- package/dist/__tests__/reproduce-bec62.test.js.map +1 -0
- package/dist/__tests__/reproduce-bec91-stuck-in-progress.test.d.ts +13 -0
- package/dist/__tests__/reproduce-bec91-stuck-in-progress.test.d.ts.map +1 -0
- package/dist/__tests__/reproduce-bec91-stuck-in-progress.test.js +220 -0
- package/dist/__tests__/reproduce-bec91-stuck-in-progress.test.js.map +1 -0
- package/dist/__tests__/review-feedback.test.d.ts +2 -0
- package/dist/__tests__/review-feedback.test.d.ts.map +1 -0
- package/dist/__tests__/review-feedback.test.js +383 -0
- package/dist/__tests__/review-feedback.test.js.map +1 -0
- package/dist/__tests__/sanitizer.test.d.ts +2 -0
- package/dist/__tests__/sanitizer.test.d.ts.map +1 -0
- package/dist/__tests__/sanitizer.test.js +162 -0
- package/dist/__tests__/sanitizer.test.js.map +1 -0
- package/dist/__tests__/security.test.d.ts +2 -0
- package/dist/__tests__/security.test.d.ts.map +1 -0
- package/dist/__tests__/security.test.js +52 -0
- package/dist/__tests__/security.test.js.map +1 -0
- package/dist/__tests__/server.test.d.ts +2 -0
- package/dist/__tests__/server.test.d.ts.map +1 -0
- package/dist/__tests__/server.test.js +61 -0
- package/dist/__tests__/server.test.js.map +1 -0
- package/dist/__tests__/slack-alerts.test.d.ts +2 -0
- package/dist/__tests__/slack-alerts.test.d.ts.map +1 -0
- package/dist/__tests__/slack-alerts.test.js +214 -0
- package/dist/__tests__/slack-alerts.test.js.map +1 -0
- package/dist/__tests__/stage-models.test.d.ts +14 -0
- package/dist/__tests__/stage-models.test.d.ts.map +1 -0
- package/dist/__tests__/stage-models.test.js +244 -0
- package/dist/__tests__/stage-models.test.js.map +1 -0
- package/dist/__tests__/start-todo.test.d.ts +2 -0
- package/dist/__tests__/start-todo.test.d.ts.map +1 -0
- package/dist/__tests__/start-todo.test.js +175 -0
- package/dist/__tests__/start-todo.test.js.map +1 -0
- package/dist/__tests__/tech-stack.test.d.ts +2 -0
- package/dist/__tests__/tech-stack.test.d.ts.map +1 -0
- package/dist/__tests__/tech-stack.test.js +75 -0
- package/dist/__tests__/tech-stack.test.js.map +1 -0
- package/dist/__tests__/templates.test.d.ts +2 -0
- package/dist/__tests__/templates.test.d.ts.map +1 -0
- package/dist/__tests__/templates.test.js +161 -0
- package/dist/__tests__/templates.test.js.map +1 -0
- package/dist/__tests__/test-quality.test.d.ts +2 -0
- package/dist/__tests__/test-quality.test.d.ts.map +1 -0
- package/dist/__tests__/test-quality.test.js +329 -0
- package/dist/__tests__/test-quality.test.js.map +1 -0
- package/dist/__tests__/token-budget.test.d.ts +2 -0
- package/dist/__tests__/token-budget.test.d.ts.map +1 -0
- package/dist/__tests__/token-budget.test.js +198 -0
- package/dist/__tests__/token-budget.test.js.map +1 -0
- package/dist/__tests__/types.test.d.ts +2 -0
- package/dist/__tests__/types.test.d.ts.map +1 -0
- package/dist/__tests__/types.test.js +156 -0
- package/dist/__tests__/types.test.js.map +1 -0
- package/dist/__tests__/validate.test.d.ts +2 -0
- package/dist/__tests__/validate.test.d.ts.map +1 -0
- package/dist/__tests__/validate.test.js +128 -0
- package/dist/__tests__/validate.test.js.map +1 -0
- package/dist/__tests__/webhook-handler.test.d.ts +2 -0
- package/dist/__tests__/webhook-handler.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-handler.test.js +286 -0
- package/dist/__tests__/webhook-handler.test.js.map +1 -0
- package/dist/__tests__/webhook.test.d.ts +2 -0
- package/dist/__tests__/webhook.test.d.ts.map +1 -0
- package/dist/__tests__/webhook.test.js +58 -0
- package/dist/__tests__/webhook.test.js.map +1 -0
- package/dist/db/client.d.ts +56 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +201 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +4 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations/postgres/001_initial_schema.sql +78 -0
- package/dist/db/migrations/postgres/002_pg_timestamps.sql +78 -0
- package/dist/db/migrations/postgres/003_retry_count.sql +10 -0
- package/dist/db/migrations/postgres/004_review_feedback.sql +20 -0
- package/dist/db/migrations/postgres/005_auto_merge.sql +15 -0
- package/dist/db/migrations/sqlite/001_initial_schema.sql +78 -0
- package/dist/db/migrations/sqlite/002_retry_count.sql +5 -0
- package/dist/db/migrations/sqlite/003_review_feedback.sql +7 -0
- package/dist/db/migrations/sqlite/004_auto_merge.sql +6 -0
- package/dist/db/migrator.d.ts +51 -0
- package/dist/db/migrator.d.ts.map +1 -0
- package/dist/db/migrator.js +188 -0
- package/dist/db/migrator.js.map +1 -0
- package/dist/db/schema.d.ts +1114 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +129 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/entrypoint.d.ts +2 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +113 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/executor/agent-config.d.ts +10 -0
- package/dist/executor/agent-config.d.ts.map +1 -0
- package/dist/executor/agent-config.js +81 -0
- package/dist/executor/agent-config.js.map +1 -0
- package/dist/executor/agent-stream.d.ts +65 -0
- package/dist/executor/agent-stream.d.ts.map +1 -0
- package/dist/executor/agent-stream.js +101 -0
- package/dist/executor/agent-stream.js.map +1 -0
- package/dist/executor/auth-check.d.ts +10 -0
- package/dist/executor/auth-check.d.ts.map +1 -0
- package/dist/executor/auth-check.js +52 -0
- package/dist/executor/auth-check.js.map +1 -0
- package/dist/executor/deep-review.d.ts +61 -0
- package/dist/executor/deep-review.d.ts.map +1 -0
- package/dist/executor/deep-review.js +308 -0
- package/dist/executor/deep-review.js.map +1 -0
- package/dist/executor/executor.d.ts +27 -0
- package/dist/executor/executor.d.ts.map +1 -0
- package/dist/executor/executor.js +168 -0
- package/dist/executor/executor.js.map +1 -0
- package/dist/executor/extract-handoff.d.ts +14 -0
- package/dist/executor/extract-handoff.d.ts.map +1 -0
- package/dist/executor/extract-handoff.js +80 -0
- package/dist/executor/extract-handoff.js.map +1 -0
- package/dist/executor/handoff.d.ts +24 -0
- package/dist/executor/handoff.d.ts.map +1 -0
- package/dist/executor/handoff.js +63 -0
- package/dist/executor/handoff.js.map +1 -0
- package/dist/executor/index.d.ts +8 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +8 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/mcp-resolver.d.ts +29 -0
- package/dist/executor/mcp-resolver.d.ts.map +1 -0
- package/dist/executor/mcp-resolver.js +80 -0
- package/dist/executor/mcp-resolver.js.map +1 -0
- package/dist/executor/permissions.d.ts +11 -0
- package/dist/executor/permissions.d.ts.map +1 -0
- package/dist/executor/permissions.js +32 -0
- package/dist/executor/permissions.js.map +1 -0
- package/dist/executor/profiles.d.ts +5 -0
- package/dist/executor/profiles.d.ts.map +1 -0
- package/dist/executor/profiles.js +35 -0
- package/dist/executor/profiles.js.map +1 -0
- package/dist/executor/prompt/assembler.d.ts +10 -0
- package/dist/executor/prompt/assembler.d.ts.map +1 -0
- package/dist/executor/prompt/assembler.js +28 -0
- package/dist/executor/prompt/assembler.js.map +1 -0
- package/dist/executor/prompt/index.d.ts +5 -0
- package/dist/executor/prompt/index.d.ts.map +1 -0
- package/dist/executor/prompt/index.js +5 -0
- package/dist/executor/prompt/index.js.map +1 -0
- package/dist/executor/prompt/sanitizer.d.ts +25 -0
- package/dist/executor/prompt/sanitizer.d.ts.map +1 -0
- package/dist/executor/prompt/sanitizer.js +81 -0
- package/dist/executor/prompt/sanitizer.js.map +1 -0
- package/dist/executor/prompt/schema-mapper.d.ts +7 -0
- package/dist/executor/prompt/schema-mapper.d.ts.map +1 -0
- package/dist/executor/prompt/schema-mapper.js +59 -0
- package/dist/executor/prompt/schema-mapper.js.map +1 -0
- package/dist/executor/prompt/templates.d.ts +31 -0
- package/dist/executor/prompt/templates.d.ts.map +1 -0
- package/dist/executor/prompt/templates.js +283 -0
- package/dist/executor/prompt/templates.js.map +1 -0
- package/dist/executor/ralph.d.ts +19 -0
- package/dist/executor/ralph.d.ts.map +1 -0
- package/dist/executor/ralph.js +112 -0
- package/dist/executor/ralph.js.map +1 -0
- package/dist/executor/test-quality.d.ts +117 -0
- package/dist/executor/test-quality.d.ts.map +1 -0
- package/dist/executor/test-quality.js +261 -0
- package/dist/executor/test-quality.js.map +1 -0
- package/dist/executor/validate.d.ts +15 -0
- package/dist/executor/validate.d.ts.map +1 -0
- package/dist/executor/validate.js +124 -0
- package/dist/executor/validate.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/license.d.ts +18 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +44 -0
- package/dist/license.js.map +1 -0
- package/dist/logger.d.ts +43 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +91 -0
- package/dist/logger.js.map +1 -0
- package/dist/notifier/composite.d.ts +13 -0
- package/dist/notifier/composite.d.ts.map +1 -0
- package/dist/notifier/composite.js +28 -0
- package/dist/notifier/composite.js.map +1 -0
- package/dist/notifier/discord.d.ts +14 -0
- package/dist/notifier/discord.d.ts.map +1 -0
- package/dist/notifier/discord.js +105 -0
- package/dist/notifier/discord.js.map +1 -0
- package/dist/notifier/index.d.ts +6 -0
- package/dist/notifier/index.d.ts.map +1 -0
- package/dist/notifier/index.js +6 -0
- package/dist/notifier/index.js.map +1 -0
- package/dist/notifier/linear.d.ts +28 -0
- package/dist/notifier/linear.d.ts.map +1 -0
- package/dist/notifier/linear.js +138 -0
- package/dist/notifier/linear.js.map +1 -0
- package/dist/notifier/slack-alerts.d.ts +62 -0
- package/dist/notifier/slack-alerts.d.ts.map +1 -0
- package/dist/notifier/slack-alerts.js +184 -0
- package/dist/notifier/slack-alerts.js.map +1 -0
- package/dist/notifier/slack.d.ts +14 -0
- package/dist/notifier/slack.d.ts.map +1 -0
- package/dist/notifier/slack.js +146 -0
- package/dist/notifier/slack.js.map +1 -0
- package/dist/pipeline/automerge.d.ts +44 -0
- package/dist/pipeline/automerge.d.ts.map +1 -0
- package/dist/pipeline/automerge.js +135 -0
- package/dist/pipeline/automerge.js.map +1 -0
- package/dist/pipeline/config.d.ts +5 -0
- package/dist/pipeline/config.d.ts.map +1 -0
- package/dist/pipeline/config.js +68 -0
- package/dist/pipeline/config.js.map +1 -0
- package/dist/pipeline/distributed-lock.d.ts +50 -0
- package/dist/pipeline/distributed-lock.d.ts.map +1 -0
- package/dist/pipeline/distributed-lock.js +114 -0
- package/dist/pipeline/distributed-lock.js.map +1 -0
- package/dist/pipeline/error-classifier.d.ts +9 -0
- package/dist/pipeline/error-classifier.d.ts.map +1 -0
- package/dist/pipeline/error-classifier.js +25 -0
- package/dist/pipeline/error-classifier.js.map +1 -0
- package/dist/pipeline/index.d.ts +9 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +9 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/pr-description.d.ts +35 -0
- package/dist/pipeline/pr-description.d.ts.map +1 -0
- package/dist/pipeline/pr-description.js +52 -0
- package/dist/pipeline/pr-description.js.map +1 -0
- package/dist/pipeline/queue.d.ts +7 -0
- package/dist/pipeline/queue.d.ts.map +1 -0
- package/dist/pipeline/queue.js +39 -0
- package/dist/pipeline/queue.js.map +1 -0
- package/dist/pipeline/router.d.ts +6 -0
- package/dist/pipeline/router.d.ts.map +1 -0
- package/dist/pipeline/router.js +19 -0
- package/dist/pipeline/router.js.map +1 -0
- package/dist/pipeline/runner.d.ts +142 -0
- package/dist/pipeline/runner.d.ts.map +1 -0
- package/dist/pipeline/runner.js +1848 -0
- package/dist/pipeline/runner.js.map +1 -0
- package/dist/pm/actions/approval-helpers.d.ts +11 -0
- package/dist/pm/actions/approval-helpers.d.ts.map +1 -0
- package/dist/pm/actions/approval-helpers.js +34 -0
- package/dist/pm/actions/approval-helpers.js.map +1 -0
- package/dist/pm/actions/cancel.d.ts +11 -0
- package/dist/pm/actions/cancel.d.ts.map +1 -0
- package/dist/pm/actions/cancel.js +68 -0
- package/dist/pm/actions/cancel.js.map +1 -0
- package/dist/pm/actions/deprioritize.d.ts +12 -0
- package/dist/pm/actions/deprioritize.d.ts.map +1 -0
- package/dist/pm/actions/deprioritize.js +55 -0
- package/dist/pm/actions/deprioritize.js.map +1 -0
- package/dist/pm/actions/promote.d.ts +11 -0
- package/dist/pm/actions/promote.d.ts.map +1 -0
- package/dist/pm/actions/promote.js +78 -0
- package/dist/pm/actions/promote.js.map +1 -0
- package/dist/pm/actions/recover-stuck.d.ts +42 -0
- package/dist/pm/actions/recover-stuck.d.ts.map +1 -0
- package/dist/pm/actions/recover-stuck.js +143 -0
- package/dist/pm/actions/recover-stuck.js.map +1 -0
- package/dist/pm/actions/recover.d.ts +18 -0
- package/dist/pm/actions/recover.d.ts.map +1 -0
- package/dist/pm/actions/recover.js +56 -0
- package/dist/pm/actions/recover.js.map +1 -0
- package/dist/pm/actions/resolve-approvals.d.ts +17 -0
- package/dist/pm/actions/resolve-approvals.d.ts.map +1 -0
- package/dist/pm/actions/resolve-approvals.js +92 -0
- package/dist/pm/actions/resolve-approvals.js.map +1 -0
- package/dist/pm/actions/start-todo.d.ts +28 -0
- package/dist/pm/actions/start-todo.d.ts.map +1 -0
- package/dist/pm/actions/start-todo.js +117 -0
- package/dist/pm/actions/start-todo.js.map +1 -0
- package/dist/pm/actions/triage.d.ts +13 -0
- package/dist/pm/actions/triage.d.ts.map +1 -0
- package/dist/pm/actions/triage.js +109 -0
- package/dist/pm/actions/triage.js.map +1 -0
- package/dist/pm/budget.d.ts +9 -0
- package/dist/pm/budget.d.ts.map +1 -0
- package/dist/pm/budget.js +62 -0
- package/dist/pm/budget.js.map +1 -0
- package/dist/pm/call-claude.d.ts +3 -0
- package/dist/pm/call-claude.d.ts.map +1 -0
- package/dist/pm/call-claude.js +37 -0
- package/dist/pm/call-claude.js.map +1 -0
- package/dist/pm/conflict-detector.d.ts +42 -0
- package/dist/pm/conflict-detector.d.ts.map +1 -0
- package/dist/pm/conflict-detector.js +116 -0
- package/dist/pm/conflict-detector.js.map +1 -0
- package/dist/pm/conflict.d.ts +20 -0
- package/dist/pm/conflict.d.ts.map +1 -0
- package/dist/pm/conflict.js +63 -0
- package/dist/pm/conflict.js.map +1 -0
- package/dist/pm/coordination.d.ts +50 -0
- package/dist/pm/coordination.d.ts.map +1 -0
- package/dist/pm/coordination.js +163 -0
- package/dist/pm/coordination.js.map +1 -0
- package/dist/pm/linear-helpers.d.ts +2 -0
- package/dist/pm/linear-helpers.d.ts.map +1 -0
- package/dist/pm/linear-helpers.js +16 -0
- package/dist/pm/linear-helpers.js.map +1 -0
- package/dist/pm/scheduler.d.ts +47 -0
- package/dist/pm/scheduler.d.ts.map +1 -0
- package/dist/pm/scheduler.js +346 -0
- package/dist/pm/scheduler.js.map +1 -0
- package/dist/pm/slack-helpers.d.ts +2 -0
- package/dist/pm/slack-helpers.d.ts.map +1 -0
- package/dist/pm/slack-helpers.js +24 -0
- package/dist/pm/slack-helpers.js.map +1 -0
- package/dist/pm/slack-interface.d.ts +133 -0
- package/dist/pm/slack-interface.d.ts.map +1 -0
- package/dist/pm/slack-interface.js +641 -0
- package/dist/pm/slack-interface.js.map +1 -0
- package/dist/pm/slack.d.ts +18 -0
- package/dist/pm/slack.d.ts.map +1 -0
- package/dist/pm/slack.js +144 -0
- package/dist/pm/slack.js.map +1 -0
- package/dist/pm/types.d.ts +99 -0
- package/dist/pm/types.d.ts.map +1 -0
- package/dist/pm/types.js +17 -0
- package/dist/pm/types.js.map +1 -0
- package/dist/repo/config.d.ts +35 -0
- package/dist/repo/config.d.ts.map +1 -0
- package/dist/repo/config.js +72 -0
- package/dist/repo/config.js.map +1 -0
- package/dist/repo/devcontainer.d.ts +33 -0
- package/dist/repo/devcontainer.d.ts.map +1 -0
- package/dist/repo/devcontainer.js +90 -0
- package/dist/repo/devcontainer.js.map +1 -0
- package/dist/repo/git.d.ts +185 -0
- package/dist/repo/git.d.ts.map +1 -0
- package/dist/repo/git.js +586 -0
- package/dist/repo/git.js.map +1 -0
- package/dist/repo/github.d.ts +56 -0
- package/dist/repo/github.d.ts.map +1 -0
- package/dist/repo/github.js +164 -0
- package/dist/repo/github.js.map +1 -0
- package/dist/repo/gitlab.d.ts +47 -0
- package/dist/repo/gitlab.d.ts.map +1 -0
- package/dist/repo/gitlab.js +91 -0
- package/dist/repo/gitlab.js.map +1 -0
- package/dist/repo/index.d.ts +7 -0
- package/dist/repo/index.d.ts.map +1 -0
- package/dist/repo/index.js +5 -0
- package/dist/repo/index.js.map +1 -0
- package/dist/repo/tech-stack.d.ts +13 -0
- package/dist/repo/tech-stack.d.ts.map +1 -0
- package/dist/repo/tech-stack.js +112 -0
- package/dist/repo/tech-stack.js.map +1 -0
- package/dist/security/index.d.ts +3 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +3 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/review-checklist.d.ts +9 -0
- package/dist/security/review-checklist.d.ts.map +1 -0
- package/dist/security/review-checklist.js +46 -0
- package/dist/security/review-checklist.js.map +1 -0
- package/dist/security/sandbox.d.ts +7 -0
- package/dist/security/sandbox.d.ts.map +1 -0
- package/dist/security/sandbox.js +31 -0
- package/dist/security/sandbox.js.map +1 -0
- package/dist/server.d.ts +48 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +90 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +1230 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +225 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook/github-handler.d.ts +39 -0
- package/dist/webhook/github-handler.d.ts.map +1 -0
- package/dist/webhook/github-handler.js +439 -0
- package/dist/webhook/github-handler.js.map +1 -0
- package/dist/webhook/handler.d.ts +16 -0
- package/dist/webhook/handler.d.ts.map +1 -0
- package/dist/webhook/handler.js +171 -0
- package/dist/webhook/handler.js.map +1 -0
- package/dist/webhook/index.d.ts +5 -0
- package/dist/webhook/index.d.ts.map +1 -0
- package/dist/webhook/index.js +5 -0
- package/dist/webhook/index.js.map +1 -0
- package/dist/webhook/parser.d.ts +18 -0
- package/dist/webhook/parser.d.ts.map +1 -0
- package/dist/webhook/parser.js +30 -0
- package/dist/webhook/parser.js.map +1 -0
- package/dist/webhook/signature.d.ts +2 -0
- package/dist/webhook/signature.d.ts.map +1 -0
- package/dist/webhook/signature.js +14 -0
- package/dist/webhook/signature.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reproduction test for BEC-113:
|
|
3
|
+
* Fix stuck-issue recovery: pagination warning and scheduler integration
|
|
4
|
+
*
|
|
5
|
+
* Gaps confirmed by this file:
|
|
6
|
+
* 1. recoverStuckInProgressIssues does NOT log a warning when Linear returns
|
|
7
|
+
* exactly 50 issues (the `first: 50` hard cap may silently truncate results).
|
|
8
|
+
* 2. The JSDoc on recoverStuckInProgressIssues does NOT document the 50-result
|
|
9
|
+
* hard limit or the warning behavior.
|
|
10
|
+
*
|
|
11
|
+
* The scheduler integration (AC #1/#4) and the scheduler test (AC #4) were
|
|
12
|
+
* already implemented in BEC-91 and pass as-is — confirmed at the bottom of
|
|
13
|
+
* this file.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
16
|
+
import { recoverStuckInProgressIssues } from "../pm/actions/recover-stuck.js";
|
|
17
|
+
import { createPmScheduler } from "../pm/scheduler.js";
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
function makeLinearClient(inProgressIssues) {
|
|
22
|
+
return {
|
|
23
|
+
issues: vi.fn().mockResolvedValue({ nodes: inProgressIssues }),
|
|
24
|
+
updateIssue: vi.fn().mockResolvedValue({}),
|
|
25
|
+
createComment: vi.fn().mockResolvedValue({}),
|
|
26
|
+
workflowStates: vi.fn().mockResolvedValue({ nodes: [] }),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function makeDb(activeRows = [], runs = []) {
|
|
30
|
+
let callCount = 0;
|
|
31
|
+
return {
|
|
32
|
+
select: vi.fn().mockReturnValue({
|
|
33
|
+
from: vi.fn().mockReturnValue({
|
|
34
|
+
where: vi.fn().mockImplementation(() => {
|
|
35
|
+
callCount++;
|
|
36
|
+
if (callCount === 1)
|
|
37
|
+
return Promise.resolve(activeRows);
|
|
38
|
+
return Promise.resolve(runs);
|
|
39
|
+
}),
|
|
40
|
+
}),
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Build N fake Linear issues (all stuck, no active runs) */
|
|
45
|
+
function makeFakeIssues(count) {
|
|
46
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
47
|
+
id: `issue-${i}`,
|
|
48
|
+
identifier: `BEC-${100 + i}`,
|
|
49
|
+
title: `Fake issue ${i}`,
|
|
50
|
+
team: Promise.resolve({ id: "team-1" }),
|
|
51
|
+
state: Promise.resolve({ name: "In Progress" }),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// GAP 1: No pagination warning when Linear returns exactly 50 results
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
describe("BEC-113 GAP: pagination truncation warning", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* When the Linear query returns exactly 50 issues the function SHOULD log:
|
|
63
|
+
* "stuck-issue query may be truncated — consider pagination"
|
|
64
|
+
*
|
|
65
|
+
* Currently this warning is MISSING — `recoverStuckInProgressIssues` has no
|
|
66
|
+
* check for `inProgressIssues.length === 50`.
|
|
67
|
+
*
|
|
68
|
+
* This test will FAIL until the warning is implemented.
|
|
69
|
+
*/
|
|
70
|
+
it("FAILS: logs truncation warning when Linear returns exactly 50 issues", async () => {
|
|
71
|
+
const issues = makeFakeIssues(50); // Exactly at the hard-cap limit
|
|
72
|
+
const linearClient = makeLinearClient(issues);
|
|
73
|
+
// All issues are stuck (no active runs)
|
|
74
|
+
const db = makeDb([], []);
|
|
75
|
+
const stateMap = new Map([["team-1:Backlog", "state-backlog-1"]]);
|
|
76
|
+
const warnSpy = vi.spyOn(process.stdout, "write");
|
|
77
|
+
await recoverStuckInProgressIssues({
|
|
78
|
+
linearClient,
|
|
79
|
+
db: db,
|
|
80
|
+
teamIds: ["team-1"],
|
|
81
|
+
targetState: "Backlog",
|
|
82
|
+
maxPerTick: 5,
|
|
83
|
+
stateMap,
|
|
84
|
+
});
|
|
85
|
+
// pino may write Buffers or strings — coerce both to string for the check
|
|
86
|
+
const allOutput = warnSpy.mock.calls
|
|
87
|
+
.map((args) => {
|
|
88
|
+
const arg = args[0];
|
|
89
|
+
if (typeof arg === "string")
|
|
90
|
+
return arg;
|
|
91
|
+
if (Buffer.isBuffer(arg))
|
|
92
|
+
return arg.toString("utf-8");
|
|
93
|
+
return "";
|
|
94
|
+
})
|
|
95
|
+
.join("");
|
|
96
|
+
expect(allOutput).toContain("stuck-issue query may be truncated — consider pagination");
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* When the Linear query returns fewer than 50 issues (e.g. 49), no
|
|
100
|
+
* truncation warning should be emitted.
|
|
101
|
+
*
|
|
102
|
+
* This test documents the expected absence of the warning for non-truncated
|
|
103
|
+
* queries. It should PASS even before the fix.
|
|
104
|
+
*/
|
|
105
|
+
it("PASSES: does NOT log truncation warning when Linear returns fewer than 50 issues", async () => {
|
|
106
|
+
const issues = makeFakeIssues(3); // Well below the limit
|
|
107
|
+
const linearClient = makeLinearClient(issues);
|
|
108
|
+
const db = makeDb([], []);
|
|
109
|
+
const stateMap = new Map([["team-1:Backlog", "state-backlog-1"]]);
|
|
110
|
+
const warnSpy = vi.spyOn(process.stdout, "write");
|
|
111
|
+
await recoverStuckInProgressIssues({
|
|
112
|
+
linearClient,
|
|
113
|
+
db: db,
|
|
114
|
+
teamIds: ["team-1"],
|
|
115
|
+
targetState: "Backlog",
|
|
116
|
+
maxPerTick: 5,
|
|
117
|
+
stateMap,
|
|
118
|
+
});
|
|
119
|
+
const allOutput = warnSpy.mock.calls
|
|
120
|
+
.map((args) => {
|
|
121
|
+
const arg = args[0];
|
|
122
|
+
if (typeof arg === "string")
|
|
123
|
+
return arg;
|
|
124
|
+
if (Buffer.isBuffer(arg))
|
|
125
|
+
return arg.toString("utf-8");
|
|
126
|
+
return "";
|
|
127
|
+
})
|
|
128
|
+
.join("");
|
|
129
|
+
expect(allOutput).not.toContain("stuck-issue query may be truncated — consider pagination");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// GAP 2: JSDoc does not document the 50-result hard limit
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
describe("BEC-113 GAP: JSDoc documents 50-result hard limit", () => {
|
|
136
|
+
/**
|
|
137
|
+
* The JSDoc on `recoverStuckInProgressIssues` should mention:
|
|
138
|
+
* - "first: 50" (or "50-result") hard limit on the Linear query
|
|
139
|
+
* - The truncation warning behavior
|
|
140
|
+
*
|
|
141
|
+
* We verify this by reading the source file and checking for the phrases.
|
|
142
|
+
* This test will FAIL until the JSDoc is updated.
|
|
143
|
+
*/
|
|
144
|
+
it("FAILS: JSDoc mentions the 50-result hard limit", async () => {
|
|
145
|
+
const fs = await import("node:fs");
|
|
146
|
+
const path = await import("node:path");
|
|
147
|
+
const filePath = path.resolve(__dirname, "../pm/actions/recover-stuck.ts");
|
|
148
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
149
|
+
// Find the JSDoc block immediately preceding the function declaration
|
|
150
|
+
const jsdocMatch = source.match(/\/\*\*[\s\S]*?\*\/\s*export async function recoverStuckInProgressIssues/);
|
|
151
|
+
expect(jsdocMatch).toBeTruthy();
|
|
152
|
+
const jsdoc = jsdocMatch[0];
|
|
153
|
+
// BUG CONFIRMED: Neither the "50" limit nor the truncation warning is
|
|
154
|
+
// mentioned in the current JSDoc — these assertions will FAIL
|
|
155
|
+
expect(jsdoc).toMatch(/50/);
|
|
156
|
+
expect(jsdoc).toMatch(/truncat|pagination|hard.?limit|hard cap/i);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// ALREADY FIXED (from BEC-91): Scheduler integration and invocation order
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
describe("BEC-113 CONFIRMED FIXED: scheduler integration", () => {
|
|
163
|
+
/**
|
|
164
|
+
* recoverStuckInProgressIssues IS imported and invoked in the scheduler
|
|
165
|
+
* tick AFTER the retriable recovery sweep and BEFORE triage.
|
|
166
|
+
*
|
|
167
|
+
* This test passes already — the scheduler integration was implemented.
|
|
168
|
+
*/
|
|
169
|
+
it("PASSES: scheduler invokes recoverStuckInProgressIssues between budget check and triage", async () => {
|
|
170
|
+
const callOrder = [];
|
|
171
|
+
const mockActions = {
|
|
172
|
+
checkBudgetGuards: vi.fn().mockImplementation(async () => {
|
|
173
|
+
callOrder.push("budget");
|
|
174
|
+
return { promoteBlocked: false, activeCount: 0, tokenSpendPercent: 0, dailyTokensUsed: 0 };
|
|
175
|
+
}),
|
|
176
|
+
recoverRetriableRuns: vi.fn().mockImplementation(async () => {
|
|
177
|
+
callOrder.push("recoverRetriable");
|
|
178
|
+
return { recovered: [], exhausted: [] };
|
|
179
|
+
}),
|
|
180
|
+
recoverStuckInProgressIssues: vi.fn().mockImplementation(async () => {
|
|
181
|
+
callOrder.push("recoverStuck");
|
|
182
|
+
return [];
|
|
183
|
+
}),
|
|
184
|
+
triageNewIssues: vi.fn().mockImplementation(async () => {
|
|
185
|
+
callOrder.push("triage");
|
|
186
|
+
return [];
|
|
187
|
+
}),
|
|
188
|
+
resolveApprovals: vi.fn().mockResolvedValue({ resolved: 0, stillPending: 0 }),
|
|
189
|
+
promoteReadyIssues: vi.fn().mockResolvedValue([]),
|
|
190
|
+
deprioritizeStaleIssues: vi.fn().mockResolvedValue([]),
|
|
191
|
+
cancelAbandonedIssues: vi.fn().mockResolvedValue([]),
|
|
192
|
+
postDigest: vi.fn().mockResolvedValue(undefined),
|
|
193
|
+
getActiveFileMaps: vi.fn().mockResolvedValue(new Map()),
|
|
194
|
+
predictConflict: vi.fn().mockResolvedValue({ overlapRisk: "none", likelyFiles: [], reasoning: "" }),
|
|
195
|
+
};
|
|
196
|
+
const scheduler = createPmScheduler({
|
|
197
|
+
config: {
|
|
198
|
+
enabled: true,
|
|
199
|
+
cronIntervalMs: 1800000,
|
|
200
|
+
triageBatchSize: 3,
|
|
201
|
+
maxInFlight: 3,
|
|
202
|
+
dailyTokenBudget: 5_000_000,
|
|
203
|
+
slackChannelId: "C123",
|
|
204
|
+
teamIds: ["team-1"],
|
|
205
|
+
stuckIssueRecovery: true,
|
|
206
|
+
stuckIssueTargetState: "Backlog",
|
|
207
|
+
stuckIssueMaxPerTick: 5,
|
|
208
|
+
},
|
|
209
|
+
db: {},
|
|
210
|
+
linearApiKey: "",
|
|
211
|
+
slackBotToken: "",
|
|
212
|
+
actions: mockActions,
|
|
213
|
+
});
|
|
214
|
+
await scheduler.tick();
|
|
215
|
+
// All three called
|
|
216
|
+
expect(mockActions.recoverStuckInProgressIssues).toHaveBeenCalledTimes(1);
|
|
217
|
+
expect(mockActions.triageNewIssues).toHaveBeenCalledTimes(1);
|
|
218
|
+
// Order: budget → recoverRetriable → recoverStuck → triage
|
|
219
|
+
const budgetIdx = callOrder.indexOf("budget");
|
|
220
|
+
const stuckIdx = callOrder.indexOf("recoverStuck");
|
|
221
|
+
const triageIdx = callOrder.indexOf("triage");
|
|
222
|
+
expect(budgetIdx).toBeLessThan(stuckIdx);
|
|
223
|
+
expect(stuckIdx).toBeLessThan(triageIdx);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
//# sourceMappingURL=reproduce-bec113-pagination-warning.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec113-pagination-warning.test.js","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec113-pagination-warning.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec43-updatedat.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec43-updatedat.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix test for BEC-43:
|
|
3
|
+
* upsertActiveWork now refreshes `updatedAt` on the conflict (update) path.
|
|
4
|
+
*
|
|
5
|
+
* Fix: the `onConflictDoUpdate` set clause in coordination.ts now includes
|
|
6
|
+
* `updatedAt` using a driver-aware SQL literal — `(unixepoch())` for SQLite
|
|
7
|
+
* and `now()` for Postgres — so the timestamp advances on every upsert.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { unlinkSync } from "node:fs";
|
|
12
|
+
import { createDb } from "../db/index.js";
|
|
13
|
+
import { upsertActiveWork, getActiveWork } from "../pm/coordination.js";
|
|
14
|
+
function tmpDbPath() {
|
|
15
|
+
const id = randomBytes(8).toString("hex");
|
|
16
|
+
return `/tmp/laf-bec43-test-${id}.sqlite`;
|
|
17
|
+
}
|
|
18
|
+
describe("BEC-43 reproduction: updatedAt not refreshed on upsert conflict", () => {
|
|
19
|
+
const paths = [];
|
|
20
|
+
async function makeDb() {
|
|
21
|
+
const path = tmpDbPath();
|
|
22
|
+
paths.push(path);
|
|
23
|
+
return createDb({ driver: "sqlite", connectionString: path });
|
|
24
|
+
}
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
for (const p of paths) {
|
|
27
|
+
try {
|
|
28
|
+
unlinkSync(p);
|
|
29
|
+
}
|
|
30
|
+
catch { /* ignore */ }
|
|
31
|
+
try {
|
|
32
|
+
unlinkSync(p + "-wal");
|
|
33
|
+
}
|
|
34
|
+
catch { /* ignore */ }
|
|
35
|
+
try {
|
|
36
|
+
unlinkSync(p + "-shm");
|
|
37
|
+
}
|
|
38
|
+
catch { /* ignore */ }
|
|
39
|
+
}
|
|
40
|
+
paths.length = 0;
|
|
41
|
+
});
|
|
42
|
+
it("updatedAt advances after a conflict-path update (BEC-43 fix)", async () => {
|
|
43
|
+
const db = await makeDb();
|
|
44
|
+
// First insert
|
|
45
|
+
await upsertActiveWork(db, {
|
|
46
|
+
runId: "run-bec43",
|
|
47
|
+
issueId: "BEC-43",
|
|
48
|
+
stage: "implement",
|
|
49
|
+
filesModified: ["src/foo.ts"],
|
|
50
|
+
});
|
|
51
|
+
const [before] = await getActiveWork(db);
|
|
52
|
+
const updatedAtBefore = before.updatedAt;
|
|
53
|
+
// Wait long enough for the timestamp to differ (SQLite unixepoch() is
|
|
54
|
+
// second-granularity, so we wait >1 s to guarantee a detectable change).
|
|
55
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
56
|
+
// Second upsert — conflict path (same runId)
|
|
57
|
+
await upsertActiveWork(db, {
|
|
58
|
+
runId: "run-bec43",
|
|
59
|
+
issueId: "BEC-43",
|
|
60
|
+
stage: "test", // stage changes
|
|
61
|
+
filesModified: ["src/foo.ts", "src/bar.ts"],
|
|
62
|
+
});
|
|
63
|
+
const [after] = await getActiveWork(db);
|
|
64
|
+
// Verify stage was updated (this works)
|
|
65
|
+
expect(after.stage).toBe("test");
|
|
66
|
+
expect(after.filesModified).toHaveLength(2);
|
|
67
|
+
// FIX: updatedAt should have advanced after the conflict-path update.
|
|
68
|
+
const updatedAtAfter = after.updatedAt;
|
|
69
|
+
console.log("updatedAt BEFORE second upsert:", updatedAtBefore);
|
|
70
|
+
console.log("updatedAt AFTER second upsert:", updatedAtAfter);
|
|
71
|
+
console.log("updatedAt changed?", String(updatedAtAfter) !== String(updatedAtBefore));
|
|
72
|
+
// The fix: updatedAt must advance to the current time on every upsert.
|
|
73
|
+
expect(Number(updatedAtAfter)).toBeGreaterThan(Number(updatedAtBefore));
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=reproduce-bec43-updatedat.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec43-updatedat.test.js","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec43-updatedat.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BEC-48 Reproduction: Multi-instance PR creation race condition
|
|
3
|
+
*
|
|
4
|
+
* The pipeline runner serialises push/PR creation with an in-process queue
|
|
5
|
+
* (concurrency=1). This queue lives INSIDE each PipelineRunner instance.
|
|
6
|
+
* When two server instances run simultaneously, each has its own independent
|
|
7
|
+
* pushQueue, so both can execute push/PR creation concurrently for the same
|
|
8
|
+
* branch — producing duplicate PRs.
|
|
9
|
+
*
|
|
10
|
+
* This test file demonstrates the gap:
|
|
11
|
+
* 1. A single-instance pushQueue(1) correctly serialises operations.
|
|
12
|
+
* 2. Two independent pushQueues(1) — simulating two server instances — do NOT
|
|
13
|
+
* serialise with each other. Both run concurrently, causing the race.
|
|
14
|
+
* 3. No distributed lock exists in the push path (only in pm/scheduler.ts for
|
|
15
|
+
* the PM Agent tick — not used here).
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=reproduce-bec48-distributed-race.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec48-distributed-race.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec48-distributed-race.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BEC-48 Reproduction: Multi-instance PR creation race condition
|
|
3
|
+
*
|
|
4
|
+
* The pipeline runner serialises push/PR creation with an in-process queue
|
|
5
|
+
* (concurrency=1). This queue lives INSIDE each PipelineRunner instance.
|
|
6
|
+
* When two server instances run simultaneously, each has its own independent
|
|
7
|
+
* pushQueue, so both can execute push/PR creation concurrently for the same
|
|
8
|
+
* branch — producing duplicate PRs.
|
|
9
|
+
*
|
|
10
|
+
* This test file demonstrates the gap:
|
|
11
|
+
* 1. A single-instance pushQueue(1) correctly serialises operations.
|
|
12
|
+
* 2. Two independent pushQueues(1) — simulating two server instances — do NOT
|
|
13
|
+
* serialise with each other. Both run concurrently, causing the race.
|
|
14
|
+
* 3. No distributed lock exists in the push path (only in pm/scheduler.ts for
|
|
15
|
+
* the PM Agent tick — not used here).
|
|
16
|
+
*/
|
|
17
|
+
import { describe, it, expect } from "vitest";
|
|
18
|
+
import { createQueue } from "../pipeline/queue.js";
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helper: simulate a "create PR" operation with a short delay
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function makePrCreator(label, log) {
|
|
23
|
+
return async () => {
|
|
24
|
+
log.push(`${label}:start`);
|
|
25
|
+
// Simulate network I/O for PR creation (GitHub API call)
|
|
26
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
27
|
+
log.push(`${label}:end`);
|
|
28
|
+
return `https://github.com/test/repo/pull/${label}`;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Test 1 — baseline: single in-process queue serialises correctly
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
describe("BEC-48 baseline: single in-process pushQueue serialises PR creation", () => {
|
|
35
|
+
it("two concurrent enqueue calls on the SAME queue execute sequentially", async () => {
|
|
36
|
+
const singleQueue = createQueue(1);
|
|
37
|
+
const log = [];
|
|
38
|
+
// Both tasks enqueued simultaneously into the SAME queue
|
|
39
|
+
await Promise.all([
|
|
40
|
+
singleQueue.enqueue(makePrCreator("instanceA", log)),
|
|
41
|
+
singleQueue.enqueue(makePrCreator("instanceB", log)),
|
|
42
|
+
]);
|
|
43
|
+
// Because concurrency=1, instanceA must fully complete before instanceB starts
|
|
44
|
+
expect(log).toEqual([
|
|
45
|
+
"instanceA:start",
|
|
46
|
+
"instanceA:end",
|
|
47
|
+
"instanceB:start",
|
|
48
|
+
"instanceB:end",
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Test 2 — reproduce the bug: two independent queues (two server instances)
|
|
54
|
+
// DO NOT protect each other → race condition
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
describe("BEC-48 reproduction: two independent pushQueues race on the same branch", () => {
|
|
57
|
+
it("DEMONSTRATES RACE: two separate PipelineRunner instances create PRs simultaneously", async () => {
|
|
58
|
+
// Each "server instance" has its own PipelineRunner with its own pushQueue.
|
|
59
|
+
// Simulated here as two independent createQueue(1) instances.
|
|
60
|
+
const instanceAQueue = createQueue(1); // Server A's pushQueue
|
|
61
|
+
const instanceBQueue = createQueue(1); // Server B's pushQueue
|
|
62
|
+
const log = [];
|
|
63
|
+
let concurrentPrCreations = 0;
|
|
64
|
+
let maxConcurrentPrCreations = 0;
|
|
65
|
+
const duplicatePrUrls = [];
|
|
66
|
+
const prCreator = (label) => async () => {
|
|
67
|
+
concurrentPrCreations++;
|
|
68
|
+
maxConcurrentPrCreations = Math.max(maxConcurrentPrCreations, concurrentPrCreations);
|
|
69
|
+
log.push(`${label}:start`);
|
|
70
|
+
// Simulate GitHub API call latency
|
|
71
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
72
|
+
log.push(`${label}:end`);
|
|
73
|
+
concurrentPrCreations--;
|
|
74
|
+
const prUrl = "https://github.com/test/repo/pull/42"; // same branch → same PR target
|
|
75
|
+
duplicatePrUrls.push(prUrl);
|
|
76
|
+
return prUrl;
|
|
77
|
+
};
|
|
78
|
+
// Both server instances receive the same webhook at roughly the same time
|
|
79
|
+
// and independently enqueue push+PR creation for the same branch.
|
|
80
|
+
await Promise.all([
|
|
81
|
+
instanceAQueue.enqueue(prCreator("serverA")),
|
|
82
|
+
instanceBQueue.enqueue(prCreator("serverB")),
|
|
83
|
+
]);
|
|
84
|
+
// RACE CONFIRMED: both PR creation calls ran concurrently
|
|
85
|
+
// (maxConcurrentPrCreations === 2 proves there was no mutual exclusion)
|
|
86
|
+
expect(maxConcurrentPrCreations).toBe(2); // ← BUG: should be 1 with a distributed lock
|
|
87
|
+
// Both instances created a PR for the same branch → duplicate PRs
|
|
88
|
+
expect(duplicatePrUrls).toHaveLength(2); // ← BUG: duplicate PR created
|
|
89
|
+
// The log interleaving shows the race: both started before either finished
|
|
90
|
+
expect(log[0]).toBe("serverA:start");
|
|
91
|
+
expect(log[1]).toBe("serverB:start"); // serverB started while serverA was still running
|
|
92
|
+
});
|
|
93
|
+
it("CONTRAST: a shared external lock would prevent concurrent PR creation", async () => {
|
|
94
|
+
// Simulate what a distributed lock (DB advisory lock / Redis SET NX) would do:
|
|
95
|
+
// only one holder at a time, regardless of which process holds it.
|
|
96
|
+
const sharedLock = createQueue(1); // shared across both "instances"
|
|
97
|
+
const instanceAQueue = createQueue(1);
|
|
98
|
+
const instanceBQueue = createQueue(1);
|
|
99
|
+
const log = [];
|
|
100
|
+
let concurrentPrCreations = 0;
|
|
101
|
+
let maxConcurrentPrCreations = 0;
|
|
102
|
+
const prCreator = (label) => async () => {
|
|
103
|
+
// Acquire the shared distributed lock BEFORE push/PR creation
|
|
104
|
+
await sharedLock.enqueue(async () => {
|
|
105
|
+
concurrentPrCreations++;
|
|
106
|
+
maxConcurrentPrCreations = Math.max(maxConcurrentPrCreations, concurrentPrCreations);
|
|
107
|
+
log.push(`${label}:start`);
|
|
108
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
109
|
+
log.push(`${label}:end`);
|
|
110
|
+
concurrentPrCreations--;
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
await Promise.all([
|
|
114
|
+
instanceAQueue.enqueue(prCreator("serverA")),
|
|
115
|
+
instanceBQueue.enqueue(prCreator("serverB")),
|
|
116
|
+
]);
|
|
117
|
+
// With a shared lock, PR creation is serialised across both instances
|
|
118
|
+
expect(maxConcurrentPrCreations).toBe(1); // ← FIXED behaviour
|
|
119
|
+
expect(log).toEqual([
|
|
120
|
+
"serverA:start",
|
|
121
|
+
"serverA:end",
|
|
122
|
+
"serverB:start",
|
|
123
|
+
"serverB:end",
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Test 3 — confirm no distributed lock exists in the push path
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
describe("BEC-48 gap: no distributed lock in runner push path", () => {
|
|
131
|
+
it("pm/scheduler uses advisory lock — but runner.ts pushQueue does NOT", async () => {
|
|
132
|
+
// The advisory lock pattern exists in packages/core/src/pm/scheduler.ts:
|
|
133
|
+
//
|
|
134
|
+
// async function tryAcquireLock(): Promise<boolean> {
|
|
135
|
+
// if (!isPostgres(deps.db)) return true;
|
|
136
|
+
// const result = await db.execute(
|
|
137
|
+
// sql`SELECT pg_try_advisory_lock(hashtext('pm-agent-tick')) as acquired`
|
|
138
|
+
// );
|
|
139
|
+
// return result?.[0]?.acquired === true;
|
|
140
|
+
// }
|
|
141
|
+
//
|
|
142
|
+
// This lock guards only the PM Agent scheduler tick.
|
|
143
|
+
// The push/PR creation in runner.ts (lines 1146–1297) uses only:
|
|
144
|
+
//
|
|
145
|
+
// await this.pushQueue.enqueue(async () => { ... });
|
|
146
|
+
//
|
|
147
|
+
// Where `this.pushQueue = createQueue(1)` — an in-process semaphore.
|
|
148
|
+
//
|
|
149
|
+
// There is NO call to pg_try_advisory_lock, Redis SET NX, or any other
|
|
150
|
+
// cross-process coordination mechanism in the push path.
|
|
151
|
+
// We can verify the gap directly by reading the queue module:
|
|
152
|
+
// createQueue() returns a pure in-memory object with no DB/Redis state.
|
|
153
|
+
const q1 = createQueue(1);
|
|
154
|
+
const q2 = createQueue(1);
|
|
155
|
+
// Both queues are fully independent — there is no shared state
|
|
156
|
+
expect(q1.pending).toBe(0);
|
|
157
|
+
expect(q2.pending).toBe(0);
|
|
158
|
+
let q1Running = false;
|
|
159
|
+
let q2StartedWhileQ1Running = false;
|
|
160
|
+
const blocker = q1.enqueue(async () => {
|
|
161
|
+
q1Running = true;
|
|
162
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
163
|
+
q1Running = false;
|
|
164
|
+
});
|
|
165
|
+
// Give q1 a tick to start
|
|
166
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
167
|
+
await q2.enqueue(async () => {
|
|
168
|
+
// q2 runs while q1 is still holding its slot — confirms no cross-instance lock
|
|
169
|
+
if (q1Running) {
|
|
170
|
+
q2StartedWhileQ1Running = true;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
await blocker;
|
|
174
|
+
// This assertion confirms the bug: q2 ran while q1 was still executing
|
|
175
|
+
expect(q2StartedWhileQ1Running).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=reproduce-bec48-distributed-race.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec48-distributed-race.test.js","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec48-distributed-race.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec62.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec62.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reproduction test for BEC-62:
|
|
3
|
+
* "/pm pause" sets isPmPaused() = true, but scheduler.ts never checks it —
|
|
4
|
+
* promote, deprioritize, and cancel all run even when the PM Agent is paused.
|
|
5
|
+
*
|
|
6
|
+
* Expected (after fix): those three actions are skipped when isPmPaused() === true.
|
|
7
|
+
* Actual (current): all actions run regardless of pause state.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
10
|
+
import { createPmScheduler } from "../pm/scheduler.js";
|
|
11
|
+
import { setPmPaused, isPmPaused } from "../pm/slack-interface.js";
|
|
12
|
+
describe("BEC-62 reproduction — scheduler ignores isPmPaused()", () => {
|
|
13
|
+
const mockActions = {
|
|
14
|
+
checkBudgetGuards: vi.fn().mockResolvedValue({
|
|
15
|
+
promoteBlocked: false,
|
|
16
|
+
activeCount: 0,
|
|
17
|
+
tokenSpendPercent: 10,
|
|
18
|
+
dailyTokensUsed: 500000,
|
|
19
|
+
}),
|
|
20
|
+
triageNewIssues: vi.fn().mockResolvedValue([]),
|
|
21
|
+
resolveApprovals: vi.fn().mockResolvedValue({ resolved: 0, stillPending: 0 }),
|
|
22
|
+
promoteReadyIssues: vi.fn().mockResolvedValue([]),
|
|
23
|
+
deprioritizeStaleIssues: vi.fn().mockResolvedValue([]),
|
|
24
|
+
cancelAbandonedIssues: vi.fn().mockResolvedValue([]),
|
|
25
|
+
postDigest: vi.fn().mockResolvedValue(undefined),
|
|
26
|
+
getActiveFileMaps: vi.fn().mockResolvedValue(new Map()),
|
|
27
|
+
predictConflict: vi.fn().mockResolvedValue({ overlapRisk: "none", likelyFiles: [], reasoning: "" }),
|
|
28
|
+
};
|
|
29
|
+
function makeScheduler() {
|
|
30
|
+
return createPmScheduler({
|
|
31
|
+
config: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
cronIntervalMs: 1800000,
|
|
34
|
+
maxInFlight: 3,
|
|
35
|
+
triageBatchSize: 3,
|
|
36
|
+
dailyTokenBudget: 5000000,
|
|
37
|
+
slackChannelId: "C123",
|
|
38
|
+
teamIds: ["team-1"],
|
|
39
|
+
stuckIssueRecovery: true,
|
|
40
|
+
stuckIssueTargetState: "Todo",
|
|
41
|
+
stuckIssueMaxPerTick: 5,
|
|
42
|
+
},
|
|
43
|
+
db: {},
|
|
44
|
+
linearApiKey: "",
|
|
45
|
+
slackBotToken: "",
|
|
46
|
+
actions: mockActions,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
vi.clearAllMocks();
|
|
51
|
+
setPmPaused(false);
|
|
52
|
+
});
|
|
53
|
+
it("confirms isPmPaused() returns true after /pm pause is issued", () => {
|
|
54
|
+
// Simulates what executePmCommand does for { type: "pause" }
|
|
55
|
+
setPmPaused(true);
|
|
56
|
+
expect(isPmPaused()).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
async function testActionSkippedWhenPaused(actionName, mockAction) {
|
|
59
|
+
setPmPaused(true);
|
|
60
|
+
const scheduler = makeScheduler();
|
|
61
|
+
await scheduler.tick();
|
|
62
|
+
expect(mockAction, `${actionName} must NOT be called when paused`).not.toHaveBeenCalled();
|
|
63
|
+
}
|
|
64
|
+
it("promote is skipped when isPmPaused() === true", async () => {
|
|
65
|
+
// Also verify the flag is set correctly (simulates what /pm pause does)
|
|
66
|
+
setPmPaused(true);
|
|
67
|
+
expect(isPmPaused()).toBe(true);
|
|
68
|
+
await testActionSkippedWhenPaused("promoteReadyIssues", mockActions.promoteReadyIssues);
|
|
69
|
+
});
|
|
70
|
+
it("deprioritize is skipped when isPmPaused() === true", async () => {
|
|
71
|
+
await testActionSkippedWhenPaused("deprioritizeStaleIssues", mockActions.deprioritizeStaleIssues);
|
|
72
|
+
});
|
|
73
|
+
it("cancel is skipped when isPmPaused() === true", async () => {
|
|
74
|
+
await testActionSkippedWhenPaused("cancelAbandonedIssues", mockActions.cancelAbandonedIssues);
|
|
75
|
+
});
|
|
76
|
+
it("non-destructive actions (triage + resolveApprovals) still run when paused", async () => {
|
|
77
|
+
setPmPaused(true);
|
|
78
|
+
const scheduler = makeScheduler();
|
|
79
|
+
await scheduler.tick();
|
|
80
|
+
// Both triage and resolveApprovals should still run — they are non-destructive.
|
|
81
|
+
// CURRENT behavior: they do run (correct, even if for the wrong reason).
|
|
82
|
+
expect(mockActions.triageNewIssues).toHaveBeenCalled();
|
|
83
|
+
expect(mockActions.resolveApprovals).toHaveBeenCalled();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=reproduce-bec62.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reproduce-bec62.test.js","sourceRoot":"","sources":["../../src/__tests__/reproduce-bec62.test.ts"],"names":[],"mappings":"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"}
|