diff --git a/cli/src/services/agent_trace.rs b/cli/src/services/agent_trace.rs index 7995f4dd..3574a2a1 100644 --- a/cli/src/services/agent_trace.rs +++ b/cli/src/services/agent_trace.rs @@ -27,11 +27,20 @@ use super::patch::{ }; pub const AGENT_TRACE_VERSION: &str = "0.1.0"; +pub const SCE_METADATA_VERSION: &str = env!("CARGO_PKG_VERSION"); fn default_agent_trace_version() -> String { AGENT_TRACE_VERSION.to_owned() } +fn default_agent_trace_metadata() -> AgentTraceMetadata { + AgentTraceMetadata { + sce: AgentTraceSceMetadata { + version: SCE_METADATA_VERSION.to_owned(), + }, + } +} + fn generate_agent_trace_id(commit_time: DateTime) -> Result { let seconds = u64::try_from(commit_time.timestamp()).with_context(|| { format!( @@ -78,6 +87,17 @@ pub struct AgentTraceTool { #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, } +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct AgentTraceMetadata { + pub sce: AgentTraceSceMetadata, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct AgentTraceSceMetadata { + pub version: String, +} fn parse_commit_timestamp(commit_timestamp: &str) -> Result> { DateTime::parse_from_rfc3339(commit_timestamp).with_context(|| { @@ -243,6 +263,9 @@ pub struct AgentTrace { /// Optional tool metadata sourced from caller-provided metadata input. #[serde(skip_serializing_if = "Option::is_none")] pub tool: Option, + /// Implementation-specific metadata for SCE-generated traces. + #[serde(default = "default_agent_trace_metadata")] + pub metadata: AgentTraceMetadata, /// File-level trace entries, one per file present in `post_commit_patch`. pub files: Vec, } @@ -442,6 +465,7 @@ pub fn build_agent_trace( revision: metadata.commit_revision.to_owned(), }), tool, + metadata: default_agent_trace_metadata(), files, }) } diff --git a/cli/src/services/agent_trace/fixtures/average_age_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/average_age_reconstruction/golden.json index e139677c..213192bb 100644 --- a/cli/src/services/agent_trace/fixtures/average_age_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/average_age_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "hunks/fib.ts", diff --git a/cli/src/services/agent_trace/fixtures/file_rename_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/file_rename_reconstruction/golden.json index 476f93f0..2f8c0900 100644 --- a/cli/src/services/agent_trace/fixtures/file_rename_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/file_rename_reconstruction/golden.json @@ -7,5 +7,10 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [] } diff --git a/cli/src/services/agent_trace/fixtures/hello_world_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/hello_world_reconstruction/golden.json index 0806e931..ae97e31b 100644 --- a/cli/src/services/agent_trace/fixtures/hello_world_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/hello_world_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "hunks/hello.ts", diff --git a/cli/src/services/agent_trace/fixtures/mixed_change_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/mixed_change_reconstruction/golden.json index 51aabb70..ee225bc5 100644 --- a/cli/src/services/agent_trace/fixtures/mixed_change_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/mixed_change_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": ".version", diff --git a/cli/src/services/agent_trace/fixtures/poem_edit_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/poem_edit_reconstruction/golden.json index c75adc95..44b91f1d 100644 --- a/cli/src/services/agent_trace/fixtures/poem_edit_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/poem_edit_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "poem.md", diff --git a/cli/src/services/agent_trace/fixtures/poem_write_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/poem_write_reconstruction/golden.json index c536c0f3..ed8665cd 100644 --- a/cli/src/services/agent_trace/fixtures/poem_write_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/poem_write_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "poem.md", diff --git a/cli/src/services/agent_trace/fixtures/text_file_lifecycle_reconstruction/golden.json b/cli/src/services/agent_trace/fixtures/text_file_lifecycle_reconstruction/golden.json index 1db1ac91..f7b38f00 100644 --- a/cli/src/services/agent_trace/fixtures/text_file_lifecycle_reconstruction/golden.json +++ b/cli/src/services/agent_trace/fixtures/text_file_lifecycle_reconstruction/golden.json @@ -7,6 +7,11 @@ "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "animals.txt", diff --git a/cli/src/services/agent_trace/tests.rs b/cli/src/services/agent_trace/tests.rs index 8a3d8146..3d223d24 100644 --- a/cli/src/services/agent_trace/tests.rs +++ b/cli/src/services/agent_trace/tests.rs @@ -79,6 +79,13 @@ fn assert_builds_expected_agent_trace(scenario: AgentTraceScenario) { ); let actual_json = serde_json::to_value(&actual).expect("agent trace should serialize"); validate_agent_trace_value(&actual_json).expect("actual json should validate against schema"); + let metadata_version = actual_json["metadata"]["sce"]["version"] + .as_str() + .expect("metadata.sce.version should serialize as a string"); + assert!( + !metadata_version.is_empty(), + "metadata.sce.version should not be empty" + ); assert_eq!(actual_json["vcs"], golden["vcs"]); assert_eq!(actual_json["files"], golden["files"]); } diff --git a/context/context-map.md b/context/context-map.md index ac795ed3..f308e935 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -46,8 +46,8 @@ Feature/domain context: - `context/sce/agent-trace-core-schema-migrations.md` (historical reference for removed local DB schema bootstrap behavior; T03 now implements the actual local DB with migrations) - `context/sce/agent-trace-retry-queue-observability.md` (inactive local-hook retry path plus historical retry/metrics reference) - `context/sce/agent-trace-local-hooks-mvp-contract-gap-matrix.md` (T01 Local Hooks MVP production contract freeze and deterministic gap matrix for `agent-trace-local-hooks-production-mvp`) -- `context/sce/agent-trace-minimal-generator.md` (implemented minimal Agent Trace builder seam at `cli/src/services/agent_trace.rs`, used by the active post-commit hook flow to produce strict `0.1.0` JSON payloads with UUIDv7 `id` derived from commit-time metadata, caller-provided commit-time `timestamp`, top-level `vcs` metadata (`type = git`, `revision` from metadata input), optional top-level `tool` metadata (`name`/`version`) sourced from builder metadata inputs, and per-file trace data from patch inputs via `intersect_patches(constructed_patch, post_commit_patch)` then `post_commit_patch`-anchored hunk classification into `ai`/`mixed`/`unknown` contributor categories, serialized per conversation as nested `contributor.type` plus optional `contributor.model_id` omitted when provenance is missing and one derived `ranges[{start_line,end_line}]` entry per post-commit hunk) -- `context/sce/agent-trace-hooks-command-routing.md` (implemented `sce hooks` command routing plus current runtime behavior: disabled-default commit-msg attribution, no-op `pre-commit`/`post-rewrite` entrypoints, active `post-commit` intersection entrypoint capturing current commit patch, querying recent `diff_traces` from past 7 days, combining valid patches via `patch::combine_patches`, intersecting via `patch::intersect_patches`, persisting results to `post_commit_patch_intersections`, building and schema-validating post-commit Agent Trace payloads, and persisting validated payloads to AgentTraceDb `agent_traces` (DB-only), plus `diff-trace` STDIN intake with required non-empty `sessionID`/`diff`/`model_id`/`tool_name`, required nullable/non-empty `tool_version`, required `u64` `time` validation, dual persistence to AgentTraceDb, and collision-safe `context/tmp/-000000-diff-trace.json` artifacts) +- `context/sce/agent-trace-minimal-generator.md` (implemented a library minimal Agent Trace generator seam at `cli/src/services/agent_trace.rs`, used by the active post-commit hook flow to produce strict `0.1.0` JSON payloads with top-level `version`, UUIDv7 `id` derived from commit-time metadata, caller-provided commit-time `timestamp`, optional top-level `vcs` metadata emitted when present (`type` from enum `git|jj|hg|svn`, `revision` from metadata input; current post-commit flow provides `git`), optional top-level `tool` metadata (`name`/`version`) sourced from builder metadata inputs when overlapping AI content exists, and always-emitted `metadata.sce.version` sourced from the compiled `sce` CLI package version, plus per-file trace data from patch inputs via `intersect_patches(constructed_patch, post_commit_patch)` then `post_commit_patch`-anchored hunk classification into `ai`/`mixed`/`unknown` contributor categories, serialized per conversation as nested `contributor.type` with optional `contributor.model_id` omitted when provenance is missing, and one derived `ranges[{start_line,end_line}]` entry per post-commit hunk) +- `context/sce/agent-trace-hooks-command-routing.md` (implemented `sce hooks` command routing plus current runtime behavior: disabled-default commit-msg attribution, no-op `pre-commit`/`post-rewrite` entrypoints, active `post-commit` intersection entrypoint capturing current commit patch, querying recent `diff_traces` from past 7 days, combining valid patches via `patch::combine_patches`, intersecting via `patch::intersect_patches`, persisting results to `post_commit_patch_intersections`, building post-commit Agent Trace payloads enriched with optional top-level `tool` metadata and `metadata.sce.version`, schema-validating them, and persisting validated payloads to AgentTraceDb `agent_traces` (DB-only), plus `diff-trace` STDIN intake with required non-empty `sessionID`/`diff`/`model_id`/`tool_name`, required nullable/non-empty `tool_version`, required `u64` `time` validation, dual persistence to AgentTraceDb, and collision-safe `context/tmp/-000000-diff-trace.json` artifacts) - `context/sce/automated-profile-contract.md` (deterministic gate policy for automated OpenCode profile, including 10 gate categories, permission mappings, automated `/commit` single-commit execution behavior, and automated profile constraints) - `context/sce/bash-tool-policy-enforcement-contract.md` (approved bash-tool blocking contract plus the implementation target for generated OpenCode enforcement, including config schema, argv-prefix matching, fixed preset catalog/messages, and precedence rules) - `context/sce/generated-opencode-plugin-registration.md` (current generated OpenCode plugin-registration contract, canonical Pkl ownership, generated manifest/plugin paths including `sce-bash-policy` + `sce-agent-trace`, and TypeScript source ownership; Claude bash-policy enforcement has been removed from generated outputs) diff --git a/context/glossary.md b/context/glossary.md index 9d1b15ef..76695a52 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -31,6 +31,7 @@ - `sce dependency baseline`: Current crate dependency set declared in `cli/Cargo.toml` (`anyhow`, `clap`, `clap_complete`, `dirs`, `hmac`, `inquire`, `reqwest`, `serde`, `serde_json`, `sha2`, `tokio`, `tracing`, `tracing-subscriber`, `turso`) and validated through normal compile/test coverage. - `local Turso adapter`: Module in `cli/src/services/local_db/mod.rs` that defines `LocalDbSpec` and exposes `LocalDb` as a `TursoDb` alias. It resolves the canonical local DB path with `local_db_path()`, currently declares zero migrations, and inherits `new()`, `execute()`, and `query()` from the shared generic adapter. - `agent trace DB adapter`: Module in `cli/src/services/agent_trace_db/mod.rs` that defines `AgentTraceDbSpec`, exposes `AgentTraceDb` as a `TursoDb` alias, resolves `/sce/agent-trace.db` through `agent_trace_db_path()`, embeds ordered migrations for `diff_traces`, `post_commit_patch_intersections`, the `diff_traces(time_ms, id)` index, `agent_traces`, nullable `diff_traces.model_id`, nullable `diff_traces.tool_name`, nullable `diff_traces.tool_version`, and nullable `agent_traces.agent_trace_id`, provides typed parameterized insert helpers for diff traces including `model_id` + tool metadata, post-commit intersection rows, and built agent-trace rows (including `agent_trace_id`), exposes chronological recent `diff_traces` query/parse support with malformed-row skip accounting, has `AgentTraceDbLifecycle` for setup/doctor integration, and is written by `sce hooks diff-trace` (`diff_traces`) plus `sce hooks post-commit` (`post_commit_patch_intersections` and built `agent_traces`). +- `Agent Trace SCE metadata`: Implementation-owned top-level metadata emitted by `build_agent_trace(...)` as `metadata.sce.version`; the value is sourced from the compiled `sce` CLI package version via `env!("CARGO_PKG_VERSION")`, is schema-validated with the rest of the payload, and is persisted in AgentTraceDb `agent_traces.trace_json` without changing the top-level Agent Trace payload/schema `version`. - `DiffTraceInsert`: Insert payload in `cli/src/services/agent_trace_db/mod.rs` carrying `time_ms`, `session_id`, `patch`, `model_id`, `tool_name`, and nullable `tool_version` for parameterized writes to the `diff_traces` table. - `DbSpec`: Service-specific database metadata trait in `cli/src/services/db/mod.rs` that supplies a diagnostic database name, canonical path resolver, and ordered embedded migration list for `TursoDb`. - `TursoDb`: Generic shared Turso database adapter in `cli/src/services/db/mod.rs`; owns parent-directory creation, Turso local open/connect flow, tokio current-thread runtime bridging, synchronous `execute()`/`query()`/`query_map()` wrappers, per-database `__sce_migrations` metadata, and generic migration execution for a `DbSpec` implementation. diff --git a/context/overview.md b/context/overview.md index 4238403f..86f7d7af 100644 --- a/context/overview.md +++ b/context/overview.md @@ -44,7 +44,7 @@ Context sync now uses an important-change gate: cross-cutting/policy/architectur The `/change-to-plan` command body is also intentionally thin orchestration: it delegates clarification and plan-shape contracts to `sce-plan-authoring` (including one-task/one-atomic-commit task slicing) while keeping wrapper-level plan output and handoff obligations explicit. The generated OpenCode command doc now also emits `entry-skill: sce-plan-authoring` plus an ordered `skills` list. The targeted support commands (`handover`, `commit`, `validate`) keep their thin-wrapper behavior and now also emit machine-readable OpenCode command frontmatter describing their entry skill and ordered skill chain. `/commit` is now split by profile: manual generated commands remain proposal-only and allow split guidance when staged changes mix unrelated goals, while the automated OpenCode `/commit` command generates exactly one commit message and runs `git commit` against the staged diff. The shared `sce-atomic-commit` contract also requires commit bodies to cite affected plan slug(s) and updated task ID(s) when staged changes include `context/plans/*.md`, and to stop for clarification instead of inventing those references when the staged plan diff is ambiguous. The prior no-git-wrapper Agent Trace design artifacts under `context/sce/agent-trace-*.md` are retained only as historical reference; the current CLI runtime no longer wires the removed Agent Trace schema adaptation, payload building, retry replay, or rewrite handling paths into local hook execution. -The hooks service now uses a minimal attribution-only runtime: `commit-msg` is the only hook that mutates behavior, conditionally injecting exactly one canonical SCE trailer when the attribution-hooks gate is enabled and `SCE_DISABLED` is false; `pre-commit` and `post-rewrite` remain deterministic no-op entrypoints; `post-commit` is an active intersection entrypoint that captures current commit patch, queries recent `diff_traces` from past 7 days, combines/intersects patches, persists intersection metadata to `post_commit_patch_intersections`, and persists the built Agent Trace payload to AgentTraceDb `agent_traces` (DB-only, no post-commit Agent Trace file artifact); and `diff-trace` currently validates/persists required non-empty `sessionID`/`diff`/`model_id`/`tool_name`, required `tool_version` (must be present and either `null` or a non-empty string), plus required `u64` millisecond `time`, with non-lossy AgentTraceDb `time_ms` conversion and collision-safe timestamp+attempt artifact filenames. +The hooks service now uses a minimal attribution-only runtime: `commit-msg` is the only hook that mutates behavior, conditionally injecting exactly one canonical SCE trailer when the attribution-hooks gate is enabled and `SCE_DISABLED` is false; `pre-commit` and `post-rewrite` remain deterministic no-op entrypoints; `post-commit` is an active intersection entrypoint that captures current commit patch, queries recent `diff_traces` from past 7 days, combines/intersects patches, persists intersection metadata to `post_commit_patch_intersections`, and persists the schema-validated built Agent Trace payload, including optional top-level `tool` metadata from recent diff-trace rows and top-level `metadata.sce.version` from the compiled `sce` CLI package version, to AgentTraceDb `agent_traces` (DB-only, no post-commit Agent Trace file artifact); and `diff-trace` currently validates/persists required non-empty `sessionID`/`diff`/`model_id`/`tool_name`, required `tool_version` (must be present and either `null` or a non-empty string), plus required `u64` millisecond `time`, with non-lossy AgentTraceDb `time_ms` conversion and collision-safe timestamp+attempt artifact filenames. The CLI now also includes an approved operator-environment doctor contract documented in `context/sce/agent-trace-hook-doctor.md`; the runtime now matches the implemented T06 slice for `sce doctor --fix` parsing/help, stable problem/fix-result reporting, canonical hook-repair reuse, and bounded doctor-owned local-DB directory bootstrap for the missing SCE-owned DB parent path. The local DB service now provides `LocalDb` as a thin `TursoDb` alias in `cli/src/services/local_db/mod.rs`; `LocalDbSpec` resolves the canonical local DB path from the shared default-path catalog and currently declares zero migrations. Shared Turso infrastructure lives in `cli/src/services/db/mod.rs`, where `DbSpec` and generic `TursoDb` support dual-mode operation — local mode via `turso::Builder::new_local()` when `SCE_SYNC_URL`+`SCE_SYNC_TOKEN` are absent, or sync (Turso Cloud) mode via `turso::sync::Builder::new_remote()` when both are set. It owns parent-directory creation, connection setup, tokio current-thread runtime bridging, synchronous `execute`/`query`/`query_map`, generic migration execution, sync operations (`push`/`pull`/`checkpoint`/`stats`) that are no-ops in local mode (sync is never triggered automatically from `execute()`), and shared DB lifecycle helpers for service-specific database wrappers. Agent Trace persistence now has its own `cli/src/services/agent_trace_db/mod.rs` wrapper, canonical `/sce/agent-trace.db` path, ordered `diff_traces` plus `post_commit_patch_intersections` plus `agent_traces` migrations, typed parameterized insert helpers for diff traces, post-commit intersection rows, and built agent-trace rows, chronological recent `diff_traces` query/parse support with malformed-row skip accounting, registered setup/doctor lifecycle provider, active `sce hooks diff-trace` writes for `diff_traces`, and active `sce hooks post-commit` writes for built `agent_traces` payloads. The hooks command surface now also supports concrete runtime subcommand routing (`pre-commit`, `commit-msg`, `post-commit`, `post-rewrite`, `diff-trace`) with deterministic argument/STDIN validation. Current runtime behavior keeps attribution disabled by default: the attribution gate enables canonical trailer insertion in `commit-msg`, `pre-commit`/`post-rewrite` remain deterministic no-ops, `post-commit` is the active bounded recent-diff-trace intersection path, and `diff-trace` is the active intake path for parsed STDIN `{ sessionID, diff, time, model_id, tool_name, tool_version }` payload persistence with required non-empty `tool_name`, required nullable/non-empty `tool_version`, required `u64` millisecond `time`, non-lossy AgentTraceDb `time_ms` conversion, and collision-safe timestamp+attempt artifact filenames. This behavior is documented in `context/sce/agent-trace-hooks-command-routing.md`. diff --git a/context/plans/agent-trace-sce-metadata.md b/context/plans/agent-trace-sce-metadata.md new file mode 100644 index 00000000..5a8b2a14 --- /dev/null +++ b/context/plans/agent-trace-sce-metadata.md @@ -0,0 +1,114 @@ +# Agent Trace SCE Metadata + +## Change summary + +Reimplement the previously dropped Agent Trace metadata change after git conflict resolution. Add implementation-specific SCE metadata to every built Agent Trace payload before it is serialized into `agent_traces.trace_json` by the post-commit flow. + +The target payload shape is a top-level `metadata` object accepted by `config/schema/agent-trace.schema.json`: + +```json +{ + "metadata": { + "sce": { + "version": "0.2.0" + } + } +} +``` + +Source `metadata.sce.version` from the compiled CLI package version (`env!("CARGO_PKG_VERSION")`), not by reading `.version` at runtime. In the current checkout, `cli/Cargo.toml` has `version = "0.2.0"`, and release/package parity checks keep it aligned with the repo-root `.version` authority. This keeps generated trace JSON deterministic, packaged-binary friendly, and independent from checkout-time files. + +Read-only plan review found the conflict-dropped baseline currently lacks this metadata in `cli/src/services/agent_trace.rs`, while `config/schema/agent-trace.schema.json` already permits a generic top-level `metadata` object. + +Optional future metadata ideas remain out of scope for this slice: + +- `metadata.sce.generator`: stable identifier for the SCE trace generator path, if downstream consumers need to distinguish local-hook vs hosted ingestion. +- `metadata.sce.agent_trace_payload_version`: copy of top-level Agent Trace `version`, only if consumers need vendor metadata to be self-contained. +- `metadata.sce.commit_window_days` or similar runtime window metadata, only if trace consumers need to explain the bounded 7-day diff-trace selection policy. + +## Success criteria + +- Built Agent Trace structs serialize with top-level `metadata.sce.version` equal to the compiled `sce` CLI package version. +- Post-commit `agent_traces.trace_json` rows receive the enriched payload because the enrichment happens in `agent_trace::build_agent_trace(...)` before schema validation and DB insertion. +- Existing Agent Trace schema validation continues to pass using the embedded schema's generic `metadata` object allowance. +- Unit/golden fixture coverage proves the metadata field is present and stable. +- Current-state context docs describing the Agent Trace generator and DB payload are updated after implementation. + +## Constraints and non-goals + +- Do not add a new Agent Trace DB migration; `trace_json` is already persisted as JSON text. +- Do not change top-level Agent Trace spec `version` semantics; `AGENT_TRACE_VERSION` remains the Agent Trace payload/schema version, while `metadata.sce.version` is the SCE CLI implementation version. +- Do not add extra metadata fields in this implementation beyond `metadata.sce.version` unless explicitly approved later. +- Do not read `.version` from disk at runtime. +- Preserve deterministic serialization and existing schema-validation behavior. +- Do not broaden hook command behavior or OpenCode plugin behavior. + +## Task stack + +- [x] T01: `Add SCE metadata to generated Agent Trace payloads` (status:done) + - Task ID: T01 + - Goal: Extend `cli/src/services/agent_trace.rs` so `build_agent_trace(...)` includes top-level `metadata.sce.version` sourced from the compiled CLI package version. + - Boundaries (in/out of scope): In - Agent Trace domain structs/helpers, `AgentTrace` serialization shape, builder assignment, existing agent-trace tests, and golden fixtures under `cli/src/services/agent_trace/fixtures/**/golden.json`. Out - DB schema migrations, hook command behavior changes, OpenCode plugin changes, extra metadata keys, runtime `.version` file reads, and release/version bump work. + - Done when: Serialized Agent Trace JSON contains `metadata: { sce: { version: env!("CARGO_PKG_VERSION") } }`; existing schema validation accepts the enriched payload; fixture/golden expectations and direct assertions cover the new field. + - Verification notes (commands or checks): Prefer targeted agent-trace Rust coverage during implementation if allowed by policy, then run repo-preferred `nix flake check` before handoff. + - Completed: 2026-05-20 + - Files changed: `cli/src/services/agent_trace.rs`, `cli/src/services/agent_trace/tests.rs`, `cli/src/services/agent_trace/fixtures/**/golden.json` + - Evidence: `nix flake check` passed; `nix run .#pkl-check-generated` passed. Targeted `nix develop -c sh -c 'cd cli && cargo test agent_trace'` was attempted first and blocked by repo bash policy `use-nix-flake-check-over-cargo-test`, so repo-preferred flake verification was used. + - Context-sync classification: localized Agent Trace payload contract change; current-state Agent Trace context docs require sync, while root shared docs are expected to be verify-only unless code truth reveals broader drift. + +- [x] T02: `Sync Agent Trace metadata context` (status:done) + - Task ID: T02 + - Goal: Update current-state context to document the new metadata contract and version-source rationale after code truth changes. + - Boundaries (in/out of scope): In - `context/sce/agent-trace-minimal-generator.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hooks-command-routing.md`, `context/context-map.md`, and glossary entries if the `AgentTrace`/`build_agent_trace` current-state contract changes. Out - historical Agent Trace reference docs unless they are incorrectly marked current-state, broad root-context churn, and unrelated Agent Trace cleanup. + - Done when: Current-state context states that generated Agent Trace payloads include `metadata.sce.version` from the compiled SCE CLI package version before schema validation and DB persistence. + - Verification notes (commands or checks): Review context references against code truth; use verify-only handling for root `overview.md`, `architecture.md`, and `patterns.md` unless implementation changes architecture or terminology beyond this localized payload addition. + - Completed: 2026-05-20 + - Files changed: `context/sce/agent-trace-minimal-generator.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hooks-command-routing.md`, `context/context-map.md`, `context/overview.md`, `context/glossary.md` + - Evidence: Code truth reviewed in `cli/src/services/agent_trace.rs`, `cli/src/services/hooks/mod.rs`, and `cli/src/services/agent_trace_db/mod.rs`; context grep for `metadata.sce.version` / compiled package version returned the expected current-state references; `git diff --check` passed. + - Context-sync classification: localized Agent Trace payload documentation update; root `architecture.md` and `patterns.md` were verify-only, while `overview.md` already carries a concise current-state hook-runtime summary. + +- [x] T03: `Validate and clean up Agent Trace metadata change` (status:done) + - Task ID: T03 + - Goal: Run final validation for the reimplemented metadata change and remove any temporary scaffolding. + - Boundaries (in/out of scope): In - full repository validation, generated-output parity check, whitespace checks if available, final plan evidence capture, and temporary/debug artifact review. Out - unrelated refactors, additional Agent Trace enrichment, release/version bump work, and new persistence migrations. + - Done when: Required validation commands pass or failures are documented with actionable follow-up; no task-introduced temporary files or debug-only code remain; context and plan status reflect the final state. + - Verification notes (commands or checks): `nix run .#pkl-check-generated`; `nix flake check`; whitespace/diff checks as available in the implementation session. + - Completed: 2026-05-20 + - Files changed by T03: `context/plans/agent-trace-sce-metadata.md` + - Evidence: `git diff --check` passed before and after validation; `nix run .#pkl-check-generated` passed with "Generated outputs are up to date."; `nix flake check` passed with all checks passed. + - Cleanup review: Worktree review found no task-introduced tracked temporary files or debug-only code. Ignored `context/tmp/` hook/runtime artifacts existed and were left untouched because they are not T03 scaffolding. + - Context-sync classification: final validation/status update only; no root context edits expected unless `sce-context-sync` finds drift. + +## Open questions + +- None blocking. Proposed additional metadata keys are deferred until explicitly requested. + +## Validation Report + +### Commands run + +- `git diff --check` -> exit 0; no whitespace errors reported. +- `nix run .#pkl-check-generated` -> exit 0; reported `Generated outputs are up to date.`. +- `nix flake check` -> exit 0; evaluated packages, checks, apps, and dev shell; reported `all checks passed`. + +### Cleanup and context verification + +- No task-introduced tracked temporary files or debug-only code were found. +- Existing ignored `context/tmp/` hook/runtime artifacts were left untouched because they are runtime artifacts, not T03 scaffolding. +- Context sync classified T03 as verify-only/final-validation status work; current-state Agent Trace documentation is present and discoverable from `context/context-map.md`. + +### Success-criteria verification + +- [x] Built Agent Trace structs serialize with top-level `metadata.sce.version` equal to the compiled `sce` CLI package version -> confirmed by `cli/src/services/agent_trace.rs` (`SCE_METADATA_VERSION = env!("CARGO_PKG_VERSION")`) and direct test assertion in `cli/src/services/agent_trace/tests.rs`. +- [x] Post-commit `agent_traces.trace_json` rows receive the enriched payload -> confirmed by current context/code path: post-commit builds via `agent_trace::build_agent_trace(...)`, validates JSON, then inserts serialized payload into AgentTraceDb. +- [x] Existing Agent Trace schema validation continues to pass -> confirmed by `nix flake check` and agent-trace golden validation coverage in the flake check surface. +- [x] Unit/golden fixture coverage proves the metadata field is present and stable -> confirmed by updated golden fixtures and test comparison of `actual_json["metadata"]` to fixture truth. +- [x] Current-state context docs describing the Agent Trace generator and DB payload are updated -> confirmed by context sync over `context/sce/agent-trace-minimal-generator.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hooks-command-routing.md`, `context/context-map.md`, `context/overview.md`, and `context/glossary.md`. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/sce/agent-trace-db.md b/context/sce/agent-trace-db.md index 73abafa4..17cd019a 100644 --- a/context/sce/agent-trace-db.md +++ b/context/sce/agent-trace-db.md @@ -97,7 +97,7 @@ The `agent_traces` migration creates: - Command success requires both artifact and database persistence to succeed. - Existing artifact files are not backfilled into the database. -Post-commit intersection rows are written by the active `post-commit` hook flow, and the same flow now also inserts built Agent Trace payloads into `agent_traces` via `AgentTraceDb::insert_agent_trace()` (see [agent-trace-hooks-command-routing.md](agent-trace-hooks-command-routing.md)). +Post-commit intersection rows are written by the active `post-commit` hook flow, and the same flow now also inserts built Agent Trace payloads into `agent_traces` via `AgentTraceDb::insert_agent_trace()` (see [agent-trace-hooks-command-routing.md](agent-trace-hooks-command-routing.md)). The persisted `trace_json` is the schema-validated `build_agent_trace(...)` output and includes top-level `metadata.sce.version` from the compiled `sce` CLI package version. ## Recent patch reads diff --git a/context/sce/agent-trace-hooks-command-routing.md b/context/sce/agent-trace-hooks-command-routing.md index be488b18..eb50d838 100644 --- a/context/sce/agent-trace-hooks-command-routing.md +++ b/context/sce/agent-trace-hooks-command-routing.md @@ -1,10 +1,12 @@ # Agent Trace Hooks Command Routing ## Scope + - Current trace-removal baseline for `cli/src/services/hooks/mod.rs` - Focus: concrete `sce hooks` subcommand routing plus current minimal runtime behavior ## Implemented command surface + - `sce hooks pre-commit` - `sce hooks commit-msg ` - `sce hooks post-commit [--vcs ]` @@ -12,12 +14,14 @@ - `sce hooks diff-trace` ## Parser and dispatch behavior + - `cli/src/app.rs` routes `hooks` through dedicated hook-subcommand parsing. - `cli/src/services/hooks/mod.rs` owns deterministic runtime dispatch through `HookSubcommand` + `run_hooks_subcommand`. - `post-commit` now parses optional `--vcs` input tolerantly at the command boundary: recognized values (`git|jj|hg|svn`) map to `Some(AgentTraceVcsType)`, while unknown values map to `None` without parse failure. - Invalid and ambiguous invocations return deterministic actionable errors pointing to `sce hooks --help`. ## Current runtime behavior + - Shared enablement gate: - env `SCE_ATTRIBUTION_HOOKS_ENABLED` - config `policies.attribution_hooks.enabled` @@ -41,6 +45,7 @@ - At the current runtime boundary, optional parsed `vcs_type` is forwarded unchanged into `agent_trace::build_agent_trace(...)`; when absent, the built payload omits top-level `vcs`. - The run-flow path maps commit-time metadata to RFC3339 and calls `agent_trace::build_agent_trace(...)`. - The same run-flow call now also forwards optional `tool_name` / `tool_version` from `PostCommitIntersectionFlowResult` into `AgentTraceMetadataInput`, so built post-commit payloads preserve tool metadata derived from recent parsed diff-trace rows. + - The built Agent Trace payload includes top-level `metadata.sce.version` from the compiled `sce` CLI package version before conversion to JSON. - The built Agent Trace payload is converted to JSON `Value` and validated via `agent_trace::validate_agent_trace_value(...)` before persistence. - Validation failures are returned through the same post-commit runtime failure path/class used for Agent Trace DB insertion failures (no silent fallback). - When validation passes, the payload is serialized and inserted into Agent Trace DB `agent_traces` using `commit_id` from flow-result commit metadata and `commit_time_ms` from flow-result post-commit timestamp metadata. @@ -51,6 +56,7 @@ - `diff-trace` success requires both persistence paths to succeed; artifact write failures and AgentTraceDb open/insert failures are command-failing runtime errors logged through `sce.hooks.diff_trace.error`. ## Explicit non-goals in the current baseline + - No checkpoint handoff file - No git-notes persistence - No backfill/import of existing `context/tmp/*-diff-trace.json` artifacts into AgentTraceDb diff --git a/context/sce/agent-trace-minimal-generator.md b/context/sce/agent-trace-minimal-generator.md index 4dc16468..34a6d694 100644 --- a/context/sce/agent-trace-minimal-generator.md +++ b/context/sce/agent-trace-minimal-generator.md @@ -17,16 +17,18 @@ Given a `constructed_patch` (AI candidate) and a `post_commit_patch` (canonical ## Domain types -| Type | Purpose | -|---|---| -| `HunkContributor` | Enum: `Ai`, `Mixed`, `Unknown` | -| `Contributor` | Nested per-conversation object carrying `type: HunkContributor` and optional `model_id` omitted when absent | -| `LineRange` | New-file line span with `start_line` + `end_line` | -| `Conversation` | Per-hunk entry: nested contributor + `ranges` (currently exactly one range derived from `post_commit_patch`) | -| `TraceFile` | Per-file entry: path + conversations | -| `AgentTraceVcs` | Optional top-level VCS metadata object carrying `type` + `revision` when present | -| `AgentTraceTool` | Optional top-level tool metadata object carrying optional `name` + optional `version` | -| `AgentTrace` | Top-level payload: `version`, `id`, `timestamp`, optional `vcs`, optional `tool`, `files` | +| Type | Purpose | +| ----------------------- | ------------------------------------------------------------------------------------------------------------ | +| `HunkContributor` | Enum: `Ai`, `Mixed`, `Unknown` | +| `Contributor` | Nested per-conversation object carrying `type: HunkContributor` and optional `model_id` omitted when absent | +| `LineRange` | New-file line span with `start_line` + `end_line` | +| `Conversation` | Per-hunk entry: nested contributor + `ranges` (currently exactly one range derived from `post_commit_patch`) | +| `TraceFile` | Per-file entry: path + conversations | +| `AgentTraceVcs` | Optional top-level VCS metadata object carrying `type` + `revision` when present | +| `AgentTraceTool` | Optional top-level tool metadata object carrying optional `name` + optional `version` | +| `AgentTraceMetadata` | Top-level implementation metadata object carrying SCE-owned metadata | +| `AgentTraceSceMetadata` | Nested `metadata.sce` object carrying the compiled SCE CLI package `version` | +| `AgentTrace` | Top-level payload: `version`, `id`, `timestamp`, optional `vcs`, optional `tool`, `metadata`, `files` | All types are `serde`-serializable with `snake_case` field naming. `Conversation.contributor` serializes as a nested object with a JSON field named `type`; `model_id` is present only when a concrete value exists. @@ -34,22 +36,28 @@ All types are `serde`-serializable with `snake_case` field naming. `Conversation Current output includes top-level metadata fields with this contract: -- `version` is fixed to `"0.1.0"` +- `version` is fixed to `"0.1.0"` and remains the Agent Trace payload/schema version - `id` is generated per `build_agent_trace(...)` call as a UUIDv7 string derived from the same commit-time moment used for `timestamp` - `timestamp` is sourced from explicit commit metadata input (`AgentTraceMetadataInput.commit_timestamp`) and must be RFC 3339 - `vcs` is emitted only when explicit commit metadata input includes `AgentTraceMetadataInput.vcs_type` - when `vcs` is emitted, `vcs.type` is sourced from the schema-aligned enum (`git | jj | hg | svn`) and `vcs.revision` is sourced from `AgentTraceMetadataInput.commit_revision` - `tool` is omitted when `intersection_patch.files` is empty (no AI content overlapped with the post-commit patch) or when both `AgentTraceMetadataInput.tool_name` and `AgentTraceMetadataInput.tool_version` are `None`; when `intersection_patch.files` is non-empty and either metadata value is present, builder construction sets `AgentTrace.tool` and it serializes as `{ "name"?: string, "version"?: string }` with each nested field omitted when absent +- `metadata.sce.version` is always emitted and is sourced from `env!("CARGO_PKG_VERSION")`, the compiled `sce` CLI package version; it is implementation metadata and does not change top-level Agent Trace `version` semantics ```json { - "version": "0.1", + "version": "0.1.0", "id": "01962f15-2d3d-7c85-9f6b-0a8b4f6b2fd1", "timestamp": "2026-04-23T10:20:30Z", "vcs": { "type": "git", "revision": "a0b1c2d3e4f5a6b7c8d9e0f11223344556677889" }, + "metadata": { + "sce": { + "version": "0.2.0" + } + }, "files": [ { "path": "src/example.ts", @@ -72,13 +80,12 @@ Current output includes top-level metadata fields with this contract: ## Public API - `classify_hunk(post_commit_hunk, intersection_hunks) -> HunkContributor` — classify a single `post_commit_patch` hunk against `intersection_patch` hunks. -- `build_agent_trace(constructed_patch, post_commit_patch, metadata) -> Result` — full generator entrypoint that validates `metadata.commit_timestamp` as RFC 3339, uses it as top-level `timestamp`, derives a UUIDv7 `id` from that same commit-time moment, conditionally emits `vcs` when `metadata.vcs_type` is present (mapping `vcs.type` from metadata and `vcs.revision` from `metadata.commit_revision`), and carries optional tool metadata inputs (`metadata.tool_name`, `metadata.tool_version`) for top-level `tool` mapping. When `intersection_patch.files` is empty, `tool` is always `None` regardless of metadata values. +- `build_agent_trace(constructed_patch, post_commit_patch, metadata) -> Result` — full generator entrypoint that validates `metadata.commit_timestamp` as RFC 3339, uses it as top-level `timestamp`, derives a UUIDv7 `id` from that same commit-time moment, conditionally emits `vcs` only when `metadata.vcs_type` is present (mapping `vcs.type` from metadata and `vcs.revision` from `metadata.commit_revision`), carries optional tool metadata inputs (`metadata.tool_name`, `metadata.tool_version`) for top-level `tool` mapping, and always emits `metadata.sce.version` from the compiled package version. When `intersection_patch.files` is empty, `tool` is always `None` regardless of metadata values. ## Test fixture contract -- Golden fixtures under `cli/src/services/agent_trace/fixtures/**/golden.json` pin deterministic literal values for top-level `id` and `timestamp`. -- Tests still validate runtime metadata behavior explicitly (`id` parses as UUIDv7 and `timestamp` equals provided commit metadata), then normalize those runtime values to the deterministic fixture literals before payload comparison. -- Because the embedded schema currently expects `contributor.model_id` as a string when present, golden/schema checks operate on a model-id-stripped comparison view, while dedicated assertions validate contributor `model_id` mapping semantics (`ai`/`mixed` populated when provenance exists, omitted when absent). +- Golden fixtures under `cli/src/services/agent_trace/fixtures/**/golden.json` pin deterministic literal values for top-level `id`, `timestamp`, optional `vcs`, `metadata.sce.version`, and expected file/conversation shapes. +- Tests validate golden fixtures and built payloads against the embedded schema, assert core runtime metadata directly (`version`, `timestamp`, optional `vcs`, and `metadata.sce.version`), and compare `vcs`, `metadata`, and `files` against fixture truth. ## Relationship to existing patch service