Skip to content

feat(sessions): add session_id FK to link agent_sessions to session logs (Phase 1 of #1668)#1812

Open
OAGr wants to merge 3 commits intomainfrom
claude/session-id-fk-consolidation
Open

feat(sessions): add session_id FK to link agent_sessions to session logs (Phase 1 of #1668)#1812
OAGr wants to merge 3 commits intomainfrom
claude/session-id-fk-consolidation

Conversation

@OAGr
Copy link
Contributor

@OAGr OAGr commented Mar 6, 2026

Summary

Phase 1 of consolidating the 3 overlapping session tracking systems (issue #1668).

After investigation, there are actually 4 tables tracking agent activity:

  1. sessions — historical logs (written at session end, immutable)
  2. agent_sessions — live checklist tracking (mutable during session)
  3. active_agents — real-time coordination (heartbeat-based, ephemeral)
  4. agent_session_events — timeline for active_agents

The Agent Sessions dashboard (E912) currently joins agent_sessions and sessions by branch name — a fragile heuristic that can mismatch when a branch is reused, and loses data when branch names differ slightly.

Changes

Schema (apps/wiki-server/src/schema.ts):

  • Add session_id INTEGER REFERENCES sessions(id) to agent_sessions (nullable, ON DELETE SET NULL)
  • Index: idx_as_session_id

Migration (0063_add_session_id_to_agent_sessions.sql):

  • ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS session_id integer REFERENCES sessions(id) ON DELETE SET NULL

API (apps/wiki-server/src/api-types.ts, routes/agent-sessions.ts):

  • UpdateAgentSessionSchema accepts sessionId (nullable int)
  • PATCH endpoint handles sessionId updates
  • All agent session responses include sessionId

CLI (crux/wiki-server/sync-session.ts):

  • After syncing a session log, sets sessionId FK on the corresponding agent_session (matched by branch)
  • Best-effort, silently skips if agent session not found

Dashboard (apps/web/src/app/internal/agent-sessions/agent-sessions-content.tsx):

  • Prefers FK-linked session log over branch-name heuristic
  • Falls back to branch join for older records without session_id

Tests (apps/wiki-server/src/__tests__/agent-sessions.test.ts):

  • 4 new tests for sessionId PATCH operations (set, clear, reject non-integer, reject zero)

What this does NOT do

  • Does NOT drop any tables (data preserved)
  • Does NOT merge tables (different lifecycles, write patterns, unique keys)
  • Does NOT backfill historical records (future work)
  • Full table consolidation is a larger Phase 2+ effort

Why not full merge?

Full merge was considered and rejected because:

  • sessions uses (date, title) unique key; agent_sessions uses (branch, status) — incompatible
  • Many sessions records predate agent_sessions (no corresponding live tracking)
  • Different write patterns: sessions are immutable logs; agent_sessions are mutable live state
  • Would require migrating all consumers across sessions/agent_sessions simultaneously

The FK link achieves the main goal (eliminating fragile branch-name join) without the risk.

Test plan

  • 42 wiki-server tests pass (including 4 new sessionId tests)
  • TypeScript check clean (wiki-server + web)
  • Gate check passes
  • Drizzle journal updated with new migration entry

Partial: #1668

Summary by CodeRabbit

  • New Features

    • Added ability to link and associate agent sessions with sessions via a direct relationship.
    • API now supports setting and updating session associations for agent sessions.
    • Session synchronization automatically attempts to establish session links when available.
  • Documentation

    • Updated configuration documentation to clarify schema constraints and migration patterns.

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 697e979f-5d9a-4918-8471-da2caa45b013

📥 Commits

Reviewing files that changed from the base of the PR and between 5ce8f99 and e800b18.

📒 Files selected for processing (9)
  • .squawk.toml
  • apps/web/src/app/internal/agent-sessions/agent-sessions-content.tsx
  • apps/wiki-server/drizzle/0063_add_session_id_to_agent_sessions.sql
  • apps/wiki-server/drizzle/meta/_journal.json
  • apps/wiki-server/src/__tests__/agent-sessions.test.ts
  • apps/wiki-server/src/api-types.ts
  • apps/wiki-server/src/routes/agent-sessions.ts
  • apps/wiki-server/src/schema.ts
  • crux/wiki-server/sync-session.ts
📝 Walkthrough

Walkthrough

This PR establishes a foreign key relationship between agent_sessions and sessions tables by adding a nullable session_id column with index. It updates database schemas, API validation, data enrichment logic with a two-tier lookup mechanism (sessionId first, then branch fallback), and includes sync logic to populate the FK relationship when sessions are created.

Changes

Cohort / File(s) Summary
Configuration & Database Migration
.squawk.toml, apps/wiki-server/drizzle/0063_add_session_id_to_agent_sessions.sql, apps/wiki-server/drizzle/meta/_journal.json
Adds SQL migration for nullable session_id FK column with index and ON DELETE SET NULL, updates Squawk linting config to exclude adding-foreign-key-constraint rule.
Database Schema
apps/wiki-server/src/schema.ts
Defines sessionId column as foreign key referencing sessions.id with set-null cascade and creates idx_as_session_id index.
API Layer
apps/wiki-server/src/api-types.ts, apps/wiki-server/src/routes/agent-sessions.ts
Adds sessionId optional field to UpdateAgentSessionSchema, updates PATCH endpoint validation to require at least one field including sessionId.
Data Enrichment
apps/web/src/app/internal/agent-sessions/agent-sessions-content.tsx
Implements two-tier lookup (sessionId via FK first, then branch fallback), extracts model, cost, costCents, durationMinutes, and title from enriched session logs.
Sync Logic
crux/wiki-server/sync-session.ts
Adds best-effort FK linking post-session-creation, fetches agent_session by branch and updates sessionId to newly created session id with try/catch error handling.
Tests
apps/wiki-server/src/__tests__/agent-sessions.test.ts
Adds sessionId field initialization, validates PATCH updates, tests null clearing, validates positive integer constraint, and rejects zero/non-integer values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 A rabbit adds a key so neat,
Where sessions and agents finally meet!
Two paths to lookup, one primary, one spare,
Foreign keys dancing through the database air. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a session_id foreign key to link agent_sessions to session logs, which is the core feature of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/session-id-fk-consolidation

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

❤️ Share

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

@vercel
Copy link

vercel bot commented Mar 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
longterm-wiki-agent3 Building Building Preview, Comment Mar 7, 2026 2:10am

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

🛑 Protected Paths Modified

This PR modifies 1 protected file(s) that control agent behavior, CI pipelines, or validation logic. These changes require human review before merging.

Linter config (.squawk.toml)

  • .squawk.toml

Action required: Add the 0-rules-reviewed label after a human has reviewed the protected file changes.

Why this check exists: Agents have write access to their own behavioral rules. Without human review, a rule change buried in a large PR could weaken validation, bypass gates, or modify agent instructions. See #1405.

@OAGr
Copy link
Contributor Author

OAGr commented Mar 7, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

✅ Actions performed

Review triggered.

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
apps/wiki-server/src/routes/agent-sessions.ts (1)

141-148: ⚠️ Potential issue | 🟠 Major

Handle foreign-key constraint violation for invalid sessionId values.

The PATCH /api/agent-sessions/:id endpoint accepts a sessionId parameter (line 141) and writes it directly to the database without validating that the session exists. Since the schema enforces a foreign key constraint from agent_sessions.session_id to sessions.id, an invalid sessionId will trigger a database FK violation. Without explicit error handling, this will surface as a generic 500 error instead of a deterministic 4xx response. Either validate the target session before the update or catch and translate the FK violation into a stable 4xx response.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/wiki-server/src/routes/agent-sessions.ts` around lines 141 - 148, The
PATCH handler in agent-sessions.ts writes sessionId directly into agent_sessions
(see the sessionId check and the db.update call using getDrizzleDb() and
agentSessions) without ensuring the referenced session exists, which can cause a
DB foreign-key error; fix it by either (A) validating the sessionId before
updating: query the sessions table (sessions) for the given sessionId and return
a 404/400 if not found, then proceed with the update, or (B) wrap the
db.update(...).returning() in a try/catch, detect a foreign-key constraint
violation from the database error and translate it into a deterministic 4xx
response (400 or 404) instead of letting it bubble as a 500—apply the approach
that matches the codebase error-handling conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/internal/agent-sessions/agent-sessions-content.tsx`:
- Around line 64-66: The current assignment for `log` uses `logsByBranch` as a
fallback even when `s.sessionId` is present, which can attach another session’s
metadata; change the `log` resolution so that when `s.sessionId != null` you
only use `logsById.get(s.sessionId)` (even if undefined) and do not fall back to
`logsByBranch`, and only run the branch-name heuristic
(`logsByBranch.get(s.branch)`) when `s.sessionId` is null; update the `log`
variable assignment in agent-sessions-content (the line that references
`s.sessionId`, `logsById`, and `logsByBranch`) accordingly.

In `@apps/wiki-server/drizzle/0063_add_session_id_to_agent_sessions.sql`:
- Around line 18-20: The migration currently uses `IF NOT EXISTS` which masks
schema drift; update the statements in migration 0063 to remove `IF NOT EXISTS`
so the DDL fails loudly if a stray `session_id` column or `idx_as_session_id`
index already exists. Specifically, change the ALTER TABLE on "agent_sessions"
to add the "session_id" integer REFERENCES "sessions"("id") ON DELETE SET NULL
without `IF NOT EXISTS`, and create "idx_as_session_id" without `IF NOT EXISTS`,
so conflicts surface during migration application and preserve the intended FK
and ON DELETE behavior.

In `@apps/wiki-server/src/schema.ts`:
- Line 765: Change the sessionId column definition to match sessions.id by
replacing integer("session_id") with bigint("session_id", { mode: "number" }) in
the schema (preserve the .references(() => sessions.id, { onDelete: "set null"
}) part); also update the corresponding migration
(0063_add_session_id_to_agent_sessions.sql) to create the session_id column as
BIGINT instead of INTEGER so the FK types align.

In `@crux/wiki-server/sync-session.ts`:
- Around line 132-136: getAgentSessionByBranch() can return a non-unique branch
row and updateAgentSession(...) unconditionally clobbers sessionId; change the
client to skip any agent session rows that already have a sessionId (check
agentSessionResult.data.sessionId) before calling updateAgentSession, only PATCH
when sessionId is null/undefined, and add a TODO comment to implement a
server-side compare-and-set for sessionId to make this atomic later.
- Around line 121-143: Replace the direct createSession(...) call in main() with
a call to syncSessionFile(resolved) so the FK-linking logic in syncSessionFile
is executed; adjust syncSessionFile to return the created session result (or an
object with ok and sessionId) instead of only boolean (so callers can inspect
result.ok and result.sessionId), and update main() to check result.ok and log
the created session id (e.g. "✓ Session synced to wiki-server (id: ...)" ) when
successful; refer to functions syncSessionFile, createSession, and main to
locate the changes.

---

Outside diff comments:
In `@apps/wiki-server/src/routes/agent-sessions.ts`:
- Around line 141-148: The PATCH handler in agent-sessions.ts writes sessionId
directly into agent_sessions (see the sessionId check and the db.update call
using getDrizzleDb() and agentSessions) without ensuring the referenced session
exists, which can cause a DB foreign-key error; fix it by either (A) validating
the sessionId before updating: query the sessions table (sessions) for the given
sessionId and return a 404/400 if not found, then proceed with the update, or
(B) wrap the db.update(...).returning() in a try/catch, detect a foreign-key
constraint violation from the database error and translate it into a
deterministic 4xx response (400 or 404) instead of letting it bubble as a
500—apply the approach that matches the codebase error-handling conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7099bbdf-c374-4515-af5b-c63c2056597c

📥 Commits

Reviewing files that changed from the base of the PR and between b518699 and 5ce8f99.

📒 Files selected for processing (9)
  • .squawk.toml
  • apps/web/src/app/internal/agent-sessions/agent-sessions-content.tsx
  • apps/wiki-server/drizzle/0063_add_session_id_to_agent_sessions.sql
  • apps/wiki-server/drizzle/meta/_journal.json
  • apps/wiki-server/src/__tests__/agent-sessions.test.ts
  • apps/wiki-server/src/api-types.ts
  • apps/wiki-server/src/routes/agent-sessions.ts
  • apps/wiki-server/src/schema.ts
  • crux/wiki-server/sync-session.ts

@OAGr OAGr marked this pull request as ready for review March 7, 2026 01:51
@OAGr OAGr added claude-working Claude Code is actively working on this and removed claude-working Claude Code is actively working on this labels Mar 7, 2026
OAGr and others added 3 commits March 6, 2026 18:06
Phase 1 of consolidating overlapping session tracking systems (#1668).

The Agent Sessions dashboard (E912) joins data from `agent_sessions` and
`sessions` tables using a fragile branch-name heuristic — same branch name
could match across unrelated sessions, and cost/model data had to be
fetched separately.

- Add `session_id INTEGER REFERENCES sessions(id)` to `agent_sessions`
  table (nullable, ON DELETE SET NULL) with index `idx_as_session_id`
- Migration: `0063_add_session_id_to_agent_sessions.sql`

- `UpdateAgentSessionSchema` now accepts `sessionId` field (nullable int)
- PATCH `/api/agent-sessions/:id` handles `sessionId` updates
- `sessionId` is included in all agent session responses

- `crux wiki-server sync-session` now links the agent_session to the
  newly-created session log via FK after successful session sync
- Best-effort: silently skips if agent session not found

- Agent Sessions dashboard prefers FK-linked session log (`s.sessionId`)
  over branch-name heuristic; falls back to branch join for older records

- Dashboard enrichment is now based on direct FK join when available
- Eliminates ambiguous branch-name matching for newer sessions
- Older records continue to work via branch-name fallback

- Does not drop any tables (data preserved)
- Does not merge active_agents into agent_sessions
- Does not backfill historical records (too many unknowns)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drizzle's migrator runs all statements in a single transaction, making
the two-step NOT VALID / VALIDATE pattern (squawk's recommended fix for
adding-foreign-key-constraint) impossible.

Excluded the rule with the same justification as the existing exclusions
for require-concurrent-index-creation and constraint-missing-not-valid.
agent_sessions is a small table (~hundreds of rows) so the brief
SHARE ROW EXCLUSIVE lock during migration is acceptable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Resolve merge conflict with pagination changes (fetchAllPaginated)
- Fix type mismatch: session_id column integer→bigint to match sessions.id
- Fix dashboard fallback: don't fall back to branch heuristic when sessionId
  is set but not found (prevents attaching wrong session metadata)
- Fix CLI: main() now uses syncSessionFile() to include FK linking
- Add clobber guard: skip FK update if agent session already has sessionId
- Add FK constraint error handling in PATCH endpoint (400 vs 500)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@OAGr OAGr force-pushed the claude/session-id-fk-consolidation branch from 5ce8f99 to e800b18 Compare March 7, 2026 02:10
@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

🛑 CodeRabbit Security Gate

Action required: This PR has 2 unresolved Critical/Major CodeRabbit finding(s). Either resolve each thread in the GitHub review interface, or add the coderabbit-security-ok label after reviewing and acknowledging each finding.

Unresolved Critical/Major Findings (2)

  • crux/wiki-server/sync-session.ts (line 143): ⚠️ Potential issue | 🟠 Major

    ⚠️ Potential issue | 🟠 Major Route the CLI through syncSessionFile(). This helper now contains the FK-linking behavior, but main() still calls createSession(entry) directly on Line 178. In practice, pnpm crux wiki-server sync-session ... still only creates the session row and neve…

  • crux/wiki-server/sync-session.ts (line 136): ⚠️ Potential issue | 🟠 Major

    ⚠️ Potential issue | 🟠 Major Avoid clobbering an existing link based only on branch. getAgentSessionByBranch() returns the latest row for a branch, and the PATCH endpoint applies sessionId unconditionally. Since agent_sessions.branch is not unique, re-syncing an older YAML or reusin…


How to resolve: Mark each CodeRabbit thread as resolved in the "Files changed" tab, or add the coderabbit-security-ok label if the findings are false positives. See #1649.

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