Skip to content

feat: Plan 6 PR B — score recompute scheduler + live CI - #13

Merged
messagesgoel-blip merged 3 commits into
mainfrom
feat/network-score-scheduler
Jul 29, 2026
Merged

feat: Plan 6 PR B — score recompute scheduler + live CI#13
messagesgoel-blip merged 3 commits into
mainfrom
feat/network-score-scheduler

Conversation

@messagesgoel-blip

@messagesgoel-blip messagesgoel-blip commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Single-flight RecomputeScheduler with debounced ingest dirty latch, hourly tick, and deterministic shutdown (finish active + drain ≤1 dirty)
  • markDirty() after successful attestation ingest; start/stop wired in control-plane index.ts
  • Mandatory live RunVeriRank integration (score-recompute.test.ts) + CI starts cmd/trust-engine with TRUST_ENGINE_ADDR
  • Pool recreate between integration suites; bootstrap seed helpers; HANDOVER updated for Plan 6 PR B

Test plan

  • cd control-plane && npm run test:unit
  • Local: trust-engine on non-conflicting ports + TRUST_ENGINE_ADDR=… npm run test:integration (14/14)
  • CI control-plane-integration green with trust-engine
  • @coderabbitai review and address findings

Summary by CodeRabbit

  • New Features

    • Added automatic score recomputation after attestations are submitted.
    • Added periodic and on-demand score updates with duplicate requests coalesced.
    • Score recomputation now starts with the service and shuts down gracefully.
  • Bug Fixes

    • Improved database connection recovery during test and service restarts.
    • Added coverage for expired attestations, deactivated principals, and stale score removal.
  • Tests

    • Expanded integration testing with a locally running trust engine and score synchronization checks.
  • Documentation

    • Updated implementation status and setup instructions for score recomputation.

Add single-flight RecomputeScheduler with dirty latch and deterministic
shutdown drain, hook markDirty after attestation ingest, and require
trust-engine in control-plane integration CI for score-recompute tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2a7e798-2466-40ed-a878-23283bb03fed

📥 Commits

Reviewing files that changed from the base of the PR and between 32ab07d and 34d9641.

📒 Files selected for processing (7)
  • control-plane/src/__tests__/integration/score-recompute.test.ts
  • control-plane/src/db/client.ts
  • control-plane/src/domains/attestation/attestationService.ts
  • control-plane/src/domains/graph/recomputeScheduler.test.ts
  • control-plane/src/domains/graph/recomputeScheduler.ts
  • control-plane/src/index.ts
  • docs/superpowers/plans/HANDOVER.md

Walkthrough

Adds a single-flight score recomputation scheduler, triggers it after attestation persistence, integrates startup and shutdown lifecycle handling, adds score recomputation integration tests, and runs the trust-engine service in CI.

Changes

Score recomputation

Layer / File(s) Summary
Recompute scheduler
control-plane/src/domains/graph/recomputeScheduler.ts, control-plane/src/domains/graph/recomputeScheduler.test.ts
Adds debounced, periodic, single-flight recomputation with dirty-signal coalescing, shutdown draining, singleton access, and lifecycle tests.
Runtime and ingest wiring
control-plane/src/domains/attestation/attestationService.ts, control-plane/src/index.ts
Marks recomputation dirty after committed attestations and starts or stops the scheduler with the control-plane process.
Integration harness and scoring validation
control-plane/src/__tests__/integration/score-recompute.test.ts, control-plane/src/db/*, control-plane/src/testutil/*
Adds score recomputation integration coverage, reusable pool lifecycle handling, bootstrap issuer seeding, configurable attestation tokens, and expanded test cleanup.
CI trust-engine execution
.github/workflows/ci.yml, docs/superpowers/plans/HANDOVER.md
Builds and health-checks trust-engine for integration tests, tears it down after execution, and updates integration instructions.
Estimated code review effort: 4 (Complex) ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AttestationService
  participant RecomputeScheduler
  participant trust-engine
  participant Database
  Client->>AttestationService: Submit attestation
  AttestationService->>Database: Commit attestation
  AttestationService->>RecomputeScheduler: markDirty()
  RecomputeScheduler->>trust-engine: RunVeriRank
  trust-engine-->>RecomputeScheduler: Score result
  RecomputeScheduler->>Database: Upsert or delete network scores
Loading

Possibly related PRs

  • Numeracode/verilink#1: Provides the trust-engine server used by the new CI and score integration flow.
  • Numeracode/verilink#12: Introduces the score computation and writer flow that this scheduler now orchestrates.
  • Numeracode/verilink#8: Also modifies the control-plane integration CI job and its environment configuration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main changes: score recompute scheduler work plus live CI integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/network-score-scheduler

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

@messagesgoel-blip

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add single-flight score recompute scheduler and require live trust-engine in CI

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add in-process single-flight score recompute scheduler with debounce, hourly tick, and
 deterministic stop.
• Mark graph as dirty after successful attestation ingest; start/stop scheduler in control-plane
 entrypoint.
• Require live trust-engine for score recompute integration tests; CI builds/starts it and sets
 TRUST_ENGINE_ADDR.
Diagram

graph TD
  A["Attestation ingest"] --> B(["RecomputeScheduler"]) --> C["Score recompute"]
  C --> D{{"trust-engine gRPC"}} 
  C --> E[("Postgres")]
  F["CI workflow"] --> G["Integration tests"] --> H["control-plane app"]
  F --> D
  G --> D
  G --> H
  subgraph Legend
    direction LR
    _svc["Service/Job"] ~~~ _sched(["Scheduler"]) ~~~ _ext{{"External"}} ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Persisted job queue (Postgres table + worker loop)
  • ➕ Survives process restarts; recompute intent is durable
  • ➕ Easier to coordinate across multiple control-plane replicas
  • ➕ Can support backoff/retries with visibility
  • ➖ More schema + operational complexity
  • ➖ Requires worker management and job dedupe semantics
  • ➖ Overkill if deployments are single-instance for now
2. Postgres LISTEN/NOTIFY + debounce in-process
  • ➕ Cross-process signaling without an external queue
  • ➕ Dirty events become immediate triggers without polling
  • ➖ NOTIFY payloads are ephemeral (missed during downtime)
  • ➖ Still needs single-flight + drain semantics in each replica
  • ➖ More moving parts in tests and local dev
3. Rely only on periodic recompute (no ingest dirty latch)
  • ➕ Simplest implementation and lifecycle surface area
  • ➕ No coupling between ingest and background scheduling
  • ➖ Higher scoring latency after ingest
  • ➖ May cause unnecessary recomputes even when graph is unchanged

Recommendation: The PR’s in-process single-flight scheduler is appropriate given the plan constraints (debounce + hourly + deterministic shutdown) and keeps coordination complexity low. If/when the control-plane is scaled horizontally or restarts become frequent, consider promoting the dirty signal to a durable, cross-replica mechanism (job table or LISTEN/NOTIFY) while keeping the scheduler’s single-flight/drain semantics.

Files changed (12) +650 / -26

Enhancement (4) +216 / -10
client.tsAllow recreating the Postgres pool singleton after end() +18/-9

Allow recreating the Postgres pool singleton after end()

• Refactors pool creation into a factory and exports a mutable singleton pool reference. Adds recreatePool() to support integration harnesses that end the pool between suites while keeping imports stable.

control-plane/src/db/client.ts

attestationService.tsMark recompute scheduler dirty after successful ingest +5/-1

Mark recompute scheduler dirty after successful ingest

• After the transactional attestation upsert completes, calls getRecomputeScheduler().markDirty() outside the transaction. This ensures score recompute is triggered only after durable ingest succeeds.

control-plane/src/domains/attestation/attestationService.ts

recomputeScheduler.tsIntroduce RecomputeScheduler with debounce, interval tick, and drain-on-stop +177/-0

Introduce RecomputeScheduler with debounce, interval tick, and drain-on-stop

• Implements an in-process scheduler that debounces idle dirties, runs single-flight recomputes, and loops while dirty. Adds deterministic shutdown: stop cancels timers, awaits active run, and drains ≤1 dirty rerun while discarding subsequent dirties; includes get/set helpers for default singleton and tests.

control-plane/src/domains/graph/recomputeScheduler.ts

index.tsWire scheduler lifecycle into control-plane startup and shutdown +16/-0

Wire scheduler lifecycle into control-plane startup and shutdown

• Starts the default recompute scheduler after migrations and logs configured debounce/interval values. On shutdown signals, stops the scheduler before closing the HTTP server and ending the DB pool.

control-plane/src/index.ts

Tests (2) +341 / -0
score-recompute.test.tsAdd live RunVeriRank score recompute integration suite +221/-0

Add live RunVeriRank score recompute integration suite

• Introduces integration tests that require a live trust-engine in CI and optionally skip locally when TRUST_ENGINE_ADDR is unset. Validates recompute applies scores and sync_events, respects expiry and deactivated principals, clears scores when bootstrap roots are removed, and exercises scheduler single-flight dirty-latch convergence.

control-plane/src/tests/integration/score-recompute.test.ts

recomputeScheduler.test.tsUnit test single-flight, dirty-latch, and deterministic stop behavior +120/-0

Unit test single-flight, dirty-latch, and deterministic stop behavior

• Adds node:test coverage verifying concurrent dirty signals coalesce into a single follow-up run. Tests stop() semantics: finish active run, drain at most one dirty rerun, and ignore triggers while stopping or before start().

control-plane/src/domains/graph/recomputeScheduler.test.ts

Documentation (2) +11 / -9
transaction.tsDocument that transactions use the live pool export +1/-0

Document that transactions use the live pool export

• Updates comments to clarify withTransaction uses the exported pool reference so pool recreation is visible after suite teardown.

control-plane/src/db/transaction.ts

HANDOVER.mdUpdate handover status for Plan 6 PR B and new integration requirement +10/-9

Update handover status for Plan 6 PR B and new integration requirement

• Updates repository status to reflect Plan 6 PR A merged and Plan 6 PR B in progress. Adds local run notes that score recompute integration requires running trust-engine and setting TRUST_ENGINE_ADDR.

docs/superpowers/plans/HANDOVER.md

Other (4) +82 / -7
ci.ymlBuild/start trust-engine for control-plane integration CI +35/-0

Build/start trust-engine for control-plane integration CI

• Adds Go toolchain setup and builds cmd/trust-engine as a CI artifact. Starts trust-engine with health check, injects TRUST_ENGINE_ADDR into integration tests, and ensures the process is stopped in an always() cleanup step.

.github/workflows/ci.yml

appHarness.tsRecreate ended pool between integration suites +9/-2

Recreate ended pool between integration suites

• Updates the harness to detect an ended singleton pool and recreate it before starting the app. Ensures stop() ends the live pool only if not already ended, improving multi-suite stability and allowing node:test to exit cleanly.

control-plane/src/testutil/appHarness.ts

seedData.tsAdd bootstrap seed helper and JWT time controls for attestations +34/-5

Add bootstrap seed helper and JWT time controls for attestations

• Adds seedBootstrapIssuer() to mark issuers as bootstrap roots for score recompute tests. Extends signAttestationToken() to accept explicit iat/exp controls (including omitting exp) to support expiry-related scenarios.

control-plane/src/testutil/seedData.ts

testDb.tsReset score-related tables between test runs +4/-0

Reset score-related tables between test runs

• Extends resetTestData() truncation list to include sync_events, network_score_history, network_scores, and bootstrap_issuers to keep integration suites isolated.

control-plane/src/testutil/testDb.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@control-plane/src/__tests__/integration/score-recompute.test.ts`:
- Around line 202-216: Replace the fixed sleeps with deadline-bounded polling in
both scheduler test sites:
control-plane/src/__tests__/integration/score-recompute.test.ts lines 202-216
should poll until calls.length reaches at least 2 before stopping the scheduler,
then retain the upper-bound assertion;
control-plane/src/domains/graph/recomputeScheduler.test.ts lines 49-52 should
use the same polling approach before asserting calls.length equals 2. Anchor the
changes to the scheduler test flow and preserve the existing assertions.

In `@control-plane/src/db/client.ts`:
- Around line 21-24: Update recreatePool() so it verifies the existing pool is
ended before replacing the singleton; reject or otherwise fail immediately when
pool is still active, and only call createPool() after the invariant is
satisfied.

In `@control-plane/src/domains/attestation/attestationService.ts`:
- Around line 234-236: Move the getRecomputeScheduler().markDirty() post-commit
call outside the try block that maps attestation write errors, while preserving
the existing row return and error mapping for the durable write. Ensure any
scheduling failure cannot be translated into CONFLICT or cause an
already-successful submission to be reported as failed.

In `@control-plane/src/domains/graph/recomputeScheduler.test.ts`:
- Around line 49-52: In the test around the first scheduled recomputation,
replace the fixed `delay(20)` wait and immediate `calls.length` assertion with
deadline-based polling. Repeatedly check until `calls.length` reaches 2 or the
timeout expires, then retain the assertion that two calls occurred.

In `@control-plane/src/domains/graph/recomputeScheduler.ts`:
- Around line 87-102: Update Scheduler.runLoop so every follow-up iteration
triggered by this.dirty waits for the configured debounce interval before
calling recompute again. Preserve the existing stopping check and error
handling, and ensure the initial run is not unnecessarily delayed.

In `@control-plane/src/index.ts`:
- Around line 39-43: Move the forced-shutdown watchdog setup before the awaited
scheduler.stop() call in the shutdown handler, so it starts before the recompute
drain and still forces exit if that drain hangs. Preserve the existing
scheduler.stop error logging and normal shutdown behavior.

In `@docs/superpowers/plans/HANDOVER.md`:
- Around line 109-110: Update the integration-test instructions around the
trust-engine startup command so the server runs in the background, then export
TRUST_ENGINE_ADDR before invoking control-plane’s npm run test:integration
command. Preserve the required startup ordering and ensure the environment
variable is available to the test process.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8cc95e87-cd26-46e3-93a6-26088a29880a

📥 Commits

Reviewing files that changed from the base of the PR and between 3e0740f and 32ab07d.

📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • control-plane/src/__tests__/integration/score-recompute.test.ts
  • control-plane/src/db/client.ts
  • control-plane/src/db/transaction.ts
  • control-plane/src/domains/attestation/attestationService.ts
  • control-plane/src/domains/graph/recomputeScheduler.test.ts
  • control-plane/src/domains/graph/recomputeScheduler.ts
  • control-plane/src/index.ts
  • control-plane/src/testutil/appHarness.ts
  • control-plane/src/testutil/seedData.ts
  • control-plane/src/testutil/testDb.ts
  • docs/superpowers/plans/HANDOVER.md

Comment thread control-plane/src/__tests__/integration/score-recompute.test.ts
Comment thread control-plane/src/db/client.ts
Comment thread control-plane/src/domains/attestation/attestationService.ts
Comment thread control-plane/src/domains/graph/recomputeScheduler.test.ts
Comment thread control-plane/src/domains/graph/recomputeScheduler.ts
Comment thread control-plane/src/index.ts
Comment thread docs/superpowers/plans/HANDOVER.md Outdated
@qodo-code-review

qodo-code-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 18 rules

Grey Divider


Action required

1. Debounce lost on stop ✓ Resolved 🐞 Bug ≡ Correctness
Description
RecomputeScheduler.markDirty() schedules a debounce without setting the dirty latch, and stop()
cancels the debounce timer; if shutdown happens during the debounce window, the recompute is skipped
even though stop() claims it drains one pending dirty run. This can leave scores stale after a
successful attestation ingest right before shutdown.
Code

control-plane/src/domains/graph/recomputeScheduler.ts[R49-68]

+  /** After successful attestation ingest — coalesce via debounce when idle. */
+  markDirty(): void {
+    // No-op until start() so integration suites that only exercise ingest
+    // do not schedule background recomputes against a test pool.
+    if (this.stopping || !this.started) return;
+    if (this.running) {
+      this.dirty = true;
+      return;
+    }
+    this.scheduleDebounce();
+  }
+
+  private scheduleDebounce(): void {
+    if (this.stopping || this.debounceTimer) return;
+    this.debounceTimer = setTimeout(() => {
+      this.debounceTimer = null;
+      if (this.stopping) return;
+      void this.kick();
+    }, this.debounceMs);
+  }
Relevance

⭐⭐⭐ High

Matches accepted scheduler spec: deterministic shutdown should drain pending dirty; current debounce
can lose it.

PR-#11

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The scheduler never sets dirty when idle+debouncing, but stop() clears the debounce timer and
only drains if dirty is true; therefore a pending debounced run can be lost on shutdown,
contradicting the stated drain behavior.

control-plane/src/domains/graph/recomputeScheduler.ts[49-68]
control-plane/src/domains/graph/recomputeScheduler.ts[120-149]
control-plane/src/domains/graph/recomputeScheduler.ts[137-139]
PR-#11

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`markDirty()` schedules a debounced `kick()` when idle but does **not** record that pending work in `dirty`. During shutdown, `stop()` clears the debounce timer and only drains when `dirty === true`, so a pending debounced recompute can be silently dropped.

### Issue Context
The scheduler’s contract/comments say shutdown should “drain ≤1 dirty rerun,” including the idle case where debounce was cancelled. Current state transitions make that idle drain path unreachable for debounced work.

### Fix Focus Areas
- control-plane/src/domains/graph/recomputeScheduler.ts[49-68]
- control-plane/src/domains/graph/recomputeScheduler.ts[120-149]

### What to change
- Represent “pending debounced recompute” in state, e.g. set `this.dirty = true` when scheduling the debounce (or set it in `stop()` when a debounce was pending).
- Ensure `stop()` treats a pending debounce as dirty so it performs exactly one drain recompute.
- (Optional but helpful) Consider clearing an outstanding debounce timer when a non-debounced `kick()` begins, to avoid an extra redundant run after an interval-triggered `kick()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Test freezes config early ✓ Resolved 🐞 Bug ☼ Reliability
Description
score-recompute.test.ts sets DATABASE_URL defaults but then statically imports
recomputeNow/RecomputeScheduler, which transitively import db/client and construct the singleton
pg.Pool using the pre-default environment. This makes local runs (without externally-set env)
unreliable and can connect recomputeNow to an unintended database.
Code

control-plane/src/tests/integration/score-recompute.test.ts[R7-29]

+process.env.DATABASE_URL ||=
+  'postgresql://verilink:********@127.0.0.1:15432/verilink_test';
+process.env.API_KEY_HMAC_SECRET ||= 'test-hmac-secret-for-integration';
+
+import { describe, it, before, after, beforeEach } from 'node:test';
+import assert from 'node:assert/strict';
+import type pg from 'pg';
+import { setupTestDb, teardownTestDb, resetTestData } from '../../testutil/testDb.js';
+import {
+  seedTenant,
+  seedIssuer,
+  seedSubject,
+  seedBootstrapIssuer,
+  seedApiKey,
+  authHeaders,
+  signAttestationToken,
+} from '../../testutil/seedData.js';
+import { startControlPlane, type ControlPlaneHarness } from '../../testutil/appHarness.js';
+import { recomputeNow } from '../../domains/graph/scoreComputationService.js';
+import {
+  RecomputeScheduler,
+  setRecomputeSchedulerForTests,
+} from '../../domains/graph/recomputeScheduler.js';
Relevance

⭐⭐ Medium

They care about env/config init order, but no close precedent for this exact test import-time pool
issue.

PR-#4

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test file statically imports scoreComputationService/recomputeScheduler, and those modules
import transaction/pool code that constructs the singleton pool at import time; therefore module
initialization can happen before the test’s env defaults execute.

control-plane/src/tests/integration/score-recompute.test.ts[7-29]
control-plane/src/domains/graph/scoreComputationService.ts[19-40]
control-plane/src/domains/graph/attestationGraphLoader.ts[1-5]
control-plane/src/db/transaction.ts[1-16]
control-plane/src/db/client.ts[6-20]
control-plane/src/config.ts[27-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The test file assigns `process.env.DATABASE_URL ||= ...` and `API_KEY_HMAC_SECRET ||= ...` but then uses **static ESM imports** of modules that initialize config and the singleton DB pool at module evaluation time. In ESM, dependency modules evaluate before the current module’s top-level statements, so the pool/config can initialize before these defaults apply.

### Issue Context
`recomputeNow` → `loadAttestationGraph` → `withTransaction` → `db/client` causes `export let pool = createPool()` to run during import. If `DATABASE_URL` wasn’t already set in the process environment, this can fail or connect to the wrong DB.

### Fix Focus Areas
- control-plane/src/__tests__/integration/score-recompute.test.ts[7-29]

### What to change
- Remove the static imports of `recomputeNow` and `RecomputeScheduler` from the top of the file.
- After setting env defaults, load these modules via `await import(...)` (top-level `await` is allowed in ESM) or inside the `before()` hook, then reference the imported symbols.
 - Example approach: declare `let recomputeNow: typeof import('...').recomputeNow;` and assign it from a dynamic import after env setup.
- This ensures config/pool modules see the intended env defaults when they initialize.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Debounce timer keeps alive ✓ Resolved 🐞 Bug ☼ Reliability
Description
RecomputeScheduler.unref() is applied to the interval timer but not to the debounce timeout; if a
suite or short-lived process triggers markDirty and misses stop(), the pending debounce can keep the
event loop alive. This undermines the comment about allowing Node test runners to exit if stop() is
forgotten.
Code

control-plane/src/domains/graph/recomputeScheduler.ts[R61-68]

+  private scheduleDebounce(): void {
+    if (this.stopping || this.debounceTimer) return;
+    this.debounceTimer = setTimeout(() => {
+      this.debounceTimer = null;
+      if (this.stopping) return;
+      void this.kick();
+    }, this.debounceMs);
+  }
Relevance

⭐⭐⭐ High

Consistent with goal to let node tests exit if stop() forgotten; debounce timeout should be unref’d
too.

PR-#11

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The interval timer is explicitly unref’d, but the debounce timer is not, so a pending debounce is
still a live event-loop handle.

control-plane/src/domains/graph/recomputeScheduler.ts[37-47]
control-plane/src/domains/graph/recomputeScheduler.ts[61-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The scheduler unrefs the interval timer, but the debounce `setTimeout` remains referenced. A referenced timeout can keep Node’s event loop alive until it fires, so missed cleanup can hang test runs.

### Issue Context
There is already an explicit intent to avoid this for the interval timer.

### Fix Focus Areas
- control-plane/src/domains/graph/recomputeScheduler.ts[37-47]
- control-plane/src/domains/graph/recomputeScheduler.ts[61-68]

### What to change
- After creating `this.debounceTimer = setTimeout(...)`, call `this.debounceTimer.unref?.()` (same pattern as the interval timer).
- Keep the existing explicit cancellation in `stop()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Hardcoded DB URL and secret 📘 Rule violation ⛨ Security
Description
The new integration test hard-codes a Postgres connection string with credentials and a value for
API_KEY_HMAC_SECRET. This risks leaking credentials/secrets and is likely to be flagged by
gitleaks, violating the repository secret-scanning requirement.
Code

control-plane/src/tests/integration/score-recompute.test.ts[R7-9]

+process.env.DATABASE_URL ||=
+  'postgresql://verilink:********@127.0.0.1:15432/verilink_test';
+process.env.API_KEY_HMAC_SECRET ||= 'test-hmac-secret-for-integration';
Relevance

⭐ Low

Close precedent: team rejected removing exact hardcoded test DB URL/secret despite gitleaks concern.

PR-#7

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2373972 requires that source code contain no secrets/credentials as detected by
gitleaks. The added test code sets process.env.DATABASE_URL to a connection string containing
verilink:verilink@... and sets process.env.API_KEY_HMAC_SECRET to a literal secret-like value,
which are credential/secret patterns.

Rule 2373972: Source code must contain no secrets or credentials as detected by gitleaks
control-plane/src/tests/integration/score-recompute.test.ts[7-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new integration test file hard-codes credential-like values (`DATABASE_URL` with username/password, and `API_KEY_HMAC_SECRET`). This can violate the gitleaks compliance gate and risks accidental secret exposure.

## Issue Context
CI already sets `DATABASE_URL` and `API_KEY_HMAC_SECRET` for integration tests in `.github/workflows/ci.yml`. The test can require these env vars (and optionally skip with a clear message locally) rather than embedding them in source.

## Fix Focus Areas
- control-plane/src/__tests__/integration/score-recompute.test.ts[7-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread control-plane/src/domains/graph/recomputeScheduler.ts
Comment thread control-plane/src/__tests__/integration/score-recompute.test.ts Outdated
Comment thread control-plane/src/domains/graph/recomputeScheduler.ts
Poll for scheduler follow-up runs instead of fixed sleeps; guard
recreatePool; isolate markDirty from write-path errors; arm shutdown
watchdog before drain; clarify trust-engine test docs.

Skip debounce-between-follow-ups: Plan 6 Decision 2 requires immediate
dirty latch reruns (spacing remains via idle debounce only).

Co-authored-by: Cursor <cursoragent@cursor.com>
@messagesgoel-blip

Copy link
Copy Markdown
Collaborator Author

Addressed CodeRabbit actionable findings except the dirty-follow-up debounce spacing (locked Plan 6 Decision 2 — immediate latch rerun). Reply left on that thread.

@messagesgoel-blip

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Set dirty when scheduling debounce so stop drains cancelled idle work,
unref debounce timers, cancel debounce on kick, and load score modules
via dynamic import after env defaults.

Co-authored-by: Cursor <cursoragent@cursor.com>
@messagesgoel-blip

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@messagesgoel-blip
messagesgoel-blip merged commit fcc57db into main Jul 29, 2026
5 checks passed
@messagesgoel-blip
messagesgoel-blip deleted the feat/network-score-scheduler branch July 29, 2026 07:47
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