Skip to content

refactor: extract CooldownFollowUpRunner from CooldownSession (Wave 2A, #375)#398

Merged
cmbays merged 2 commits intomainfrom
wave-2a-cooldown-follow-up-runner
Mar 22, 2026
Merged

refactor: extract CooldownFollowUpRunner from CooldownSession (Wave 2A, #375)#398
cmbays merged 2 commits intomainfrom
wave-2a-cooldown-follow-up-runner

Conversation

@cmbays
Copy link
Owner

@cmbays cmbays commented Mar 22, 2026

Summary\n\n- Extract follow-up analysis pipeline into dedicated CooldownFollowUpRunner class (#375 Wave 2A)\n- 5 analyses extracted: prediction matching, calibration detection, hierarchical promotion, expiry check, friction analysis\n- CooldownSession 1,201 → 1,098 lines (-103L), CooldownFollowUpRunner 124L new\n- Constructor DI with CooldownFollowUpDeps interface using Pick<> for collaborators\n\n## Quality Loop Results\n\n| Gate | Result |\n|------|--------|\n| BDD | 15 scenarios (CPO + CQO audited) |\n| Unit tests | 20 tests |\n| Typecheck | 0 errors |\n| CRAP | PASS (worst 6.0, threshold 8) |\n| ArchUnit | 14 of 15 (pre-existing CycleManager LCOM4) |\n| Mutation | 88.89% on covered code |\n| Full suite | 3,453 unit + 80 acceptance tests pass |\n\n## Key Design Decisions\n\n- Pipeline ordering preserved: calibration runs after prediction matching (verified by BDD scenario using invocationCallOrder)\n- Per-run error isolation: each analysis catches errors per-run so one bad run doesn't poison the batch\n- Duck-typed checkExpiry: knowledge store capability detection preserved as-is\n- Nullable deps pattern: predictionMatcher | null for optional analyses\n\nCloses part of #375\n\n## Test plan\n\n- [x] All 15 BDD scenarios pass\n- [x] All 20 unit tests pass\n- [x] Full unit suite (3,453 tests) passes\n- [x] Full acceptance suite (80 tests) passes\n- [x] Typecheck clean\n- [x] CRAP analysis passes\n- [x] Mutation testing above threshold\n\n🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Implemented cooldown follow-up analysis pipeline with prediction matching, calibration detection, learning promotion, expiry checking, and friction analysis stages.
    • Per-run error isolation with warning logs for failed operations.
  • Tests

    • Added acceptance test scenarios covering pipeline execution and failure modes.
    • Added comprehensive unit test suite for the pipeline runner.

#375)

Extract follow-up analysis pipeline (prediction matching, calibration
detection, hierarchical promotion, expiry check, friction analysis)
into dedicated CooldownFollowUpRunner class with constructor DI.

- CooldownFollowUpRunner (124L new) with CooldownFollowUpDeps interface
- CooldownSession 1,201 -> 1,098 lines (-103L, delegating via followUpRunner)
- 15 BDD scenarios (CPO + CQO audited, pipeline ordering verified)
- 20 unit tests covering all analyses, error isolation, ordering
- CRAP < 8, ArchUnit 14 of 15, mutation 88.89% on covered code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Warning

Rate limit exceeded

@cmbays has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2fa5304a-3c26-4629-9031-5d693c67bf3f

📥 Commits

Reviewing files that changed from the base of the PR and between b3a63cb and 0e7884b.

📒 Files selected for processing (2)
  • src/features/cycle-management/cooldown-follow-up-runner.steps.ts
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
📝 Walkthrough

Walkthrough

This PR extracts cooldown follow-up orchestration logic from CooldownSession into a new, dependency-injected CooldownFollowUpRunner class. The runner orchestrates a pipeline of prediction matching, calibration detection, hierarchical learning promotion, knowledge expiry checking, and friction analysis with isolated error handling. The refactoring includes comprehensive feature specifications, unit tests, and acceptance test definitions.

Changes

Cohort / File(s) Summary
CooldownFollowUpRunner Implementation
src/features/cycle-management/cooldown-follow-up-runner.ts
New class that orchestrates a fixed pipeline of optional dependencies (prediction matcher, calibration detector, hierarchical promoter, friction analyzer) with per-run error isolation, debug logging for expiry checks, and graceful handling of missing dependencies.
Feature & Test Coverage
src/features/cycle-management/cooldown-follow-up-runner.feature, src/features/cycle-management/cooldown-follow-up-runner.steps.ts, src/features/cycle-management/cooldown-follow-up-runner.test.ts
New Gherkin feature specification defining end-to-end pipeline scenarios with background setup; QuickPickle step definitions wiring mock dependencies and verifying invocation order, conditional execution, and warning logs; Vitest suite confirming orchestration, ordering (prediction before calibration), failure isolation per run, and expiry-check hook behavior.
CooldownSession Refactoring
src/features/cycle-management/cooldown-session.ts
Extracted ~103 lines of inline follow-up orchestration (prediction matching, calibration, promotion, expiry, friction) and replaced with single delegated call to followUpRunner.run(phase.cycle) in both run() and prepare() methods.
Setup & Configuration
src/acceptance/setup.ts, stryker.config.mjs
Added import for cooldown-follow-up-runner.steps module in acceptance test setup; added cooldown-follow-up-runner.ts to Stryker mutation testing scope.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A runner hops through cycles bright,
Predictions matched, then checks unite!
We've bundled up the follow-through,
Extracted clean from old to new.
Hop, hop, refactor—done just right! 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main refactoring: extracting CooldownFollowUpRunner from CooldownSession, with wave and issue reference for context.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wave-2a-cooldown-follow-up-runner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 22, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/cycle-management/cooldown-follow-up-runner.steps.ts`:
- Around line 83-90: The logger spies created in the Background (loggerWarnSpy
and loggerDebugSpy on logger) persist across scenarios and leak call history;
add an After hook for the CooldownFollowUpRunnerWorld that calls
vi.restoreAllMocks() (or vi.clearAllMocks() as preferred) to restore/reset the
spies after each scenario, mirroring the pattern used by the SessionBridgeWorld
After hook; also apply the same After cleanup wherever other logger spies are
created (the other block creating spies) so each scenario runs with fresh mocks.

In `@src/features/cycle-management/cooldown-follow-up-runner.test.ts`:
- Line 1: Remove the explicit imports of Vitest globals from the import
statement: eliminate describe, it, expect (and any other test globals like
beforeEach/afterEach if they are only used as globals) and only import vi if you
need the mocking API; rely on the repo's globals: true setting so
describe/it/expect/beforeEach/afterEach are available without import. Update the
import line that currently reads "import { vi, describe, it, expect, beforeEach,
afterEach } from 'vitest';" to only include vi when used, or remove the import
entirely if vi is unused.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e76040ec-92ae-4a23-8a75-50b5debc5263

📥 Commits

Reviewing files that changed from the base of the PR and between 1146853 and b3a63cb.

📒 Files selected for processing (7)
  • src/acceptance/setup.ts
  • src/features/cycle-management/cooldown-follow-up-runner.feature
  • src/features/cycle-management/cooldown-follow-up-runner.steps.ts
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
  • src/features/cycle-management/cooldown-follow-up-runner.ts
  • src/features/cycle-management/cooldown-session.ts
  • stryker.config.mjs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint, Tests, Quality Loop & Build
🧰 Additional context used
📓 Path-based instructions (5)
src/acceptance/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Register step files for .feature files through src/acceptance/setup.ts

Files:

  • src/acceptance/setup.ts
src/**/*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESM-only imports with .js extensions on all internal imports. Project must have "type": "module" in package.json.

Files:

  • src/acceptance/setup.ts
  • src/features/cycle-management/cooldown-session.ts
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
  • src/features/cycle-management/cooldown-follow-up-runner.steps.ts
  • src/features/cycle-management/cooldown-follow-up-runner.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.ts: Use path aliases for imports: @domain/*src/domain/*, @infra/*src/infrastructure/*, @features/*src/features/*, @shared/*src/shared/*, @cli/*src/cli/*. Aliases must be defined in tsconfig.json and vitest.config.ts.
Dependency direction must be strictly enforced: domain → infrastructure → features → shared → cli. No reverse imports allowed.
Coverage thresholds must maintain 80% statements/functions/lines and 75% branches. Coverage excludes test files and src/cli/index.ts.

Files:

  • src/acceptance/setup.ts
  • src/features/cycle-management/cooldown-session.ts
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
  • src/features/cycle-management/cooldown-follow-up-runner.steps.ts
  • src/features/cycle-management/cooldown-follow-up-runner.ts
src/**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be colocated next to source files as *.test.ts with Vitest globals enabled (no need to import describe/it/expect).

Files:

  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
**/*.feature

📄 CodeRabbit inference engine (AGENTS.md)

**/*.feature: Co-locate .feature files with the feature they specify
For new behavior or behavior clarification, acceptance scenarios must exist and fail before implementation

Files:

  • src/features/cycle-management/cooldown-follow-up-runner.feature
🧠 Learnings (10)
📚 Learning: 2026-03-15T01:51:21.342Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Applies to src/acceptance/**/*.ts : Register step files for `.feature` files through `src/acceptance/setup.ts`

Applied to files:

  • src/acceptance/setup.ts
  • stryker.config.mjs
  • src/features/cycle-management/cooldown-follow-up-runner.steps.ts
📚 Learning: 2026-03-15T01:51:21.342Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Run focused mutation testing on changed behavior-heavy modules

Applied to files:

  • stryker.config.mjs
📚 Learning: 2026-03-15T01:51:21.342Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Focused mutation testing must be run for changed business logic or orchestration seams with meaningful survivors explicitly called out

Applied to files:

  • stryker.config.mjs
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
📚 Learning: 2026-03-15T01:51:21.342Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Applies to src/**/{cli,orchestrat}*.ts : Prefer extracting pure helpers from CLI/orchestration files before adding more direct tests to large side-effect-heavy modules

Applied to files:

  • stryker.config.mjs
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
📚 Learning: 2026-03-21T22:25:46.055Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T22:25:46.055Z
Learning: Applies to src/**/*.ts : Dependency direction must be strictly enforced: domain → infrastructure → features → shared → cli. No reverse imports allowed.

Applied to files:

  • stryker.config.mjs
📚 Learning: 2026-03-21T22:25:46.055Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T22:25:46.055Z
Learning: Applies to src/{cli,domain/types}/index.ts : Two entrypoints must be defined via tsup: `cli/index` (bin) and `domain/types/index` (library exports).

Applied to files:

  • stryker.config.mjs
📚 Learning: 2026-03-21T22:25:46.055Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T22:25:46.055Z
Learning: Applies to src/**/*.ts : Coverage thresholds must maintain 80% statements/functions/lines and 75% branches. Coverage excludes test files and `src/cli/index.ts`.

Applied to files:

  • stryker.config.mjs
📚 Learning: 2026-03-15T01:51:21.342Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Applies to src/**/*.command.test.ts : Keep command-level tests focused on wiring and delegation; put parsing/formatting/selection behavior into direct unit tests

Applied to files:

  • stryker.config.mjs
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
📚 Learning: 2026-03-21T22:25:46.055Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T22:25:46.055Z
Learning: Applies to src/**/*.ts : Use path aliases for imports: `domain/*` → `src/domain/*`, `infra/*` → `src/infrastructure/*`, `features/*` → `src/features/*`, `shared/*` → `src/shared/*`, `cli/*` → `src/cli/*`. Aliases must be defined in tsconfig.json and vitest.config.ts.

Applied to files:

  • stryker.config.mjs
📚 Learning: 2026-03-21T22:25:46.055Z
Learnt from: CR
Repo: cmbays/kata PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T22:25:46.055Z
Learning: Applies to src/**/*.test.ts : Test files must be colocated next to source files as `*.test.ts` with Vitest globals enabled (no need to import describe/it/expect).

Applied to files:

  • stryker.config.mjs
  • src/features/cycle-management/cooldown-follow-up-runner.test.ts
🔇 Additional comments (6)
src/features/cycle-management/cooldown-follow-up-runner.ts (2)

42-123: Nice preservation of ordering and failure isolation.

The extracted runner keeps the pipeline order explicit and contains non-critical failures to warnings without aborting cooldown.


8-8: 🧹 Nitpick | 🔵 Trivial

Finish isolating this seam from CooldownSession helpers.

CooldownFollowUpRunner still reaches back into cooldown-session.helpers.ts, so part of the extracted follow-up behavior remains coupled to the old orchestration module. A small runner-local/shared helper for expiry-message formatting would keep the extraction cleaner; if the formatter stays shared, use the @features/... alias instead of ./....

Based on learnings, "Applies to src/**/{cli,orchestrat}*.ts : Prefer extracting pure helpers from CLI/orchestration files before adding more direct tests to large side-effect-heavy modules."

⛔ Skipped due to learnings
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Applies to src/**/{cli,orchestrat}*.ts : Prefer extracting pure helpers from CLI/orchestration files before adding more direct tests to large side-effect-heavy modules
Learnt from: CR
Repo: cmbays/kata PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-15T01:51:21.342Z
Learning: Applies to src/**/*.command.test.ts : Keep command-level tests focused on wiring and delegation; put parsing/formatting/selection behavior into direct unit tests
stryker.config.mjs (1)

19-19: Good mutation-scope update.

Adding the new runner here keeps the extracted orchestration seam inside focused mutation coverage.

Based on learnings, "Focused mutation testing must be run for changed business logic or orchestration seams with meaningful survivors explicitly called out."

src/acceptance/setup.ts (1)

5-5: Good step registration.

This ensures the new cooldown follow-up feature is actually loaded by the acceptance harness.

As per coding guidelines, "Register step files for .feature files through src/acceptance/setup.ts."

src/features/cycle-management/cooldown-session.ts (1)

268-293: Nice single delegation seam.

Resolving the collaborators once and routing both run() and prepare() through followUpRunner should keep future follow-up changes from drifting between the two paths.

Also applies to: 549-549, 605-605

src/features/cycle-management/cooldown-follow-up-runner.feature (1)

1-116: Good acceptance surface for this refactor.

These scenarios lock down ordering, optional capability behavior, and failure isolation—the exact contract this extraction changes.

As per coding guidelines, "For new behavior or behavior clarification, acceptance scenarios must exist and fail before implementation."

- Add After hook to steps file to reset logger spies between scenarios
- Remove explicit Vitest globals imports (repo has globals: true)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cmbays cmbays dismissed coderabbitai[bot]’s stale review March 22, 2026 20:26

Both findings addressed: After hook added for spy cleanup, Vitest globals imports removed.

@cmbays cmbays merged commit d8701e2 into main Mar 22, 2026
1 of 3 checks passed
@cmbays cmbays deleted the wave-2a-cooldown-follow-up-runner branch March 22, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant