Skip to content

Harden fleet control and routing invariants - #21

Open
k-taro56 wants to merge 1 commit into
mainfrom
eng-956
Open

Harden fleet control and routing invariants#21
k-taro56 wants to merge 1 commit into
mainfrom
eng-956

Conversation

@k-taro56

@k-taro56 k-taro56 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • enforce fleet/domain ownership, operation authorization, slot-state validity, and snapshot invariants at the database and repository boundaries
  • preserve byte-identical chat request bodies and harden routing-cache publication against stale or overlapping loads
  • make server/supervisor model selection and timeout propagation safe across rolling upgrades, with authenticated request-size limits
  • align probe limits across Zod, JSON Schema, PostgreSQL constraints, migrations, tests, and English/Japanese documentation
  • constrain the supported Node development runtimes through devEngines

Why

Adversarial review found several boundary cases where cross-fleet identifiers or malformed persisted state could bypass assumptions, concurrent cache loads could publish obsolete knowledge, raw chat payloads could be rewritten during JSON decoding, and mixed-version server/supervisor deployments could shorten timeout budgets or request excessive status data.

Impact

Invalid topology and operation references are now rejected before they can affect routing. Promotion commits require the exact running operation and step. Chat payload bytes and vendor extensions are preserved while malformed UTF-8 fails early. Supervisor calls remain bounded and compatible in either rolling-upgrade direction.

Migration 0003_dazzling_may_parker intentionally stops when existing topology, slot state, or probe-policy rows violate the new constraints. Operators must repair those rows explicitly before applying the migration.

Validation

  • Node 24.17.0 and 24.18.0: build, typecheck, lint, and all 396 tests passed
  • pnpm format:check
  • pnpm exec turbo run build typecheck lint --force (21/21 tasks, no cache)
  • pnpm exec turbo run test --force (12/12 tasks, 396 tests, no cache)
  • pnpm schemas:generate with no resulting diff
  • pnpm db:generate reports no schema changes
  • git diff --check

Summary by cubic

Locks down fleet control and routing by enforcing ownership/state invariants at the DB and repo layers, preserves byte‑exact chat bodies, and makes supervisor timeouts/model selection safe across rolling upgrades. Adds strict probe-policy bounds and fixes routing-cache races. Addresses ENG-956.

  • Bug Fixes

    • Enforced ownership and invariants: composite FKs for fleet/domain pointers, CAS checks include same‑fleet targets, operations cannot target another fleet’s domain, and slot kind/state pairs are CHECKed from @haru/core with snapshot reads validating drifted rows.
    • Stabilized routing cache: coalesces full snapshot loads by fleet/routeRevision/generation to prevent stale or overlapping publications; pointer reads remain per request.
    • Preserved chat body bytes end‑to‑end: proxy forwards the original request bytes (vendor params, whitespace, optional UTF‑8 BOM intact) and rejects malformed UTF‑8 early.
    • Safer supervisor calls: inner timeout = outer minus headroom; status/probe select only layout models via query (size‑capped with all‑models fallback); aligned status/probe budgets and authenticated request-size limits centralized in @haru/protocol.
    • Bounded synthetic probe policy across Zod, JSON Schema, and PostgreSQL: prompt ≤ 8,192 Unicode code points; maxTokens ≤ 256.
  • Migration

    • Run 0003_dazzling_may_parker: it stops if data violates new constraints (cross‑fleet pointers, invalid slot states, or out‑of‑range probe policy). Repair offending rows, then re-run.
    • Before upgrading, ensure policy.probe.prompt ≤ 8,192 code points and policy.probe.maxTokens ≤ 256. Re-applying a layout does not update existing policy rows.
    • Dev environments: use Node 24.17.0 or 24.18.0 per devEngines.

Written for commit 0f21d86. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Supervisor status and probe requests can target specific configured models.
    • Improved timeout budgeting and cancellation for status checks and probes.
    • Probe prompts and token counts now enforce shared limits: 8,192 Unicode code points and 256 tokens.
  • Bug Fixes

    • Chat requests now preserve original request bytes, including formatting and Unicode details.
    • Improved routing safety, fleet-state validation, snapshot loading, and cache behavior.
    • Invalid model selections and inconsistent persisted state are rejected more reliably.
  • Documentation

    • Documented probe-policy limits and upgrade requirements.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​node@​24.13.31001008195100

View full report

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR strengthens fleet persistence invariants, adds bounded probe policies, introduces model-aware supervisor status and probe flows, preserves chat request bytes, and coalesces snapshot loads while protecting cache freshness during concurrent revisions.

Changes

Protocol and persistence integrity

Layer / File(s) Summary
Protocol invariants and probe contracts
packages/protocol/...
Adds fleet snapshot relationship checks, Unicode-aware probe prompt limits, token limits, JSON Schema synchronization, and supervisor timeout/model-selection schemas.
Database ownership and state constraints
packages/db/src/schema/..., packages/db/drizzle/meta/*
Adds composite fleet-domain ownership constraints, probe policy checks, valid slot-state checks, and updated migration metadata.
Legacy migration preflight
packages/db/drizzle/0003_dazzling_may_parker.sql, packages/db/src/migration-preflight.test.ts
Validates legacy ownership, policy, and slot data before applying new constraints.
Repository guards and persistence validation
packages/db/src/repo/*, packages/db/src/*test.ts
Tightens routing-operation CAS checks, atomic operation creation, and persisted snapshot validation.

Model-aware supervisor control flow

Layer / File(s) Summary
Supervisor timeout and selection contracts
packages/protocol/src/supervisor.ts
Defines shared timeout, model-selection, and request validation contracts.
Selector-aware supervisor client
services/haru-server/src/supervisor-client.ts, services/haru-server/src/supervisor-client.test.ts
Adds model query serialization, inner timeout budgets, frozen timeout reads, and oversized-selector fallback behavior.
Status and probe request handling
services/haru-supervisor/src/app.ts, services/haru-supervisor/src/probe.ts, services/haru-supervisor/src/vllm-client.ts
Filters selected models, enforces body and timeout validation, and propagates abort signals.
Layout-bound reconciler decisions
services/haru-server/src/reconciler/*
Passes layout-derived inference model names into supervisor status and probe calls.
Supervisor behavior coverage
services/haru-supervisor/src/app.test.ts
Tests authentication ordering, selection semantics, timeout handling, body limits, aborts, and probe policy bounds.

Chat proxy and snapshot cache

Layer / File(s) Summary
Snapshot single-flight and publication
services/haru-server/src/app.ts, services/haru-server/src/chat.test.ts
Coalesces concurrent snapshot reads, cleans failed loads, starts TTL at publication, and preserves newer cached revisions.
Byte-preserving chat proxy
services/haru-server/src/app.ts, services/haru-server/src/chat-proxy.ts, services/haru-server/src/fake-supervisor.test-helper.ts
Forwards original request bytes while decoding a separate copy to extract model.
Cache and proxy behavior coverage
services/haru-server/src/chat.test.ts, services/haru-server/src/promotion-flow.test.ts
Covers byte identity, invalid UTF-8, cache coalescing, TTL timing, retries, and timeout-budget expectations.

Documentation and runtime support

Layer / File(s) Summary
Policy and proxy boundary documentation
AGENTS.md, README*
Documents raw-byte forwarding and synthetic probe policy migration limits.
Runtime and test execution constraints
package.json, pnpm-workspace.yaml, */vitest.config.ts
Pins supported Node versions and limits Vitest workers to two.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • arkorlab/haru#1 — Defines the original chat proxy path modified here for byte-preserving forwarding.
  • arkorlab/haru#6 — Overlaps with supervisor and reconciler readiness and timeout handling.
  • arkorlab/haru#12 — Overlaps with fail-open snapshot caching and chat completion handling.

Suggested reviewers: soleil-colza

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.96% which is insufficient. The required threshold is 100.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main theme of hardening fleet control and routing invariants.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eng-956
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch eng-956

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.

@drift-check

drift-check Bot commented Jul 20, 2026

Copy link
Copy Markdown

Code Review Bot

No comment/code divergences detected. ⚠️ The documentation drift check failed and was not evaluated. Reviewed 43 file(s); skipped 1.

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/haru-server/src/reconciler/steps.ts (1)

447-453: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse configuredInferenceModelNames instead of re-deriving the expected model set.

This inline flatMap is identical to the new configuredInferenceModelNames(domain) helper (also reused by isEveryConfiguredInferenceModel). Two copies of the layout-bound-model derivation can silently diverge, and this one gates routing safety, so consolidate.

♻️ Proposed consolidation
-      const expectedModels = new Set(
-        domain.slots.flatMap((slot) =>
-          slot.spec.kind === "inference"
-            ? slot.spec.models.map((m) => m.name)
-            : [],
-        ),
-      );
+      const expectedModels = new Set(configuredInferenceModelNames(domain));
🤖 Prompt for 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.

In `@services/haru-server/src/reconciler/steps.ts` around lines 447 - 453, Replace
the inline expectedModels Set construction in the reconciler step with the
existing configuredInferenceModelNames(domain) helper. Keep the resulting set
semantics and routing-safety behavior unchanged, and do not duplicate the
slot/model derivation logic.
🤖 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 `@packages/db/drizzle/0003_dazzling_may_parker.sql`:
- Around line 81-83: The composite foreign keys
fleets_active_domain_membership_fk, operations_target_domain_membership_fk, and
operations_source_domain_membership_fk should be added with NOT VALID, followed
by separate VALIDATE CONSTRAINT statements to reduce the exclusive-lock window
while preserving enforcement for new writes. Apply the same NOT
VALID-then-VALIDATE approach to the CHECK constraints immediately below.

In `@README.md`:
- Around line 306-307: Update the migration repair guidance in README.md lines
306-307 to refer to the offending persisted prompt or maxTokens value,
instructing operators to edit that value or remove its field rather than
referring to a persisted key. Apply the equivalent wording in README.ja.md lines
317-318, such as directing operators to fix the violating persisted value or
delete the relevant field.

---

Outside diff comments:
In `@services/haru-server/src/reconciler/steps.ts`:
- Around line 447-453: Replace the inline expectedModels Set construction in the
reconciler step with the existing configuredInferenceModelNames(domain) helper.
Keep the resulting set semantics and routing-safety behavior unchanged, and do
not duplicate the slot/model derivation logic.
🪄 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 Plus

Run ID: a6adc4c7-2ff1-43b1-b098-32344535f0a2

📥 Commits

Reviewing files that changed from the base of the PR and between 9bf0062 and 0f21d86.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • AGENTS.md
  • README.ja.md
  • README.md
  • package.json
  • packages/db/drizzle/0003_dazzling_may_parker.sql
  • packages/db/drizzle/meta/0003_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/cas.test.ts
  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/operations.test.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/repo/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/db/src/schema/domains.ts
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/fleets.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/schema/slots.ts
  • packages/db/vitest.config.ts
  • packages/protocol/schemas/fleet-layout.schema.json
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • packages/protocol/src/fleet.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/src/json-schema.ts
  • packages/protocol/src/policy.ts
  • packages/protocol/src/protocol.test.ts
  • packages/protocol/src/supervisor.ts
  • pnpm-workspace.yaml
  • services/haru-server/src/app.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/src/chat.test.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/promotion-flow.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-server/src/supervisor-client.test.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-server/vitest.config.ts
  • services/haru-supervisor/src/app.test.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-supervisor/src/probe.ts
  • services/haru-supervisor/src/vllm-client.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: check
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep new I/O behind injectable boundaries so it can be tested without GPUs, cloud accounts, or a running database.

新しい I/O は注入可能な境界の背後に配置し、外部実行、fetch、子プロセス、タイマーなどをテストダブルに置き換えられるようにする。

**/*.{ts,tsx}: Respect the dependency graph: @haru/protocol has no workspace dependencies beyond permitted built-ins, @haru/core is pure and performs no I/O, @haru/db depends on core, services/haru-supervisor depends only on protocol, and shared server/supervisor functionality belongs in protocol.
Build outbound URLs with joinUrl from @haru/protocol; do not use new URL('/path', base) when the base may contain a path prefix.
Use English comments and avoid the em dash character (U+2014) in code and prose.

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/schema/domains.ts
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/db/src/schema/fleets.ts
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add a Vitest case next to changed code when introducing or modifying behavior.

変更したコードの近くに Vitest のテストを追加し、外部 I/O は注入可能な境界に対してテストする。

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/db/src/persistence-invariants.test.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-supervisor/src/app.test.ts
  • services/haru-server/src/chat.test.ts
**/*.{ts,tsx,js,jsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use oxfmt as the owner of formatting, including whitespace, wrapping, quotes, and trailing commas; do not hand-tune formatting for ESLint.

Files:

  • packages/db/src/migration-preflight.test.ts
  • package.json
  • packages/db/src/schema/domains.ts
  • README.md
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/schemas/fleet-layout.schema.json
  • AGENTS.md
  • packages/db/src/schema/fleets.ts
  • README.ja.md
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schema/fleet-topology.ts
  • pnpm-workspace.yaml
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • packages/db/drizzle/meta/0003_snapshot.json
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run both root-configured linters, oxlint --type-aware followed by strict type-aware ESLint 10; add overrides at the repository root rather than per-package configs.

**/*.{ts,tsx,js,jsx}: oxlint の型認識 lint を実行した後、型情報ベースの strict ESLint 10 を実行する。設定はパッケージごとではなくリポジトリルートに置き、例外には理由をコメントしたスコープ付きオーバーライドを優先する。
コード内のコメントは英語で記述する。

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/schema/domains.ts
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/db/src/schema/fleets.ts
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Use kebab-case for file names.
Write comments in English.
Do not use the em dash character (U+2014) in code or prose; use a colon, comma, parentheses, or spaced hyphen instead.

ファイル名は kebab-case にする。

Keep the repository publishable: do not reference consumer-private repositories or infrastructure, and do not include specific model or GPU names in code, seeds, or example layouts.

Files:

  • packages/db/src/migration-preflight.test.ts
  • package.json
  • packages/db/src/schema/domains.ts
  • README.md
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/schemas/fleet-layout.schema.json
  • AGENTS.md
  • packages/db/src/schema/fleets.ts
  • README.ja.md
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schema/fleet-topology.ts
  • pnpm-workspace.yaml
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/db/drizzle/0003_dazzling_may_parker.sql
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • packages/db/drizzle/meta/0003_snapshot.json
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
packages/db/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test @haru/db against in-memory PGlite using the committed Drizzle migrations, including compare-and-swap SQL and concurrent-winner races.

状態ストアの状態遷移では、compare-and-swap SQL によって並行実行時の勝者決定レースを保護する。

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/schema/domains.ts
  • packages/db/src/operations.test.ts
  • packages/db/src/schema/fleets.ts
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/slots.ts
  • packages/db/src/repo/operations.ts
  • packages/db/src/cas.test.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
**/*.{ts,tsx,js,jsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (CONTRIBUTING.ja.md)

oxfmt を使用して、空白、折り返し、クォート、末尾カンマを整形する。整形確認には pnpm format:check を使用する。

Files:

  • packages/db/src/migration-preflight.test.ts
  • package.json
  • packages/db/src/schema/domains.ts
  • README.md
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/schemas/fleet-layout.schema.json
  • AGENTS.md
  • packages/db/src/schema/fleets.ts
  • README.ja.md
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schema/fleet-topology.ts
  • pnpm-workspace.yaml
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • packages/db/drizzle/meta/0003_snapshot.json
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
**/*.{ts,tsx,js,jsx,md}

📄 CodeRabbit inference engine (CONTRIBUTING.ja.md)

コードと文章ではエムダッシュ (U+2014) を使用せず、コロン、コンマ、括弧、またはスペース付きハイフンを使用する。

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/schema/domains.ts
  • README.md
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • AGENTS.md
  • packages/db/src/schema/fleets.ts
  • README.ja.md
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/slots.ts
  • services/haru-supervisor/src/probe.ts
  • packages/protocol/src/json-schema.ts
  • packages/db/src/repo/operations.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/protocol/src/policy.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/vitest.config.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
  • packages/protocol/src/fleet.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-supervisor/src/app.test.ts
  • packages/protocol/src/supervisor.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-supervisor/src/app.ts
  • services/haru-server/src/chat.test.ts
packages/db/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

packages/db/src/**/*.{ts,tsx}: Implement every database state transition as a single-statement compare-and-swap with guarded predicates and row-count checking. Never use db.transaction() or perform external work between a read and its dependent write.
Keep core state tables as the single source of truth and have repositories reject invalid (from, to) transitions with InvalidTransitionError.
Use createTestDatabase with committed migrations and do not add per-test migration calls.

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/schema/domains.ts
  • packages/db/src/operations.test.ts
  • packages/db/src/schema/fleets.ts
  • packages/db/src/schema/fleet-topology.ts
  • packages/db/src/schema/slots.ts
  • packages/db/src/repo/operations.ts
  • packages/db/src/cas.test.ts
  • packages/db/src/repo/fleets.ts
  • packages/db/src/persistence-invariants.test.ts
  • packages/db/src/schema/operations.ts
  • packages/db/src/repo/snapshots.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Inject all I/O including fetch, exec, spawn, and clocks. Server tests should use app.request() with scripted fake supervisors; supervisor signal escalation tests should use fake timers.

Files:

  • packages/db/src/migration-preflight.test.ts
  • packages/db/src/operations.test.ts
  • packages/protocol/src/json-schema.test.ts
  • packages/protocol/src/fleet-snapshot-invariants.test.ts
  • services/haru-server/src/supervisor-client.test.ts
  • packages/db/src/cas.test.ts
  • packages/db/src/persistence-invariants.test.ts
  • services/haru-server/src/promotion-flow.test.ts
  • packages/protocol/src/protocol.test.ts
  • services/haru-supervisor/src/app.test.ts
  • services/haru-server/src/chat.test.ts
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Resolve direct third-party dependencies through the catalog: in pnpm-workspace.yaml; keep workspace links as workspace:*.

Files:

  • package.json
{README.md,README.ja.md,CONTRIBUTING.md,CONTRIBUTING.ja.md,KNOWN_ISSUES.md,KNOWN_ISSUES.ja.md}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain English/Japanese documentation pairs together; update the corresponding translation when editing one side, and record new reviewed deferrals in both KNOWN_ISSUES files.

Files:

  • README.md
  • README.ja.md
pnpm-workspace.yaml

📄 CodeRabbit inference engine (AGENTS.md)

Do not weaken supply-chain protections such as minimumReleaseAge, trustPolicy, blockExoticSubdeps, or allowBuilds, and do not add trustPolicyExclude entries autonomously.

Files:

  • pnpm-workspace.yaml
services/haru-supervisor/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Test supervisor behavior with fake fetch, fake child-process handles, and fake timers, including SIGTERM-to-grace-period-to-SIGKILL escalation.

スーパーバイザーのプロセス終了処理では、SIGTERM を送信し、猶予期間後に SIGKILL へエスカレーションする挙動をテストする。

Files:

  • services/haru-supervisor/src/probe.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-supervisor/src/app.test.ts
  • services/haru-supervisor/src/app.ts
services/haru-supervisor/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Training child processes must register both exit and error handlers on ChildHandle; spawn failures emit error without exit.

Files:

  • services/haru-supervisor/src/probe.ts
  • services/haru-supervisor/src/vllm-client.ts
  • services/haru-supervisor/src/app.test.ts
  • services/haru-supervisor/src/app.ts
services/haru-server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run server suites against in-memory PGlite with committed Drizzle migrations so compare-and-swap SQL guarding state transitions is exercised, including concurrent-winner races.

Files:

  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/vitest.config.ts
  • services/haru-server/src/supervisor-client.test.ts
  • services/haru-server/src/promotion-flow.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-server/src/chat.test.ts
services/haru-server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

services/haru-server/src/**/*.{ts,tsx}: Use fleets.activeDomainId as the sole routing pointer; switchActive is the only writer and must atomically commit routing and the driving operation’s routingCommitted state.
The reconciler must be re-entrant and check-and-nudge based: issue at most one step nudge per tick, make executors safe to rerun, and apply executor outcomes and timeouts through the single CAS-and-audit path applyStepResolution.
Use the injected application clock for stepStartedAt and domains.stateUpdatedAt; do not replace it with sqlnow()``.
Map supervisor failures through withSupervisor: target-domain 401/403 responses fail immediately, other failures remain pending until the policy budget expires, and best-effort source steps treat authentication failures as pending. Keep response parsing inside SupervisorError wrapping.
Degraded escalation must remain guarded by policy settings, a viable standby, no in-flight operation, and the active routing pointer; all guards belong inside the escalation UPDATE. Recovery of failed active domains must verify every LAYOUT-bound model is served.
Completion checks over supervisor-reported lists must require `length > 0 && every(...)`; never allow an empty list to vacuously succeed.
Keep the data path fail-open and the control path fail-closed. Serve only the last cached snapshot when the pointer read fails; after a successful pointer read, fail closed on revision movement or malformed state, and serve a cached snapshot only when its revision matches.
`/healthz` must never access the database.
Distinguish a pointer lookup returning `null` from throwing: `null` means the fleet is gone and may be forgotten; a thrown lookup means the store is unavailable and the cache entry must be retained.
Use case-insensitive lowercase identity only for UUID fleet IDs; keep slugs and aliases case-sensitive. Apply the database ID-first resolution rule so UUID-shaped slugs cannot shadow an ID owner.
Do not publish an ove...

Files:

  • services/haru-server/src/chat-proxy.ts
  • services/haru-server/src/supervisor-client.test.ts
  • services/haru-server/src/promotion-flow.test.ts
  • services/haru-server/src/reconciler/reconciler.ts
  • services/haru-server/src/app.ts
  • services/haru-server/src/fake-supervisor.test-helper.ts
  • services/haru-server/src/reconciler/steps.ts
  • services/haru-server/src/supervisor-client.ts
  • services/haru-server/src/chat.test.ts
🪛 ast-grep (0.44.1)
packages/db/src/migration-preflight.test.ts

[error] 32-47: Avoid SQL injection
Context: client.query(
INSERT INTO fleets (slug, policy) VALUES ($1, $2::jsonb), ($3, $4::jsonb),
[
"oversized-prompt",
JSON.stringify({
probe: {
prompt: "x".repeat(MAX_PROBE_PROMPT_CODE_POINTS + 1),
},
}),
"oversized-tokens",
JSON.stringify({
probe: { maxTokens: MAX_PROBE_TOKENS + 1 },
}),
],
)
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-typescript)

🪛 OpenGrep (1.25.0)
packages/db/src/migration-preflight.test.ts

[ERROR] 17-17: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 52-52: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 70-70: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🪛 SQLFluff (4.2.2)
packages/db/drizzle/0003_dazzling_may_parker.sql

[error] 81-81: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 82-82: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 83-83: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)

🔇 Additional comments (44)
package.json (1)

45-45: LGTM!

pnpm-workspace.yaml (1)

16-16: LGTM!

packages/db/vitest.config.ts (1)

10-13: LGTM!

services/haru-server/vitest.config.ts (1)

10-12: LGTM!

packages/protocol/schemas/fleet-layout.schema.json (1)

85-91: LGTM!

packages/protocol/src/policy.ts (1)

12-44: LGTM!

packages/protocol/src/json-schema.ts (1)

4-48: LGTM!

packages/protocol/src/json-schema.test.ts (1)

66-89: LGTM!

packages/protocol/src/fleet.ts (1)

128-218: LGTM!

packages/protocol/src/protocol.test.ts (1)

126-166: LGTM!

Also applies to: 243-330, 584-643

packages/db/drizzle/0003_dazzling_may_parker.sql (1)

1-74: LGTM!

packages/db/src/migration-preflight.test.ts (1)

1-97: LGTM!

packages/db/src/repo/snapshots.ts (1)

1-1: LGTM!

Also applies to: 146-192

packages/db/src/operations.test.ts (1)

100-119: LGTM!

packages/db/src/persistence-invariants.test.ts (1)

1-188: LGTM!

packages/protocol/src/fleet-snapshot-invariants.test.ts (1)

1-115: LGTM!

packages/db/src/schema/fleet-topology.ts (1)

1-136: LGTM!

packages/db/src/schema/domains.ts (1)

1-2: LGTM!

packages/db/src/schema/fleets.ts (1)

1-2: LGTM!

packages/db/src/schema/operations.ts (1)

4-4: LGTM!

Also applies to: 29-36, 63-82

packages/db/src/schema/slots.ts (1)

1-1: LGTM!

Also applies to: 19-28, 56-59

packages/db/drizzle/meta/0003_snapshot.json (1)

1-653: LGTM!

packages/db/drizzle/meta/_journal.json (1)

25-31: LGTM!

packages/db/src/repo/fleets.ts (1)

3-3: LGTM!

Also applies to: 41-53, 76-84

packages/db/src/repo/operations.ts (1)

56-87: LGTM!

packages/db/src/cas.test.ts (1)

2-10: LGTM!

Also applies to: 61-78, 124-177, 612-631

services/haru-server/src/app.ts (2)

169-175: LGTM!

Also applies to: 195-221, 358-362, 387-395


625-625: LGTM!

Also applies to: 820-832, 875-875

services/haru-server/src/chat.test.ts (3)

9-9: LGTM!

Also applies to: 39-52, 76-94


124-179: LGTM!

Also applies to: 593-682


877-877: LGTM!

Also applies to: 909-909, 993-993, 1025-1025, 1057-1057, 1092-1092, 1134-1134, 1157-1157, 1275-1319, 1388-1388, 1422-1422

AGENTS.md (1)

143-145: LGTM!

services/haru-server/src/chat-proxy.ts (1)

35-42: LGTM!

Also applies to: 61-61

services/haru-server/src/fake-supervisor.test-helper.ts (1)

201-221: LGTM!

Also applies to: 250-254

services/haru-server/src/promotion-flow.test.ts (1)

266-268: LGTM!

Also applies to: 279-279

packages/protocol/src/supervisor.ts (1)

4-35: LGTM!

Also applies to: 46-63, 188-209, 227-238

services/haru-server/src/supervisor-client.ts (1)

6-8: LGTM!

Also applies to: 47-54, 97-158, 181-204, 227-249

services/haru-server/src/supervisor-client.test.ts (1)

71-99: LGTM!

Also applies to: 101-129, 131-167

services/haru-supervisor/src/app.ts (1)

3-16: LGTM!

Also applies to: 24-52, 66-97, 158-210, 212-256, 434-508

services/haru-supervisor/src/probe.ts (1)

25-25: LGTM!

Also applies to: 43-43

services/haru-supervisor/src/vllm-client.ts (1)

11-15: LGTM!

Also applies to: 30-52, 101-113

services/haru-server/src/reconciler/reconciler.ts (1)

26-34: LGTM!

Also applies to: 220-228

services/haru-server/src/reconciler/steps.ts (1)

212-224: LGTM!

Also applies to: 247-252, 324-327, 404-407, 554-557, 585-588, 621-624, 656-659

services/haru-supervisor/src/app.test.ts (1)

1-6: LGTM!

Also applies to: 114-162, 248-303, 305-335, 541-579, 612-693

Comment on lines +81 to +83
ALTER TABLE "fleets" ADD CONSTRAINT "fleets_active_domain_membership_fk" FOREIGN KEY ("id","active_domain_id") REFERENCES "public"."domains"("fleet_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "operations" ADD CONSTRAINT "operations_target_domain_membership_fk" FOREIGN KEY ("fleet_id","target_domain_id") REFERENCES "public"."domains"("fleet_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "operations" ADD CONSTRAINT "operations_source_domain_membership_fk" FOREIGN KEY ("fleet_id","source_domain_id") REFERENCES "public"."domains"("fleet_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔵 Trivial

Consider NOT VALID + a follow-up VALIDATE CONSTRAINT for the composite FKs.

The plain ADD CONSTRAINT ... FOREIGN KEY takes an ACCESS EXCLUSIVE lock on the referencing table and performs a full validating scan. The preflight DO $$ block above has already proven every referencing row satisfies these relationships, so the validating scan here is redundant lock time. Adding the FK NOT VALID (constraint still enforces all new writes immediately) and running VALIDATE CONSTRAINT separately (which takes only SHARE UPDATE EXCLUSIVE) shortens the exclusive-lock window during rollout. The same applies to the CHECK constraints on lines 84-85. Given these are small control-plane tables the impact is modest, so treat this as a rollout hygiene improvement rather than a blocker.

🧰 Tools
🪛 SQLFluff (4.2.2)

[error] 81-81: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 82-82: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 83-83: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)

🤖 Prompt for 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.

In `@packages/db/drizzle/0003_dazzling_may_parker.sql` around lines 81 - 83, The
composite foreign keys fleets_active_domain_membership_fk,
operations_target_domain_membership_fk, and
operations_source_domain_membership_fk should be added with NOT VALID, followed
by separate VALIDATE CONSTRAINT statements to reduce the exclusive-lock window
while preserving enforcement for new writes. Apply the same NOT
VALID-then-VALIDATE approach to the CHECK constraints immediately below.

Source: Linters/SAST tools

Comment thread README.md
Comment on lines +306 to +307
existing value above either limit. Shorten, lower, or remove the
offending persisted key before upgrading; re-applying a layout does

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Describe the migration repair target as a persisted value or field, not a key.

  • README.md#L306-L307: Tell operators to edit the offending prompt or maxTokens value, or remove that field.
  • README.ja.md#L317-L318: Use wording such as 違反している保存済みの値を修正するか、該当フィールドを削除してください。
📍 Affects 2 files
  • README.md#L306-L307 (this comment)
  • README.ja.md#L317-L318
🤖 Prompt for 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.

In `@README.md` around lines 306 - 307, Update the migration repair guidance in
README.md lines 306-307 to refer to the offending persisted prompt or maxTokens
value, instructing operators to edit that value or remove its field rather than
referring to a persisted key. Apply the equivalent wording in README.ja.md lines
317-318, such as directing operators to fix the violating persisted value or
delete the relevant field.

@k-taro56
k-taro56 marked this pull request as ready for review July 20, 2026 06:37
@k-taro56

Copy link
Copy Markdown
Contributor Author

@codex

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR hardens fleet control, routing, and supervisor boundaries. The main changes are:

  • Database and repository checks for fleet ownership and valid persisted state.
  • Byte-preserving chat proxy requests and safer routing-cache publication.
  • Model-selective supervisor calls with rolling-upgrade timeout handling.
  • Shared probe limits across schemas, migrations, tests, and documentation.

Confidence Score: 5/5

This looks safe to merge after a small memory-bounding cleanup.

  • Core ownership, migration, cache-race, and rolling-upgrade paths are consistent.
  • The remaining issue is gradual memory growth from unique fleet references.

services/haru-server/src/app.ts

Important Files Changed

Filename Overview
services/haru-server/src/app.ts Adds raw-body handling and race-safe snapshot caching, but the new reference-generation map has no eviction policy.
services/haru-server/src/chat-proxy.ts Forwards the original chat request bytes without reserializing the payload.
packages/db/drizzle/0003_dazzling_may_parker.sql Adds preflight checks and database constraints for ownership, slot states, and probe limits.
packages/db/src/repo/fleets.ts Restricts active-domain changes to same-fleet targets and the exact authorized promotion step.
packages/db/src/repo/operations.ts Creates operations through an ownership-scoped insert that captures the source pointer atomically.
packages/db/src/repo/snapshots.ts Rejects malformed slot kind and state combinations at the persistence read boundary.
services/haru-server/src/supervisor-client.ts Adds model selectors and separates inner work timeouts from outer response budgets.
services/haru-supervisor/src/app.ts Adds request limits, model filtering, timeout compatibility, and cancellation propagation.
packages/protocol/src/policy.ts Centralizes probe prompt and token limits using Unicode code-point semantics.
packages/protocol/src/fleet.ts Validates snapshot ownership, uniqueness, and active-domain relationships.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant Server as haru-server
    participant Cache as Routing cache
    participant DB
    participant Supervisor
    participant VLLM

    Client->>Server: Authenticated chat request
    Server->>DB: Read route pointer
    Server->>Cache: Check revisioned snapshot
    alt Snapshot missing or stale
        Server->>DB: Coalesced snapshot load
        DB-->>Server: Validated snapshot
        Server->>Cache: Publish if generation is current
    end
    Server->>Supervisor: Status or probe for selected models
    Supervisor->>VLLM: Bounded cancellable request
    Supervisor-->>Server: Model result
    Server->>VLLM: Forward original chat bytes
    VLLM-->>Client: Stream completion
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant Server as haru-server
    participant Cache as Routing cache
    participant DB
    participant Supervisor
    participant VLLM

    Client->>Server: Authenticated chat request
    Server->>DB: Read route pointer
    Server->>Cache: Check revisioned snapshot
    alt Snapshot missing or stale
        Server->>DB: Coalesced snapshot load
        DB-->>Server: Validated snapshot
        Server->>Cache: Publish if generation is current
    end
    Server->>Supervisor: Status or probe for selected models
    Supervisor->>VLLM: Bounded cancellable request
    Supervisor-->>Server: Model result
    Server->>VLLM: Forward original chat bytes
    VLLM-->>Client: Stream completion
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
services/haru-server/src/app.ts:168-169
**Reference Generations Grow Without Bound**

Each distinct fleet reference is retained in `referenceVerdictGenerations`, including references that never resolve. An authenticated client can continuously submit unique references, causing this process-wide map to grow for the lifetime of the server and eventually increase memory use without limit.

Reviews (1): Last reviewed commit: "Harden fleet control and routing invaria..." | Re-trigger Greptile

Comment on lines 168 to +169
const forgottenGenerations = new Map<string, number>();
// Coalesce ONLY the expensive full-snapshot read. Pointer reads stay

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Reference Generations Grow Without Bound

Each distinct fleet reference is retained in referenceVerdictGenerations, including references that never resolve. An authenticated client can continuously submit unique references, causing this process-wide map to grow for the lifetime of the server and eventually increase memory use without limit.

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/haru-server/src/app.ts
Line: 168-169

Comment:
**Reference Generations Grow Without Bound**

Each distinct fleet reference is retained in `referenceVerdictGenerations`, including references that never resolve. An authenticated client can continuously submit unique references, causing this process-wide map to grow for the lifetime of the server and eventually increase memory use without limit.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 0f21d861df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 44 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/db/src/schema/fleet-topology.ts">

<violation number="1" location="packages/db/src/schema/fleet-topology.ts:85">
P2: A direct DB write can persist non-positive or fractional `probe.maxTokens` (and an empty prompt), then every snapshot read for that fleet fails protocol validation. Extend this persistence check/preflight to enforce the runtime lower bounds and integer/min-length invariants, not only the upper caps.</violation>
</file>

<file name="services/haru-server/src/reconciler/steps.ts">

<violation number="1" location="services/haru-server/src/reconciler/steps.ts:220">
P3: The `probe()` step function builds `expectedModels` using inline flatMap/filter/map over `domain.slots`, which duplicates the exact same logic in the newly extracted `configuredInferenceModelNames()` helper a few lines above. Both traverse slots, filter for `kind === "inference"`, and map to model names. The probe function additionally wraps the result in a `Set` (for `.has()`/`.difference()` operations later), but could reuse the helper for the extraction part.

Consider using `configuredInferenceModelNames(domain)` and converting to a Set: `const modelNames = configuredInferenceModelNames(domain); const expectedModels = new Set(modelNames);` This keeps the inference-model-name extraction in one place.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

// code points, matching JSON Schema maxLength and runtime parsing.
check(
"fleets_probe_policy_limits",
sql`(CASE WHEN jsonb_typeof(${table.policy} #> '{probe,prompt}') = 'string' THEN char_length(${table.policy} #>> '{probe,prompt}') <= ${MAX_PROBE_PROMPT_CODE_POINTS_SQL} ELSE TRUE END) AND (CASE WHEN jsonb_typeof(${table.policy} #> '{probe,maxTokens}') = 'number' THEN (${table.policy} #>> '{probe,maxTokens}')::numeric <= ${MAX_PROBE_TOKENS_SQL} ELSE TRUE END)`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A direct DB write can persist non-positive or fractional probe.maxTokens (and an empty prompt), then every snapshot read for that fleet fails protocol validation. Extend this persistence check/preflight to enforce the runtime lower bounds and integer/min-length invariants, not only the upper caps.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/src/schema/fleet-topology.ts, line 85:

<comment>A direct DB write can persist non-positive or fractional `probe.maxTokens` (and an empty prompt), then every snapshot read for that fleet fails protocol validation. Extend this persistence check/preflight to enforce the runtime lower bounds and integer/min-length invariants, not only the upper caps.</comment>

<file context>
@@ -0,0 +1,135 @@
+    // code points, matching JSON Schema maxLength and runtime parsing.
+    check(
+      "fleets_probe_policy_limits",
+      sql`(CASE WHEN jsonb_typeof(${table.policy} #> '{probe,prompt}') = 'string' THEN char_length(${table.policy} #>> '{probe,prompt}') <= ${MAX_PROBE_PROMPT_CODE_POINTS_SQL} ELSE TRUE END) AND (CASE WHEN jsonb_typeof(${table.policy} #> '{probe,maxTokens}') = 'number' THEN (${table.policy} #>> '{probe,maxTokens}')::numeric <= ${MAX_PROBE_TOKENS_SQL} ELSE TRUE END)`,
+    ),
+    // The routing revision only ever increments from its default of 1.
</file context>

domain: DomainSnapshot,
): string[] {
return domain.slots.flatMap((slot) =>
slot.spec.kind === "inference"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The probe() step function builds expectedModels using inline flatMap/filter/map over domain.slots, which duplicates the exact same logic in the newly extracted configuredInferenceModelNames() helper a few lines above. Both traverse slots, filter for kind === "inference", and map to model names. The probe function additionally wraps the result in a Set (for .has()/.difference() operations later), but could reuse the helper for the extraction part.

Consider using configuredInferenceModelNames(domain) and converting to a Set: const modelNames = configuredInferenceModelNames(domain); const expectedModels = new Set(modelNames); This keeps the inference-model-name extraction in one place.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/haru-server/src/reconciler/steps.ts, line 220:

<comment>The `probe()` step function builds `expectedModels` using inline flatMap/filter/map over `domain.slots`, which duplicates the exact same logic in the newly extracted `configuredInferenceModelNames()` helper a few lines above. Both traverse slots, filter for `kind === "inference"`, and map to model names. The probe function additionally wraps the result in a `Set` (for `.has()`/`.difference()` operations later), but could reuse the helper for the extraction part.

Consider using `configuredInferenceModelNames(domain)` and converting to a Set: `const modelNames = configuredInferenceModelNames(domain); const expectedModels = new Set(modelNames);` This keeps the inference-model-name extraction in one place.</comment>

<file context>
@@ -209,6 +209,20 @@ const hasNoTrainingSlots = (domain: DomainSnapshot): boolean =>
+  domain: DomainSnapshot,
+): string[] {
+  return domain.slots.flatMap((slot) =>
+    slot.spec.kind === "inference"
+      ? slot.spec.models.map((model) => model.name)
+      : [],
</file context>

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