Skip to content

agent-trace: migrate diff capture to message.updated and add model attribution#42

Open
ivke995 wants to merge 8 commits into
mainfrom
fix/use-message-updated
Open

agent-trace: migrate diff capture to message.updated and add model attribution#42
ivke995 wants to merge 8 commits into
mainfrom
fix/use-message-updated

Conversation

@ivke995
Copy link
Copy Markdown
Contributor

@ivke995 ivke995 commented May 15, 2026

Summary by CodeRabbit

  • New Features

    • Agent trace system now captures and stores the AI model identifier for each code change, enabling better tracking of which model generated specific code modifications.
  • Chores

    • Updated plugin dependency version.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

📝 Walkthrough

Walkthrough

This PR extends the agent trace pipeline to capture and track model provenance (model_id) through diff patches. It updates the event source from session.diff to message.updated, refactors payload extraction across four plugin implementations to read from nested message metadata, adds model_id to database storage and patch intersection logic, and attributes captured model IDs to trace contributors.

Changes

Model ID Provenance Through Agent Trace Pipeline

Layer / File(s) Summary
Patch hunk provenance model and intersection logic
cli/src/services/patch.rs
PatchHunk gains optional model_id field. intersect_patches and combine_patches updated to track and propagate model_id from matched hunks, with new AvailableTouchedLine helper. Parsing initializes model_id to None.
Database schema and diff trace persistence
cli/migrations/agent-trace/005_add_diff_traces_model_id.sql, cli/src/services/agent_trace_db/mod.rs
Migration adds model_id TEXT column to diff_traces. Database queries (INSERT/SELECT) updated. DiffTraceInsert and DiffTracePatchRow extended with model_id. Recent patch parsing propagates model_id into hunks. Tests updated.
Hook input contract and trace building
cli/src/services/hooks/mod.rs, cli/src/services/agent_trace.rs
DiffTracePayload gains model_id field. Hook parser validates and extracts non-empty model_id from JSON. Contributor struct gains optional model_id. Trace building matches model_id from intersection patches for Ai/Mixed classifications. Error message updated.
Plugin implementations capturing model_id from events
.opencode/plugins/sce-agent-trace.ts, config/.opencode/plugins/sce-agent-trace.ts, config/automated/.opencode/plugins/sce-agent-trace.ts, config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts, config/lib/agent-trace-plugin/package.json
Event source changed from session.diff to message.updated. Payload extraction refactored to read from event.properties.info with user role filtering. Diffs sourced from info.summary.diffs. Patch aggregation simplified to unconditional appending. model_id added from model.providerID/modelID. Plugin dependency bumped to 1.14.28.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • crocoder-dev/shared-context-engineering#36: Both PRs modify the agent trace DB/hook pipeline around "recent diff trace patches" processing—this PR adds model_id into the diff-trace payload/DB schema and propagates it through patch intersection, which directly impacts the post-commit hook flow introduced in the retrieved PR.
🚥 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 clearly and specifically summarizes the main changes: migrating diff capture from session.diff to message.updated events and adding model_id attribution throughout the codebase.
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 fix/use-message-updated

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.

@stefanskoricdev stefanskoricdev force-pushed the fix/use-message-updated branch from 0d08c52 to 25153cf Compare May 18, 2026 13:10
ivke995 and others added 7 commits May 18, 2026 16:42
….updated

The agent-trace plugin previously captured session.diff events, but the
upstream OpenCode API no longer emits this event reliably. Switch to
message.updated events, which carry the same diff information per user
message.

Key changes:
- Listen for message.updated instead of session.diff
- Extract diffs from properties.info.summary?.diffs[].patch instead of
  properties.diff[]
- Filter to user messages only (info.role === user)
- Fall back sessionID to unknown when info.sessionID is absent
- Use patch-only extraction (no diff field fallback needed for FileDiff)
- Bump @opencode-ai/plugin from 1.3.0 to 1.14.28 for message.updated support

Generated plugin outputs regenerated via Pkl; context documentation updated
to reflect the new event contract.

Plan: agent-trace-plugin-message-updated (T01, T02, T03)

Co-authored-by: SCE <sce@crocoder.dev>
Capture the OpenCode model identifier in emitted diff-trace payloads and add the nullable diff_traces.model_id migration so later Rust hook wiring can persist it without changing recent-patch reads.

Plan: add-diff-traces-model-id

Updated tasks: T01, T02

Co-authored-by: SCE <sce@crocoder.dev>
Register the model_id migration and extend DiffTraceInsert plus the diff_traces INSERT statement so AgentTraceDb writes nullable model identifiers with captured diff traces.

This is the DB-layer slice; hook payload parsing remains in the next task.

Plan: add-diff-traces-model-id

Task: T03

Co-authored-by: SCE <sce@crocoder.dev>
Parse the OpenCode-provided model_id from diff-trace payloads and pass it through to AgentTraceDb inserts so stored diff_traces retain model attribution.

Plan: add-diff-traces-model-id

Task: T04

Co-authored-by: SCE <sce@crocoder.dev>
…plan

- Mark T05 (Validation and cleanup) as done with evidence

- Add execution record: pkl-check-generated passed, nix flake check passed

- Append validation report confirming all 6 success criteria met

- Clean stale context/tmp/ artifacts (157 files removed)

- Confirm no new test files remain from this plan

Plan: add-diff-traces-model-id

Task: T05

Co-authored-by: SCE <sce@crocoder.dev>
…ributor output

Carry nullable diff_traces.model_id through recent patch loading into PatchHunk provenance, preserve it across patch combine/intersect operations, and emit contributor.model_id for ai/mixed conversations when available.

This keeps attribution metadata intact from stored diff traces to generated Agent Trace payloads while preserving deterministic patch shaping and serialization behavior.

Co-authored-by: SCE <sce@crocoder.dev>
@ivke995 ivke995 force-pushed the fix/use-message-updated branch from 7853cc8 to 37e5071 Compare May 18, 2026 14:47
Update Agent Trace docs to reflect that the minimal builder is consumed by the active post-commit hook flow, with contributor model provenance and validated AgentTraceDb persistence documented consistently.

Co-authored-by: SCE <sce@crocoder.dev>
@ivke995 ivke995 changed the title agent-trace-plugin: Switch agent-trace plugin from session.diff to message.updated event capture agent-trace: migrate diff capture to message.updated and add model attribution May 18, 2026
@ivke995 ivke995 marked this pull request as ready for review May 18, 2026 16:04
@ivke995 ivke995 requested a review from davidabram May 18, 2026 16:05
Copy link
Copy Markdown

@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 (2)
.opencode/plugins/sce-agent-trace.ts (1)

73-81: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Empty patches are collected, potentially producing empty diff content.

The loop pushes entry.patch || "" unconditionally, so entries with empty or missing patch fields contribute empty strings. If all diff entries have empty patches, the final diff field will be an empty string (or just newlines), which will fail the backend's required_non_empty_string_field validation for "diff".

Consider filtering out empty patches before pushing:

🛡️ Proposed fix
     const entryObj = entry as {patch?:string};
     const patch = entryObj.patch || "";
-  
-    patches.push(patch);  
+    if (patch.trim().length > 0) {
+      patches.push(patch);
+    }
   }
🤖 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 @.opencode/plugins/sce-agent-trace.ts around lines 73 - 81, The loop is
pushing entry.patch || "" into the patches array which allows empty strings to
be collected and yields an empty "diff" later; change the logic where entryObj
and patch are derived (the entryObj = entry as {patch?:string} / const patch =
entryObj.patch || "") to compute a trimmedPatch (e.g., entryObj.patch?.trim())
and only push into patches when trimmedPatch is a non-empty string, leaving the
existing check (if (patches.length === 0) return undefined) intact.
config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts (1)

55-88: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing null check on model before property access.

Line 87 accesses model.providerID and model.modelID without verifying that model is defined. If infoObj.model is undefined or null, this will throw a runtime exception.

🐛 Proposed fix: Add null guard for model
   const model = infoObj.model;
+  if (typeof model !== "object" || model === null) {
+    return undefined;
+  }

   // Access info.summary?.diffs via explicit checks
   const summary = infoObj.summary;

Alternatively, if you want to allow traces without model attribution:

+  const modelId =
+    typeof model === "object" &&
+    model !== null &&
+    typeof model.providerID === "string" &&
+    typeof model.modelID === "string"
+      ? `${model.providerID}/${model.modelID}`
+      : "unknown";
+
   return {
     sessionID,
     diff: patches.join("\n"),
     time: Date.now(),
-    model_id: `${model.providerID}/${model.modelID}`,
+    model_id: modelId,
   };
🤖 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 `@config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts` around
lines 55 - 88, The code reads infoObj.model into model then unconditionally
accesses model.providerID/model.modelID when returning the trace object, which
can throw if model is null/undefined; update the logic in this function to guard
against a missing model (infoObj.model) before using its properties — either
return undefined or set a safe default for model_id when model is falsy, e.g.
check model !== null && model !== undefined (or use a short-circuit) before
constructing model_id, and ensure sessionID/diff/time logic remains unchanged so
the function returns undefined or a trace with a defensive model_id instead of
throwing.
🤖 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 @.opencode/plugins/sce-agent-trace.ts:
- Line 55: The code reads const model = infoObj.model without validating it but
later accesses model.providerID and model.modelID; add a null/undefined guard
before any property access (e.g., check that model is truthy and has
providerID/modelID) and handle the missing case (return early, throw a
descriptive error, or use safe defaults) in the function containing this logic
so accesses like model.providerID and model.modelID are only executed when model
is valid.

In `@config/.opencode/plugins/sce-agent-trace.ts`:
- Around line 73-77: The loop is currently pushing empty patch strings into
patches (using entryObj, patch, patches); update the logic to only push
non-empty patches by checking something like if (patch && patch.trim() !== "")
before calling patches.push(patch) so empty/whitespace-only patch values are
filtered out and won't produce an empty diff.
- Line 55: The code reads const model = infoObj.model and later accesses
model.providerID and model.modelID without checking for null/undefined; add a
defensive null check for model (and ensure providerID/modelID exist) before
accessing their properties in the functions that reference model (e.g., where
const model = infoObj.model is declared and the subsequent accesses around the
locations noted, including the accesses at the later references), returning
early or handling the error path (log/throw) if model is missing so property
access won't throw.

In `@config/automated/.opencode/plugins/sce-agent-trace.ts`:
- Line 55: The assignment const model = infoObj.model is used without a
null/undefined guard before accessing model.providerID and model.modelID (also
at the other occurrences around the block at lines referenced), so add a
defensive check: verify model is non-null (e.g., if (!model) { handle/maybe
throw or return } or provide safe defaults) before reading providerID/modelID;
update the same pattern for the other occurrences (the accesses at the
referenced 87-88 locations) to avoid runtime TypeError when infoObj.model is
missing.
- Around line 73-77: The code collects empty patch strings into patches; update
the logic around entryObj / patch so only non-empty patches are added — e.g.,
derive patch = (entry as {patch?: string}).patch || "" and push it into patches
only when patch.trim() !== ""; alternatively filter patches after collection
with patches = patches.filter(p => p && p.trim() !== ""); ensure you reference
the entryObj/patch variables and the patches array when making the change.

---

Outside diff comments:
In @.opencode/plugins/sce-agent-trace.ts:
- Around line 73-81: The loop is pushing entry.patch || "" into the patches
array which allows empty strings to be collected and yields an empty "diff"
later; change the logic where entryObj and patch are derived (the entryObj =
entry as {patch?:string} / const patch = entryObj.patch || "") to compute a
trimmedPatch (e.g., entryObj.patch?.trim()) and only push into patches when
trimmedPatch is a non-empty string, leaving the existing check (if
(patches.length === 0) return undefined) intact.

In `@config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts`:
- Around line 55-88: The code reads infoObj.model into model then
unconditionally accesses model.providerID/model.modelID when returning the trace
object, which can throw if model is null/undefined; update the logic in this
function to guard against a missing model (infoObj.model) before using its
properties — either return undefined or set a safe default for model_id when
model is falsy, e.g. check model !== null && model !== undefined (or use a
short-circuit) before constructing model_id, and ensure sessionID/diff/time
logic remains unchanged so the function returns undefined or a trace with a
defensive model_id instead of throwing.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d6c74f66-c3dc-406a-b666-3fed7debb2c7

📥 Commits

Reviewing files that changed from the base of the PR and between 0456f57 and 7f7b2c7.

⛔ Files ignored due to path filters (14)
  • config/lib/agent-trace-plugin/bun.lock is excluded by !**/*.lock
  • context/architecture.md is excluded by !context/**/*.md
  • context/cli/cli-command-surface.md is excluded by !context/**/*.md
  • context/cli/patch-service.md is excluded by !context/**/*.md
  • context/context-map.md is excluded by !context/**/*.md
  • context/glossary.md is excluded by !context/**/*.md
  • context/overview.md is excluded by !context/**/*.md
  • context/patterns.md is excluded by !context/**/*.md
  • context/plans/add-diff-traces-model-id.md is excluded by !context/**/*.md
  • context/plans/agent-trace-plugin-message-updated.md is excluded by !context/**/*.md
  • context/sce/agent-trace-db.md is excluded by !context/**/*.md
  • context/sce/agent-trace-hooks-command-routing.md is excluded by !context/**/*.md
  • context/sce/agent-trace-minimal-generator.md is excluded by !context/**/*.md
  • context/sce/opencode-agent-trace-plugin-runtime.md is excluded by !context/**/*.md
📒 Files selected for processing (10)
  • .opencode/plugins/sce-agent-trace.ts
  • cli/migrations/agent-trace/005_add_diff_traces_model_id.sql
  • cli/src/services/agent_trace.rs
  • cli/src/services/agent_trace_db/mod.rs
  • cli/src/services/hooks/mod.rs
  • cli/src/services/patch.rs
  • config/.opencode/plugins/sce-agent-trace.ts
  • config/automated/.opencode/plugins/sce-agent-trace.ts
  • config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts
  • config/lib/agent-trace-plugin/package.json

Comment thread .opencode/plugins/sce-agent-trace.ts
Comment thread config/.opencode/plugins/sce-agent-trace.ts
Comment thread config/.opencode/plugins/sce-agent-trace.ts
Comment thread config/automated/.opencode/plugins/sce-agent-trace.ts
Comment thread config/automated/.opencode/plugins/sce-agent-trace.ts
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.

2 participants