Skip to content

Phase 0 — M0 close-out: seam fence (0.F), CI (0.G), docs (0.H), @relavium/db (0.I)#3

Merged
cemililik merged 4 commits into
mainfrom
development
Jun 4, 2026
Merged

Phase 0 — M0 close-out: seam fence (0.F), CI (0.G), docs (0.H), @relavium/db (0.I)#3
cemililik merged 4 commits into
mainfrom
development

Conversation

@cemililik

@cemililik cemililik commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Closes the M0 — Foundations green workstreams (0.F–0.I). Three commits, one per workstream — the multi-review hardening is folded into each workstream's commit (not a separate fix commit), so each commit is self-consistent and green.

Commits

Commit Workstream What it adds Review fixes folded in
3520718 0.I @relavium/db Drizzle schema for the 9 Phase-1 tables (faithful to database-schema.md), drizzle-kit migration set, better-sqlite3 client (WAL + foreign_keys=ON) + migration runner, 6-test smoke suite. ADR-0021 (driver) + ADR-0005 amendment + default export on @relavium/shared. bool defaults → integer 1/0; @types/node pinned to the 20.11 floor (so node:sqlite is a type error); inList quote-escaping; CHECK test now inserts a valid workflow first + asserts the constraint name (was masked by the FK error); run→step_executions→messages cascade test
7a855dc 0.F seam fence Two-rule @relavium/llm fence on TS + JS, lifted only inside packages/llm/src/adapters/*; quarantined fixture + pnpm lint:fence-check. config-name ignore narrowed (a *.config.ts source file no longer escapes); computed import(expr) banned; JS source now fenced; export … from forms added; assert-fence.mjs asserts exact per-rule counts + a config-named regression fixture
671beb6 0.G CI ci.yml: lint/typecheck/test/build + format + fence-check on push/PR, frozen lockfile, Turbo cache, strict-peer gate, reserved Phase-1 lanes. honest remote-cache framing; build-step rationale comment

0.H (docs) is folded into the commits above (ADR index, tech-stack driver pin, db README). Per the maintainer's rule, the roadmap "done" marks (current.md, phase doc) are deferred to post-merge — they are intentionally not flipped here.

Review trail

Three independent reviews ran against the branch. Review 1 probed the seam fence adversarially and found real bypasses (config-named files, computed dynamic imports, JS source) that Reviews 2 & 3 missed — all reproduced locally, then closed. The fence now covers 9 import syntaxes with self-checking fixtures so it can't silently regress. Three findings were refuted with reasons (roadmap-not-updated = intentional; CI commit Refs: = no ADR to ref; amendment heading = the > Amended blockquote is the documented pattern).

Validation

pnpm install --frozen-lockfilepnpm turbo run lint typecheck test build (8/8) → pnpm format:checkpnpm lint:fence-check (5/5 static, 4/4 syntax, config-named fenced) → strict-peer gate — all green. The node:sqlite floor guard verified (TS2307).

🤖 Generated with Claude Code

Summary by Sourcery

Finalize Phase 0 foundations by adding a typed SQLite persistence layer, enforcing a strict LLM provider SDK import fence, and wiring a CI workflow that gates pushes and PRs on lint, typecheck, tests, build, formatting, and fence integrity.

New Features:

  • Introduce the @relavium/db package with a Drizzle-based SQLite schema, migrations, and a better-sqlite3-backed client plus migration runner for Phase-1 run history tables.
  • Add a quarantined lint fixture harness and eslint rules that fence provider SDK imports to the @relavium/llm adapter boundary across both TS and JS sources.
  • Add a GitHub Actions CI workflow that runs lint, typecheck, tests, build, formatting checks, and the seam-fence verification on pushes and pull requests.

Enhancements:

  • Document the @relavium/db package, its conventions, scripts, and Node-side driver choice, and update ADR-0005, ADR index, and tech stack to reference ADR-0021 and the better-sqlite3 driver.
  • Export a default entry point from @relavium/shared to simplify consumer imports.
  • Refine eslint ignores so config files at repo/app roots are excluded without letting *.config.ts source files bypass the seam fence.
  • Pin @types/node to the Node runtime floor and add catalog entries for Drizzle, better-sqlite3, and their type packages to keep type-checking aligned with supported Node features.

CI:

  • Introduce a CI workflow that runs pnpm-based lint, typecheck, test, build, format checks, and the seam fence check with Turborepo caching, plus a strict peer-dependency gate job and reserved lanes for future provider conformance suites.

Documentation:

  • Add ADR-0021 to record better-sqlite3 as the Node-side SQLite driver for @relavium/db and link it into existing ADRs and the tech stack.
  • Replace the @relavium/db placeholder README with detailed documentation of its schema, conventions, scripts, and status.

Tests:

  • Add a Vitest smoke test that runs all Drizzle migrations against a fresh SQLite database, verifies key tables and indexes, and exercises FK cascades and CHECK constraints.
  • Add ESLint-based seam-fence fixtures and an assertion script that ensure all vendor-import syntaxes are enforced and that config-named source files remain covered by the fence.

Summary by CodeRabbit

  • New Features

    • Added a local SQLite database package with schema, migrations, client API, and smoke tests for Phase‑1.
  • New Features

    • Introduced a CI workflow that runs lint, typecheck, tests, format checks, and builds, plus a strict peer-dependency gate.
  • Documentation

    • Added ADRs and tech‑stack updates documenting the Node SQLite driver choice and integration notes.
  • Infrastructure

    • Tightened lint rules to block vendor SDK imports and added fixtures + a CI fence-check script; updated workspace/package configs.

cemililik and others added 3 commits June 4, 2026 19:45
…client

Stand up @relavium/db (Phase 0 · 0.I): the Drizzle SQLite schema for the nine
Phase-1 local tables (llm_providers, model_catalog, agents, workflows, runs,
step_executions, messages, run_events, run_costs), faithful to the canonical DDL —
TEXT UUIDs, JSON-as-TEXT, epoch-ms INTEGER timestamps, integer micro-cents,
CHECK-string enums, integer-bool (0/1) defaults, soft-delete partial indexes, and
ON DELETE CASCADE on the run children. The runs.status / runs.execution_mode CHECK
value sets are imported from @relavium/shared so the persisted enums cannot drift
from the contract.

Adds the drizzle-kit migration set, a better-sqlite3 client factory (WAL +
foreign_keys = ON) and migration runner, and a Vitest smoke test (6 tests) that
applies every migration to a fresh DB, round-trips a runs + run_events row, proves
the run -> step_executions -> messages and run -> run_events cascades, and asserts
the status/execution_mode CHECKs fire by constraint name (FK satisfied first, so
only the CHECK can reject).

The Node-side SQLite driver (ADR-0005 left it open) is pinned to better-sqlite3 in
ADR-0021. @types/node is pinned to the engines floor (20.11) so a Node-22-only API
is a type error, not a runtime break. Also broadens @relavium/shared's exports with
a `default` condition so CJS-condition tooling (drizzle-kit) resolves it.

Refs: ADR-0005, ADR-0021

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…0.F)

Wire the @relavium/llm seam fence before Phase 1's first adapter exists, applied to
BOTH TS and JS source: two complementary ESLint rules forbid every provider-SDK
(@anthropic-ai/sdk, openai, @google/genai) import outside packages/llm/src/adapters/*.
no-restricted-imports covers the static forms (bare, subpath, `import type`, and
`export … from` re-exports); no-restricted-syntax closes the rest — dynamic
`import()`, ANY non-literal computed `import(expr)`, the `import('…').T` type query,
and `require()`. The config-file ignore is scoped to repo/package/app-root tooling
configs so a source file named `*.config.ts` cannot escape the fence. The fence is
lifted only inside the adapter zone.

A quarantined fixture exercises all nine forms and assert-fence.mjs asserts each rule
family fires its EXACT count (plus a config-named source file stays fenced) — so a
partial regression fails the check rather than passing on the remaining errors.
Wired as `pnpm lint:fence-check`.

Refs: ADR-0011

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add ci.yml running `pnpm turbo run lint typecheck test` (+ build, format:check, and
the seam fence-check) on every push and PR with a frozen lockfile and the Turborepo
cache, plus a CI-only strict-peer-dependency gate. The GitHub Actions .turbo cache is
the always-on layer (no-change re-runs hit); the Vercel-style remote cache is opt-in
via secrets. Reserves the Phase-1 conformance and nightly live-API lanes as commented
anchors. The `ci` job is the required check; branch protection is set in repo settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements Phase 0 close-out for three workstreams: a strict ESLint-based LLM provider seam fence (TS+JS) with self-checking fixtures, a new @relavium/db package (Drizzle schema, migrations, better-sqlite3 client, smoke tests, docs, and ADR), and a CI workflow that runs lint/typecheck/test/build/format plus the seam-fence check and a strict peer-dependency gate.

Entity relationship diagram for @relavium/db Phase-1 schema

erDiagram
  llm_providers ||--o{ model_catalog : provides
  model_catalog ||--o{ agents : backs
  model_catalog ||--o{ run_costs : priced_by

  workflows ||--o{ runs : instance_of
  runs ||--o{ step_executions : has_steps
  runs ||--o{ run_events : has_events
  runs ||--o{ run_costs : has_costs

  step_executions ||--o{ messages : emits

  agents ||--o{ step_executions : used_by

  llm_providers {
    text id PK
    text name
  }
  model_catalog {
    text id PK
    text provider_id FK
    text model_id
  }
  workflows {
    text id PK
    text slug
  }
  runs {
    text id PK
    text workflow_id FK
    text status
    text execution_mode
  }
  step_executions {
    text id PK
    text run_id FK
    text agent_id FK
  }
  messages {
    text id PK
    text step_execution_id FK
    text run_id
  }
  run_events {
    text id PK
    text run_id FK
  }
  run_costs {
    text id PK
    text run_id FK
    text model_id FK
  }
  agents {
    text id PK
    text model_id FK
  }
Loading

Flow diagram for CI pipeline and seam-fence check

flowchart LR
  Dev[push / pull_request]
  CI[GitHub Actions CI workflow]
  Install[pnpm install --frozen-lockfile]
  Turbo[turbo run lint typecheck test]
  Build[pnpm turbo run build]
  Format[pnpm format:check]
  FenceCheck[pnpm lint:fence-check]
  ESLint[ESLint with seam rules]
  Fixtures[lint fixtures + assert-fence.mjs]
  PeerGate[peer-dep-gate job]

  Dev --> CI
  CI --> Install --> Turbo --> Build --> Format --> FenceCheck
  FenceCheck --> Fixtures --> ESLint
  CI --> PeerGate
  PeerGate --> Install
Loading

File-Level Changes

Change Details Files
Add an ESLint-based no-vendor-across-@relavium/llm seam fence with quarantined fixtures and a dedicated lint check.
  • Introduce shared seam rule configuration using no-restricted-imports and no-restricted-syntax to block all static and dynamic vendor SDK imports (including type-only, dynamic import, TS import-type queries, and require).
  • Apply the seam rules to both TS and JS sources, while lifting them only inside packages/llm/src/adapters and disabling type-aware linting for quarantined fixtures.
  • Tighten ESLint ignores so only root-level tooling configs are skipped, preventing *.config.ts source files from escaping the fence.
  • Add lint fixtures and an assert-fence script that lints them via ESLint and asserts exact rule hit counts plus coverage of a config-named source file.
  • Wire a package.json lint:fence-check script to run the fence assertion in CI.
eslint.config.mjs
tools/lint-fixtures/assert-fence.mjs
tools/lint-fixtures/forbidden-vendor-import.ts
tools/lint-fixtures/forbidden-in-name.config.ts
package.json
Introduce the @relavium/db package with Drizzle schema, migrations, a better-sqlite3 client/migration runner, tests, and documentation, plus a Node driver ADR and catalog updates.
  • Create @relavium/db package with exports, build/typecheck/lint/test/db:generate scripts, and catalog-based dependencies on drizzle-orm, drizzle-kit, better-sqlite3, @types/node, @types/better-sqlite3.
  • Implement a Drizzle SQLite schema covering the nine Phase-1 tables, including helpers for UUIDs, timestamps, booleans, JSON, enums with CHECK constraints sourced from @relavium/shared, and FK/index definitions faithful to database-schema.md.
  • Add a drizzle-kit config and initial migration SQL snapshot plus metadata, ensuring schema and migrations stay in sync.
  • Implement a better-sqlite3-based client factory that applies WAL and foreign_keys pragmas, exposes a schema-bound Drizzle Db, and a runMigrations helper that locates the drizzle folder relative to the built module.
  • Add a Vitest smoke test that runs migrations against a real temp DB, asserts PRAGMAs, validates table/index presence, exercises FK cascades (runs → step_executions → messages and run_events), and proves CHECK constraints for runs.status/execution_mode fire with the expected constraint names.
  • Document @relavium/db in its README (status, conventions, scripts), update ADR-0005 with an amendment referencing the Node driver, introduce ADR-0021 selecting better-sqlite3 for Node-side SQLite, and reflect the driver choice in the tech stack and ADR index.
  • Export a default entry point from @relavium/shared to satisfy default import expectations from @relavium/db.
packages/db/package.json
packages/db/src/schema.ts
packages/db/src/client.ts
packages/db/src/client.test.ts
packages/db/src/index.ts
packages/db/drizzle/0000_cuddly_war_machine.sql
packages/db/drizzle.config.ts
packages/db/tsconfig.json
packages/db/tsconfig.build.json
packages/db/drizzle/meta/0000_snapshot.json
packages/db/drizzle/meta/_journal.json
packages/db/README.md
pnpm-workspace.yaml
docs/decisions/0021-node-sqlite-driver-better-sqlite3.md
docs/decisions/0005-sqlite-drizzle-local-postgres-cloud.md
docs/tech-stack.md
docs/decisions/README.md
packages/shared/package.json
Add a CI workflow that runs the full monorepo pipeline and a strict peer-dependency gate, aligning with Phase 0 CI requirements.
  • Create a GitHub Actions CI workflow that triggers on pushes to main/development and PRs, sets up pnpm+Node from .nvmrc, installs with a frozen lockfile, restores/saves the Turbo cache (with optional remote cache via TURBO_TOKEN/TURBO_TEAM), then runs turbo lint/typecheck/test, followed by turbo build, format:check, and lint:fence-check.
  • Introduce a separate peer-dep-gate job that installs with --config.strict-peer-dependencies=true to detect peer drift without blocking local installs.
  • Reserve commented Phase-1 lanes for provider conformance tests and nightly live-API runs, documenting how they will be wired once adapters land.
.github/workflows/ci.yml
package.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31cfddfa-0745-4f5e-a96d-78fd7d9b8783

📥 Commits

Reviewing files that changed from the base of the PR and between 671beb6 and 6492ad4.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • packages/db/README.md
  • packages/db/src/schema.ts
  • tools/lint-fixtures/assert-fence.mjs
✅ Files skipped from review due to trivial changes (1)
  • packages/db/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • tools/lint-fixtures/assert-fence.mjs
  • .github/workflows/ci.yml
  • packages/db/src/schema.ts

📝 Walkthrough

Walkthrough

Adds a Phase‑1 SQLite persistence package (Drizzle schema, migrations, better‑sqlite3 client, tests), an ESLint seam fence with fixtures and validation, workspace/tooling updates, and a GitHub Actions CI workflow that runs lint/typecheck/test/build and enforces the seam fence.

Changes

SQLite Database Layer & Seam Fence Enforcement

Layer / File(s) Summary
Architecture Decisions & Tech Stack
docs/decisions/0021-node-sqlite-driver-better-sqlite3.md, docs/decisions/README.md, docs/decisions/0005-sqlite-drizzle-local-postgres-cloud.md, docs/tech-stack.md
ADR-0021 records choosing better-sqlite3 for Node consumers; ADR-0005 amended and tech-stack updated.
Database Schema and SQL Migrations
packages/db/drizzle.config.ts, packages/db/src/schema.ts, packages/db/drizzle/0000_cuddly_war_machine.sql, packages/db/drizzle/meta/0000_snapshot.json, packages/db/drizzle/meta/_journal.json
Drizzle schema and generated SQL migration create nine Phase‑1 tables with CHECKs, FKs, partial indexes, snapshot and journal metadata.
Database Client Library and Package Setup
packages/db/package.json, packages/db/tsconfig.json, packages/db/tsconfig.build.json, packages/db/src/client.ts, packages/db/src/index.ts, packages/db/README.md
New @relavium/db package: ESM exports, TS configs, createClient (better-sqlite3 + WAL/foreign_keys) and runMigrations, and re-exports of schema/types.
Database Integration Tests
packages/db/src/client.test.ts
Vitest smoke tests run migrations against a temp DB and assert pragmas, table/index presence, round-trip CRUD, cascading deletes, and CHECK constraint enforcement.
Lint Seam Fence for Vendor SDK Isolation
eslint.config.mjs, package.json, tools/lint-fixtures/assert-fence.mjs, tools/lint-fixtures/forbidden-vendor-import.ts, tools/lint-fixtures/forbidden-in-name.config.ts
Seam fence implemented with no-restricted-imports/no-restricted-syntax across TS/JS; adapters zone disables fence; fixtures + assert script verify expected ESLint rule hits.
GitHub Actions CI Pipeline
.github/workflows/ci.yml
Workflow runs on pushes/PRs, installs with frozen-lockfile, restores Turbo cache, runs turborepo lint/typecheck/test/build, format and seam-fence checks; includes a peer-dep-gate job.
Workspace Dependencies and Tooling
pnpm-workspace.yaml, .prettierignore, packages/shared/package.json
Catalog pins types/tools and adds drizzle-orm/better-sqlite3; Prettier ignores **/drizzle/**; shared package export map adds default entry.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • HodeTech/Relavium#1: Introduces the foundational ESLint flat config and LLM adapter infrastructure; this PR extends that config with seam fence rules and fixture validation.

Poem

🐰 I tunneled through schema, dug a migration nest,
Better‑SQLite cradles each run and test,
Fences of lint guard the vendor gate,
CI hums nightly to validate fate,
Hops of joy — code compiled and blessed!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main changes: completing Phase 0 workstreams 0.F–0.I covering seam fence, CI, docs, and the @relavium/db package with specific ordinal references.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ 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 development

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

@sourcery-ai sourcery-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.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tools/lint-fixtures/assert-fence.mjs" line_range="27-28" />
<code_context>
+
+const eslint = new ESLint();
+const results = await eslint.lintFiles([MAIN, CONFIG_NAMED]);
+const resultFor = (suffix) => results.find((r) => r.filePath.endsWith(suffix));
+const seamErrors = (res, ruleId) =>
+  (res?.messages ?? []).filter((m) => m.ruleId === ruleId && m.severity === 2).length;
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Path-suffix matching can fail on Windows due to hard-coded '/' separators.

Using `r.filePath.endsWith('/…')` assumes POSIX separators; on Windows it will be `'\'`, so `resultFor('/forbidden-vendor-import.ts')` and `resultFor('/forbidden-in-name.config.ts')` may return `undefined`, breaking the regression detection. Please normalize paths (e.g. `path.normalize` + normalized suffix) or use `path.basename` for filename-only checks so this works cross‑platform.
</issue_to_address>

### Comment 2
<location path="packages/db/README.md" line_range="21" />
<code_context>
+  `better-sqlite3` ([ADR-0021](../../docs/decisions/0021-node-sqlite-driver-better-sqlite3.md)).
+- A **smoke test** that applies every migration to a fresh DB and round-trips a row.
+
+It depends only on `@relavium/shared` (the contract enums its CHECKs reuse) and Drizzle.
+
+## Conventions (from the canonical DDL)
</code_context>
<issue_to_address>
**suggestion (typo):** Clarify wording in parenthetical about contract enums and CHECKs.

The phrase "the contract enums its CHECKs reuse" is ungrammatical. Please change it to something like "the contract enums that its CHECKs reuse" for clarity.

```suggestion
It depends only on `@relavium/shared` (the contract enums that its CHECKs reuse) and Drizzle.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tools/lint-fixtures/assert-fence.mjs Outdated
Comment thread packages/db/README.md Outdated

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request scaffolds the @relavium/db package, introducing a Drizzle SQLite schema for Phase-1 tables, a better-sqlite3 client factory, and migration smoke tests, aligned with ADR-0021. It also implements an airtight ESLint import fence to prevent provider SDKs from crossing the @relavium/llm seam outside of designated adapters, backed by quarantined lint fixtures and an assertion script. The review feedback suggests simplifying the boolean default value helper in the schema by passing the boolean directly to Drizzle, and recommends adding a CHECK constraint on the messages.role column to prevent invalid roles.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/db/src/schema.ts
Comment on lines +49 to +52
const boolFlag = (name: string, def: boolean) =>
integer(name, { mode: 'boolean' })
.notNull()
.default(def ? sql`1` : sql`0`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Since mode: 'boolean' is configured on the integer column, Drizzle ORM's .default() method natively accepts a boolean value (which it automatically serializes to 1 or 0 for SQLite). Passing raw SQL sql'1' or sql'0' is redundant and bypasses TypeScript's type safety for the default value. We can simplify this by passing the boolean def directly.

Suggested change
const boolFlag = (name: string, def: boolean) =>
integer(name, { mode: 'boolean' })
.notNull()
.default(def ? sql`1` : sql`0`);
const boolFlag = (name: string, def: boolean) =>
integer(name, { mode: 'boolean' })
.notNull()
.default(def);

Comment thread packages/db/src/schema.ts
Comment on lines +291 to +306
// role values (system|user|assistant|tool) are documented literals; the reference
// DDL declares no CHECK on role, so none is added here.
role: text('role').notNull(),
content: text('content'),
contentParts: jsonText('content_parts'),
toolCalls: jsonText('tool_calls'),
toolCallId: text('tool_call_id'),
name: text('name'),
finishReason: text('finish_reason'),
createdAt: epochMs('created_at').notNull(),
},
(t) => [
index('idx_messages_step').on(t.stepExecutionId, t.sequenceNumber),
index('idx_messages_run').on(t.runId, t.createdAt),
],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To prevent database corruption and ensure defensive programming, it is highly recommended to enforce a CHECK constraint on the role column of the messages table, restricting it to the allowed values ('system', 'user', 'assistant', 'tool'). This aligns with the check constraints defined on other enum-like columns in the schema (such as runs.status and step_executions.status).

Suggested change
// role values (system|user|assistant|tool) are documented literals; the reference
// DDL declares no CHECK on role, so none is added here.
role: text('role').notNull(),
content: text('content'),
contentParts: jsonText('content_parts'),
toolCalls: jsonText('tool_calls'),
toolCallId: text('tool_call_id'),
name: text('name'),
finishReason: text('finish_reason'),
createdAt: epochMs('created_at').notNull(),
},
(t) => [
index('idx_messages_step').on(t.stepExecutionId, t.sequenceNumber),
index('idx_messages_run').on(t.runId, t.createdAt),
],
);
// role values (system|user|assistant|tool) are documented literals; the reference
// DDL declares no CHECK on role, so none is added here.
role: text('role').notNull(),
content: text('content'),
contentParts: jsonText('content_parts'),
toolCalls: jsonText('tool_calls'),
toolCallId: text('tool_call_id'),
name: text('name'),
finishReason: text('finish_reason'),
createdAt: epochMs('created_at').notNull(),
},
(t) => [
check('messages_role_check', sql`${t.role} in (${inList(['system', 'user', 'assistant', 'tool'])})`),
index('idx_messages_step').on(t.stepExecutionId, t.sequenceNumber),
index('idx_messages_run').on(t.runId, t.createdAt),
],
);

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

🧹 Nitpick comments (1)
packages/db/src/schema.ts (1)

57-58: 💤 Low value

Consider using replaceAll() for clarity.

The global regex works correctly, but replaceAll() (ES2021) is slightly more readable. Since Node ≥ 20 is required per ADR-0021, this is safe.

Suggested change
 const inList = (values: readonly string[]) =>
-  sql.raw(values.map((v) => `'${v.replace(/'/g, "''")}'`).join(', '));
+  sql.raw(values.map((v) => `'${v.replaceAll("'", "''")}'`).join(', '));
🤖 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/src/schema.ts` around lines 57 - 58, The inList helper builds a
raw SQL list by replacing single quotes via a regex; change the replacement to
use String.prototype.replaceAll for clarity by updating the implementation of
inList (the function that calls sql.raw) to use v.replaceAll("'", "''") instead
of v.replace(/'/g, "''"), preserving the existing behavior and keeping the
values: readonly string[] handling and the sql.raw call intact.
🤖 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 @.github/workflows/ci.yml:
- Line 39: Replace floating tags with commit-pinned SHAs for GitHub Actions used
(e.g., actions/checkout, pnpm/action-setup, actions/setup-node, actions/cache)
and add the checkout option to disable credential persistence: for every
checkout step (including the peer-dep-gate job) change uses: actions/checkout@v4
to the corresponding commit SHA (actions/checkout@<sha>) and add with:
persist-credentials: false; apply the same commit-pinning approach to
pnpm/action-setup, actions/setup-node and actions/cache usages so all action
references use exact SHAs instead of tags.

---

Nitpick comments:
In `@packages/db/src/schema.ts`:
- Around line 57-58: The inList helper builds a raw SQL list by replacing single
quotes via a regex; change the replacement to use String.prototype.replaceAll
for clarity by updating the implementation of inList (the function that calls
sql.raw) to use v.replaceAll("'", "''") instead of v.replace(/'/g, "''"),
preserving the existing behavior and keeping the values: readonly string[]
handling and the sql.raw call intact.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18b6cf35-f8df-41f6-9788-bfa2bd109606

📥 Commits

Reviewing files that changed from the base of the PR and between c2780b9 and 671beb6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • .github/workflows/ci.yml
  • .prettierignore
  • docs/decisions/0005-sqlite-drizzle-local-postgres-cloud.md
  • docs/decisions/0021-node-sqlite-driver-better-sqlite3.md
  • docs/decisions/README.md
  • docs/tech-stack.md
  • eslint.config.mjs
  • package.json
  • packages/db/README.md
  • packages/db/drizzle.config.ts
  • packages/db/drizzle/0000_cuddly_war_machine.sql
  • packages/db/drizzle/meta/0000_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/package.json
  • packages/db/src/client.test.ts
  • packages/db/src/client.ts
  • packages/db/src/index.ts
  • packages/db/src/schema.ts
  • packages/db/tsconfig.build.json
  • packages/db/tsconfig.json
  • packages/shared/package.json
  • pnpm-workspace.yaml
  • tools/lint-fixtures/assert-fence.mjs
  • tools/lint-fixtures/forbidden-in-name.config.ts
  • tools/lint-fixtures/forbidden-vendor-import.ts

Comment thread .github/workflows/ci.yml Outdated
…e fence-check

- ci: pin every third-party action to a full commit SHA (with a `# vX.Y.Z` tracking
  comment) and set `persist-credentials: false` on checkout — closes the SonarCloud
  security hotspot, so a moved tag can no longer inject unreviewed code.
- tools/lint-fixtures/assert-fence.mjs: match ESLint results by `path.basename`
  instead of an `endsWith('/…')` suffix, so the fence-check works on Windows (`\`).
- packages/db: `inList` uses `String.prototype.replaceAll` for clarity (output is
  identical — verified no migration drift); fix a README grammar nit.

Skipped, with reason:
- boolean defaults keep `sql`1`/`sql`0``: drizzle's `.default(true)` emits
  `DEFAULT true` in the DDL, which diverges from the canonical `DEFAULT 1/0`
  (database-schema.md). The explicit literal is intentional for byte-fidelity, and
  the `boolFlag(name, def: boolean)` wrapper keeps the call site type-safe.
- no CHECK added on `messages.role`: the canonical DDL declares none; adding one
  here would drift from the single-canonical-home reference (it belongs in
  database-schema.md first, as a deliberate contract change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jun 4, 2026

Copy link
Copy Markdown

@cemililik
cemililik merged commit b08f738 into main Jun 4, 2026
7 checks passed
cemililik added a commit that referenced this pull request Jun 4, 2026
…ings

Phase 0 — Foundations is done: all workstreams 0.A–0.I merged (PR #1#3), achieving
milestone M0. Reflect that across the roadmap and entry docs:
- phase-0-foundations.md: status -> Complete; 0.F–0.I marked done; the 0.M3/M0 milestone
  done; exit criterion #7 lists better-sqlite3 (ADR-0021).
- current.md: Where-we-are / What-is-active / next-steps now point at Phase 1
  (@relavium/llm seam + @relavium/core engine); M1 is the next checkpoint.
- roadmap/README.md milestone spine: M0 done; root README + CLAUDE.md status updated.

Adds docs/roadmap/deferred-tasks.md: every confirmed-but-deferred finding from the
97-agent comprehensive review, as discrete checkable tasks (decisions, schema/test depth,
tooling, docs, packaging) so none get lost. None blocked M0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Jun 19, 2026
…gress-test/doc-drift)

13-dimension, double-verified end-to-end review of 1.AF (engine media plumbing).
This lands the two confirmed HIGH items, the behavioral mediums, and the doc-drift
cluster; the 1.AH-latent host-wiring items stay recorded in deferred-tasks.md.

H1 (I3): redact inline media from agent:tool_call.toolInput before it rides the
event/IPC/log stream. A model can emit a base64 data: URI or a {kind:'base64',data}
object as a tool argument; the result side (outputSummary) was redacted this
workstream but the symmetric input side was not. Reuse the same redactInlineMedia
walk at sanitizeInput — display-only (the dispatch already ran on the real args).

H2: unify the load-time output_modalities check (validate-catalog) and the runtime
FallbackChain pre-skip (capabilities) on ONE exact-membership predicate
(isOutputCombinationSupported, @relavium/llm). The runtime did text-strip + subset,
which wrongly ADMITTED wire-invalid combinations (the closed set exists to reject)
and diverged from the load-check + ADR-0031 decision #3. Both now share the
predicate; text-only stays always-emittable (no regression for []-combo models).

GC grace (db): bump media_objects.last_referenced_at on addReference AND the
terminal sweep (removeRunReferences), so the grace window measures from
drop-to-zero, not production time (ADR-0042 section 4). A long-lived handle losing
its last reference no longer gets zero effective grace.

Egress test (db): mock node:https to exercise the concrete nodeMediaEgressDeps
(the un-injectable pin/SNI/port/family wiring) + the default-deps range-block — the
one part of the SSRF-critical path with no executable proof (E43-7).

Docs: sse-event-schema (cost:updated.mediaUnits deferred — the layering forbids
shared importing the llm seam type), tool-registry (thirteen built-ins; add
media_scope_denied to the deny union), built-in-tools (read_media row); record the
keychain no-raw-key IPC test + the mediaUnits-surface deferrals in deferred-tasks.md.

Validation: lint/typecheck/test/build 16/16 green, prettier clean, Leakwatch 0.
Refs: ADR-0042, ADR-0043, ADR-0044

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Jun 24, 2026
…xt pickup 2.I

2.G (interactive human-gate prompt + `relavium gate` cross-process resume) merged via PR
#47, fully closing 2.K's deferred gate-resume half. Flip the status surfaces:

- phase-2-cli.md: §2.G heading → Done; status header + Remaining-build-order status line →
  add 2.G; the build-order table drops the 2.G row and renumbers (2.I now #1, 2.L #2, 2.S
  #3, …); the gate-closing backbone shrinks to `2.I → 2.L`; the §2.K deferred gate-resume
  note → landed.
- current.md: 2.G added to Landed; next pickup → 2.I.
- CLAUDE.md + README.md: 2.G landed; next pickup 2.I.

The structural views (dependency matrix, ordered waves, Mermaid graphs) are the
from-scratch plan, not a live tracker, so they keep 2.G as a graph node unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Jul 2, 2026
… nitpick)

Verify-each-against-current-code pass; all 6 were still valid, fixed minimally.

- agent-run.test.ts: replace an unsafe inline `as { error?: { code?: string } }`
  cast with a local `isRecord` type guard (strict-TS).
- chat.ts: `onAbort` handler → block body so it never forwards `session.abort()`'s
  return value (void-style).
- chat-ink.tsx: the ChatApp 'abort' case now mirrors home-controller — when
  `onAbort` is absent (made OPTIONAL; the no-op default dropped), Esc rejects a
  PENDING approval directly instead of a no-op that would hang the decision. The
  prop is `(() => void) | undefined` so the createElement passthrough forwards an
  absent `ctx.onAbort` under exactOptionalPropertyTypes.
- persister.ts: derive the session title inside the COMPLETED-exchange branch
  (from `pendingUserText`), not in `beginUserTurn` — so an aborted/errored first
  turn (whose rows are rolled back) never labels the row with a phantom title.
- fs.ts: `listOne` recursive walk now checks `isSensitiveReadPath` on the entry's
  REALPATH (falling back to lexical on a broken link), mirroring collectGlobFiles
  — a symlink named innocuously but pointing into a .ssh/.relavium/credential
  store is no longer surfaced in a listing. Displayed `rel` name unchanged.
- docs/roadmap/current.md: bump the stale "Last updated" stamp 2026-06-29 →
  2026-06-30 (aligns with the newest body entry).

Tests: persister aborted-title pin; fs symlink-to-sensitive-store list-skip pin
(itPosix); agent-run guard is itself the fix. #3 relies on the tested
home-controller parity (ChatApp has no unit harness). Full turbo typecheck/test/
lint green + fence/engine-deps/bundle-closure + Leakwatch clean; changed files
prettier-clean.

Refs: ADR-0057
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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