From b525f5e8441e285fe73eedb6c062971352083d1d Mon Sep 17 00:00:00 2001 From: David Abram Date: Sun, 13 Sep 2026 13:39:02 +0200 Subject: [PATCH 1/2] runtime+context: Remove checkout identity infrastructure Checkout identity was diagnostic-only and did not affect repository-scoped database selection or Agent Trace persistence. Remove its service and storage, setup, and doctor consumers so SCE no longer creates or reads checkout-id files or reports checkout identity. Update durable context and add the remove-checkout-id plan tracking the implementation and context follow-up, while leaving pre-existing checkout-id files inert and untouched. --- cli/src/services/agent_trace_db/lifecycle.rs | 6 +- cli/src/services/agent_trace_storage/mod.rs | 55 +------- cli/src/services/checkout/mod.rs | 133 ------------------- cli/src/services/doctor/inspect.rs | 22 +-- cli/src/services/doctor/render.rs | 3 - cli/src/services/doctor/types.rs | 6 - cli/src/services/mod.rs | 1 - context/architecture.md | 8 +- context/cli/agent-trace-storage.md | 9 +- context/cli/checkout-identity.md | 32 +---- context/cli/cli-command-surface.md | 10 +- context/cli/service-lifecycle.md | 2 +- context/context-map.md | 8 +- context/glossary.md | 12 +- context/overview.md | 2 +- context/plans/remove-checkout-id.md | 83 ++++++++++++ context/sce/agent-trace-db.md | 7 +- context/sce/agent-trace-hook-doctor.md | 6 +- context/sce/shared-turso-db.md | 2 +- 19 files changed, 132 insertions(+), 275 deletions(-) delete mode 100644 cli/src/services/checkout/mod.rs create mode 100644 context/plans/remove-checkout-id.md diff --git a/cli/src/services/agent_trace_db/lifecycle.rs b/cli/src/services/agent_trace_db/lifecycle.rs index 08ad3959d..e257f9d23 100644 --- a/cli/src/services/agent_trace_db/lifecycle.rs +++ b/cli/src/services/agent_trace_db/lifecycle.rs @@ -80,7 +80,6 @@ struct RepositoryDatabaseSetup { canonical_identity: String, identity_source: String, configured_remote: Option, - checkout_id: String, source_instance_id: String, database_path: PathBuf, } @@ -107,7 +106,6 @@ fn initialize_repository_agent_trace_db(repo_root: &Path) -> Result S .map(|remote| format!("\nAgent Trace configured remote: {remote}")) .unwrap_or_default(); format!( - "Agent Trace repository ID: {}\nAgent Trace identity source: {}\nAgent Trace canonical identity: {}{}\nAgent Trace checkout identity: {}\nAgent Trace source-instance ID: {}\nAgent Trace repository-scoped database initialized at '{}'.", + "Agent Trace repository ID: {}\nAgent Trace identity source: {}\nAgent Trace canonical identity: {}{}\nAgent Trace source-instance ID: {}\nAgent Trace repository-scoped database initialized at '{}'.", setup.repository_id, setup.identity_source, setup.canonical_identity, remote_line, - setup.checkout_id, setup.source_instance_id, setup.database_path.display() ) @@ -237,7 +234,6 @@ mod tests { canonical_identity: String::from("github.com/example/repo"), identity_source: String::from("remote_url"), configured_remote: configured_remote.map(String::from), - checkout_id: String::from("checkout-abc"), source_instance_id: String::from("11111111-2222-3333-4444-555555555555"), database_path: PathBuf::from("/tmp/agent-trace.db"), } diff --git a/cli/src/services/agent_trace_storage/mod.rs b/cli/src/services/agent_trace_storage/mod.rs index 3cdf3aaec..9b775a3cb 100644 --- a/cli/src/services/agent_trace_storage/mod.rs +++ b/cli/src/services/agent_trace_storage/mod.rs @@ -1,21 +1,10 @@ -//! Repository-scoped Agent Trace storage resolution. -//! -//! Resolves the active Agent Trace database for a Git repository checkout: -//! one logical Git repository maps to exactly one database at -//! `/sce/repos//agent-trace.db`. Clones and linked -//! worktrees of the same logical repository share that database while keeping -//! their own distinct checkout IDs. Legacy checkout-scoped -//! `agent-trace-.db` files are never selected, created, or -//! touched by this resolver. - use std::path::{Path, PathBuf}; use std::thread; use std::time::Duration; -use anyhow::{Context, Result}; +use anyhow::Result; use crate::services::agent_trace_db::repository::{RepositoryAgentTraceDb, RepositoryMetadata}; -use crate::services::checkout::{get_or_create_checkout_id, resolve_git_dir}; use crate::services::default_paths::{ agent_trace_db_path_for_repository, agent_trace_db_path_for_repository_at, }; @@ -46,9 +35,6 @@ pub struct ResolvedAgentTraceStorage { /// Repository identity (canonical identity plus repository ID) and the /// source it was resolved from. pub repository_identity: ResolvedRepositoryIdentity, - /// Stable identity of this clone/worktree. Kept for diagnostics only; - /// never persisted on Agent Trace rows. - pub checkout_id: String, /// Repository-scoped database path /// `/sce/repos//agent-trace.db`. pub db_path: PathBuf, @@ -67,7 +53,7 @@ pub fn resolve_agent_trace_storage( ) -> Result { let repository_identity = resolve_identity(context)?; let db_path = agent_trace_db_path_for_repository(&repository_identity.identity.repository_id)?; - open_storage(context, repository_identity, db_path) + open_storage(repository_identity, db_path) } /// Resolution core against an explicit state root, so tests can exercise the @@ -81,7 +67,7 @@ pub fn resolve_agent_trace_storage_at_state_root( state_root, &repository_identity.identity.repository_id, )?; - open_storage(context, repository_identity, db_path) + open_storage(repository_identity, db_path) } /// Resolves repository-scoped Agent Trace storage for high-frequency hook @@ -97,7 +83,7 @@ pub fn resolve_agent_trace_storage_for_hook_runtime( ) -> Result { let repository_identity = resolve_identity(context)?; let db_path = agent_trace_db_path_for_repository(&repository_identity.identity.repository_id)?; - open_storage_for_hook_runtime(context, repository_identity, db_path) + open_storage_for_hook_runtime(repository_identity, db_path) } /// Hook-runtime resolution core against an explicit state root, so tests can @@ -111,7 +97,7 @@ pub fn resolve_agent_trace_storage_for_hook_runtime_at_state_root( state_root, &repository_identity.identity.repository_id, )?; - open_storage_for_hook_runtime(context, repository_identity, db_path) + open_storage_for_hook_runtime(repository_identity, db_path) } fn resolve_identity(context: &AgentTraceStorageContext<'_>) -> Result { @@ -124,16 +110,10 @@ fn resolve_identity(context: &AgentTraceStorageContext<'_>) -> Result, repository_identity: ResolvedRepositoryIdentity, db_path: PathBuf, ) -> Result { - // Opening the database creates `repos//` when missing; - // directory creation is idempotent and first-time schema initialization may - // briefly race on SQLite metadata locks, so retry the fast-path/migrate - // sequence a small bounded number of times. open_storage_with( - context, repository_identity, db_path, open_repository_db_concurrently_safe, @@ -141,12 +121,10 @@ fn open_storage( } fn open_storage_for_hook_runtime( - context: &AgentTraceStorageContext<'_>, repository_identity: ResolvedRepositoryIdentity, db_path: PathBuf, ) -> Result { open_storage_with( - context, repository_identity, db_path, open_repository_db_for_hook_runtime, @@ -154,30 +132,15 @@ fn open_storage_for_hook_runtime( } fn open_storage_with( - context: &AgentTraceStorageContext<'_>, repository_identity: ResolvedRepositoryIdentity, db_path: PathBuf, open_db: impl FnOnce(&Path, &str) -> Result<(RepositoryAgentTraceDb, RepositoryMetadata)>, ) -> Result { - let git_dir = resolve_git_dir(context.repository_root).with_context(|| { - format!( - "failed to resolve git directory for Agent Trace repository DB from '{}'", - context.repository_root.display() - ) - })?; - let checkout_id = get_or_create_checkout_id(&git_dir).with_context(|| { - format!( - "failed to get or create checkout identity under '{}'", - git_dir.display() - ) - })?; - let repository_id = &repository_identity.identity.repository_id; let (db, metadata) = open_db(&db_path, repository_id)?; Ok(ResolvedAgentTraceStorage { repository_identity, - checkout_id, db_path, db, metadata, @@ -345,7 +308,7 @@ mod tests { } #[test] - fn clones_of_the_same_repository_share_the_db_path_with_distinct_checkout_ids() { + fn clones_of_the_same_repository_share_the_db_path() { let state_root = unique_temp_dir("state-clones"); // Equivalent SSH and HTTPS remotes for the same logical repository. let clone_a = init_git_repo_with_remote("clone-a", "git@github.com:acme/widgets.git"); @@ -363,7 +326,6 @@ mod tests { storage_b.repository_identity.identity.repository_id ); assert_eq!(storage_a.db_path, storage_b.db_path); - assert_ne!(storage_a.checkout_id, storage_b.checkout_id); assert_no_legacy_db_paths(&state_root); std::fs::remove_dir_all(&state_root).expect("clean up state root"); @@ -372,7 +334,7 @@ mod tests { } #[test] - fn linked_worktree_shares_the_db_path_with_a_distinct_checkout_id() { + fn linked_worktree_shares_the_db_path() { let state_root = unique_temp_dir("state-worktree"); let repo = init_git_repo_with_remote("worktree-main", "git@github.com:acme/widgets.git"); git(&repo, &["config", "user.email", "test@example.com"]); @@ -399,7 +361,6 @@ mod tests { .expect("worktree storage should resolve"); assert_eq!(storage_main.db_path, storage_worktree.db_path); - assert_ne!(storage_main.checkout_id, storage_worktree.checkout_id); assert_no_legacy_db_paths(&state_root); std::fs::remove_dir_all(&state_root).expect("clean up state root"); @@ -445,7 +406,6 @@ mod tests { .expect("second resolution should succeed"); assert_eq!(first.db_path, second.db_path); - assert_eq!(first.checkout_id, second.checkout_id); assert_eq!( first.repository_identity.identity.repository_id, second.repository_identity.identity.repository_id @@ -551,7 +511,6 @@ mod tests { resolve_agent_trace_storage_at_state_root(&context_for(&clone_b), &state_root) .expect("clone B storage should resolve"); assert_eq!(storage_a.db_path, storage_b.db_path); - assert_ne!(storage_a.checkout_id, storage_b.checkout_id); let patches = storage_b .db diff --git a/cli/src/services/checkout/mod.rs b/cli/src/services/checkout/mod.rs deleted file mode 100644 index 70b59ed34..000000000 --- a/cli/src/services/checkout/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -//! Checkout identity service. -//! -//! Each cloned repository (and linked Git worktree) gets its own stable checkout -//! identity stored in `/sce/checkout-id`. The checkout ID is a `UUIDv7` -//! string, consistent with the existing `agent_trace_id` convention in this -//! codebase. -//! -//! Checkout identity is repository metadata used by Agent Trace diagnostics. - -use std::path::{Path, PathBuf}; -use std::process::Command; - -use anyhow::{anyhow, Context, Result}; -use uuid::Uuid; - -/// Subdirectory inside `/` where SCE checkout metadata lives. -const SCE_CHECKOUT_DIR: &str = "sce"; - -/// File name for the checkout ID inside `/sce/`. -const CHECKOUT_ID_FILE: &str = "checkout-id"; - -/// Resolves the Git directory (`.git` for normal clones, or the worktree-specific -/// path for linked worktrees) by running `git rev-parse --git-dir` from the -/// given repository root. -/// -/// For a normal clone this returns `/.git`. -/// For a linked worktree it returns the worktree-specific Git directory -/// (e.g. `/.git/worktrees/`). -pub fn resolve_git_dir(repo_root: &Path) -> Result { - let output = Command::new("git") - .args(["rev-parse", "--git-dir"]) - .current_dir(repo_root) - .output() - .with_context(|| { - format!( - "Failed to run git rev-parse --git-dir in '{}'", - repo_root.display() - ) - })?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); - return Err(anyhow!( - "git rev-parse --git-dir failed in '{}': {}", - repo_root.display(), - stderr - )); - } - - let git_dir_relative = String::from_utf8(output.stdout) - .with_context(|| "git rev-parse --git-dir emitted invalid UTF-8")? - .trim() - .to_string(); - - // `git rev-parse --git-dir` returns a path relative to the repo root - // (or an absolute path). Resolve it against the repo root. - let git_dir = PathBuf::from(&git_dir_relative); - if git_dir.is_absolute() { - Ok(git_dir) - } else { - Ok(repo_root.join(git_dir)) - } -} - -/// Reads an existing checkout ID from `/sce/checkout-id`. -/// -/// Returns `Ok(Some(id))` if the file exists and contains a valid checkout ID. -/// Returns `Ok(None)` if the file does not exist. -/// Returns an error if the file exists but cannot be read or contains invalid data. -pub fn read_checkout_id(git_dir: &Path) -> Result> { - let checkout_id_path = git_dir.join(SCE_CHECKOUT_DIR).join(CHECKOUT_ID_FILE); - - if !checkout_id_path.exists() { - return Ok(None); - } - - let content = std::fs::read_to_string(&checkout_id_path).with_context(|| { - format!( - "Failed to read checkout ID from '{}'", - checkout_id_path.display() - ) - })?; - - let id = content.trim().to_string(); - - if id.is_empty() { - return Err(anyhow!( - "Checkout ID file '{}' is empty", - checkout_id_path.display() - )); - } - - // Validate that the stored value is a valid UUIDv7. - Uuid::parse_str(&id).with_context(|| { - format!( - "Invalid checkout ID '{}' in '{}'", - id, - checkout_id_path.display() - ) - })?; - - Ok(Some(id)) -} - -/// Gets the existing checkout ID or creates a new one. -/// -/// If `/sce/checkout-id` already exists, returns the stored ID (idempotent). -/// If it does not exist, generates a new `UUIDv7`, writes it to the file, and returns it. -pub fn get_or_create_checkout_id(git_dir: &Path) -> Result { - if let Some(existing_id) = read_checkout_id(git_dir)? { - return Ok(existing_id); - } - - let checkout_id = Uuid::now_v7().to_string(); - - let checkout_dir = git_dir.join(SCE_CHECKOUT_DIR); - std::fs::create_dir_all(&checkout_dir).with_context(|| { - format!( - "Failed to create checkout directory '{}'", - checkout_dir.display() - ) - })?; - - let checkout_id_path = checkout_dir.join(CHECKOUT_ID_FILE); - std::fs::write(&checkout_id_path, &checkout_id).with_context(|| { - format!( - "Failed to write checkout ID to '{}'", - checkout_id_path.display() - ) - })?; - - Ok(checkout_id) -} diff --git a/cli/src/services/doctor/inspect.rs b/cli/src/services/doctor/inspect.rs index 92bb015e8..a07b0355c 100644 --- a/cli/src/services/doctor/inspect.rs +++ b/cli/src/services/doctor/inspect.rs @@ -4,7 +4,6 @@ use std::path::{Path, PathBuf}; use sha2::{Digest, Sha256}; use crate::services::agent_trace_db::lifecycle::diagnose_agent_trace_db_health; -use crate::services::checkout; use crate::services::codex_hook_config; use crate::services::codex_hook_policy::CodexHookPolicyReadiness; use crate::services::codex_hook_trust; @@ -24,12 +23,11 @@ use crate::services::setup::{ }; use super::types::{ - AgentTraceDbHealth, CheckoutIdentityHealth, DoctorFixResultRecord, DoctorProblem, - FileLocationHealth, FixResult, GlobalStateHealth, HookContentState, HookDoctorReport, - HookFileHealth, HookPathSource, IntegrationArea, IntegrationChildHealth, - IntegrationContentState, IntegrationGroupHealth, IntegrationGroupKey, IntegrationTarget, - PostCommitAutoSyncHealth, PostCommitAutoSyncState, ProblemCategory, ProblemFixability, - ProblemKind, ProblemSeverity, Readiness, + AgentTraceDbHealth, DoctorFixResultRecord, DoctorProblem, FileLocationHealth, FixResult, + GlobalStateHealth, HookContentState, HookDoctorReport, HookFileHealth, HookPathSource, + IntegrationArea, IntegrationChildHealth, IntegrationContentState, IntegrationGroupHealth, + IntegrationGroupKey, IntegrationTarget, PostCommitAutoSyncHealth, PostCommitAutoSyncState, + ProblemCategory, ProblemFixability, ProblemKind, ProblemSeverity, Readiness, }; use super::{is_executable, DoctorDependencies, DoctorMode, REQUIRED_HOOKS}; @@ -47,7 +45,6 @@ pub(super) fn build_report_with_lifecycle_problems( lifecycle_problems, codex_policy_readiness, ); - report.checkout_identity = collect_checkout_identity_health(repository_root); report.agent_trace_db = collect_agent_trace_db_health(repository_root, &mut report.problems); report.readiness = if report .problems @@ -69,7 +66,6 @@ fn build_report_without_service_owned_problem_checks( codex_policy_readiness: &CodexHookPolicyReadiness, ) -> HookDoctorReport { let global_state = collect_global_state_locations(repository_root, dependencies); - let checkout_identity = collect_checkout_identity_health(repository_root); let agent_trace_db = collect_agent_trace_db_health(repository_root, &mut problems); let git_available = (dependencies.check_git_available)(); @@ -157,7 +153,6 @@ fn build_report_without_service_owned_problem_checks( mode, readiness: Readiness::Ready, state_root: global_state.state_root, - checkout_identity, agent_trace_db, repository_root: detected_repository_root, hook_path_source, @@ -267,13 +262,6 @@ fn collect_global_state_locations( } } -fn collect_checkout_identity_health(repository_root: &Path) -> Option { - let git_dir = checkout::resolve_git_dir(repository_root).ok()?; - let checkout_id = checkout::read_checkout_id(&git_dir).ok()??; - - Some(CheckoutIdentityHealth { checkout_id }) -} - fn collect_agent_trace_db_health( repository_root: &Path, problems: &mut Vec, diff --git a/cli/src/services/doctor/render.rs b/cli/src/services/doctor/render.rs index e875e2786..405f67e55 100644 --- a/cli/src/services/doctor/render.rs +++ b/cli/src/services/doctor/render.rs @@ -776,9 +776,6 @@ fn render_report_json(execution: &DoctorExecution) -> Result { "canonical_identity": location.canonical_identity, "configured_remote": location.configured_remote, })), - "checkout_identity": report.checkout_identity.as_ref().map(|identity| json!({ - "checkout_id": identity.checkout_id, - })), "hook_path_source": match report.hook_path_source { HookPathSource::Default => "default", HookPathSource::LocalConfig => "local_config", diff --git a/cli/src/services/doctor/types.rs b/cli/src/services/doctor/types.rs index dc832b303..7d192b547 100644 --- a/cli/src/services/doctor/types.rs +++ b/cli/src/services/doctor/types.rs @@ -48,7 +48,6 @@ pub(super) struct HookDoctorReport { pub(super) mode: super::DoctorMode, pub(super) readiness: Readiness, pub(super) state_root: Option, - pub(super) checkout_identity: Option, pub(super) agent_trace_db: Option, pub(super) repository_root: Option, pub(super) hook_path_source: HookPathSource, @@ -77,11 +76,6 @@ pub(super) enum PostCommitAutoSyncState { NotApplicable, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub(super) struct CheckoutIdentityHealth { - pub(super) checkout_id: String, -} - #[derive(Clone, Debug, Eq, PartialEq)] pub(super) struct AgentTraceDbHealth { pub(super) label: &'static str, diff --git a/cli/src/services/mod.rs b/cli/src/services/mod.rs index 76d3c43ef..6bc6aad85 100644 --- a/cli/src/services/mod.rs +++ b/cli/src/services/mod.rs @@ -13,7 +13,6 @@ pub mod auth_db; #[allow(dead_code)] pub mod bash_policy; pub mod capabilities; -pub mod checkout; pub(crate) mod codex_hook_config; pub(crate) mod codex_hook_policy; pub(crate) mod codex_hook_trust; diff --git a/context/architecture.md b/context/architecture.md index f1a305309..52395de24 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -113,14 +113,14 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `cli/src/services/observability.rs` provides deterministic runtime observability controls and rendering for app lifecycle logs, including shared config-resolved threshold/format, explicit config-file/default `log_to_file`, and `log_dir` inputs with precedence `env > config file > defaults` for non-flag observability keys, stable event identifiers, severity filtering, the forced-emission warning path used for invalid discovered config startup diagnostics, normal logger records routed only to files when file logging is enabled and only error-level logger records routed to `stderr` when file logging is disabled, direct file-write diagnostics remaining on `stderr`, redaction-safe emission through the shared security helper, and log-directory writes with bounded retention. Config resolution also carries a positive config-file/default-only `log_file_retention_limit` (`10` by default) into startup observability config and `sce config show`; the concrete logger stores that resolved value and threads it through primary and v2 cleanup. When `log_dir` resolves from `SCE_LOG_DIR`, config, or the `/sce/logs` default, each enabled or forced log operation selects `/sce-.log` or `/sce--.log` using the machine-local date and optional logger session context, with deterministic percent-encoding for unsafe session filename bytes; after successfully writing a newly created selected file, retention keeps the configured number of newest direct regular `*.log` files by mtime plus path/name tie-break and fails open on cleanup errors. Its `observability::traits` submodule exposes the current `Logger` API with `Option<&str>` session context plus object-safe `Telemetry` trait boundaries and `NoopLogger`; the concrete observability logger and telemetry runtime still own behavior and implement those traits. `services::app_support::render_run_outcome` consumes the logger through that trait boundary when logging classified errors and stdout-write failures. - `cli/src/services/observability.rs` no longer owns duplicate log enums or parsing helpers; it consumes the canonical primitive seam from `cli/src/services/config/mod.rs` and stays focused on logger and telemetry runtime behavior. - `cli/src/cli_schema.rs` is now the canonical owner for top-level command metadata for the real clap-backed command set (`auth`, `config`, `setup`, `doctor`, `hooks`, `policy`, `sync`, `version`, `completion`), including the slim top-level help purpose text and per-command visibility on `sce`, `sce help`, and `sce --help`; `cli/src/command_surface.rs` remains the custom top-level help renderer and known-command classifier, adding the synthetic `help` row plus the ASCII banner while consuming that shared metadata instead of maintaining a parallel command catalog. -- `cli/src/services/default_paths.rs` is the canonical production path catalog for the CLI: it resolves config/state/cache roots with platform-aware XDG or `dirs` fallbacks through an internal `roots` seam, exposes named default paths for current persisted artifacts and database/log files (global config, auth tokens, auth DB, local DB, default observability log directory, and the sole Agent Trace DB path helper `agent_trace_db_path_for_repository` under `repos//agent-trace.db`; the former global-sentinel and per-checkout Agent Trace path helpers were removed by the `retire-legacy-agent-trace-db` plan), and owns canonical repo-relative, embedded-asset, install, hook, and context-path accessors so non-test production path definitions have one shared owner. Compile-time generated payload paths are owned by `build.rs` under `OUT_DIR`, not by the default-path catalog. Current production consumers such as config discovery, observability config resolution, doctor reporting, setup/install flows, database adapters, checkout identity, Agent Trace storage resolution, and local hook runtime path resolution consume this shared catalog rather than defining owned path literals in their own modules. +- `cli/src/services/default_paths.rs` is the canonical production path catalog for the CLI: it resolves config/state/cache roots with platform-aware XDG or `dirs` fallbacks through an internal `roots` seam, exposes named default paths for current persisted artifacts and database/log files (global config, auth tokens, auth DB, local DB, default observability log directory, and the sole Agent Trace DB path helper `agent_trace_db_path_for_repository` under `repos//agent-trace.db`; the former global-sentinel and per-checkout Agent Trace path helpers were removed by the `retire-legacy-agent-trace-db` plan), and owns canonical repo-relative, embedded-asset, install, hook, and context-path accessors so non-test production path definitions have one shared owner. Compile-time generated payload paths are owned by `build.rs` under `OUT_DIR`, not by the default-path catalog. Current production consumers such as config discovery, observability config resolution, doctor reporting, setup/install flows, database adapters, Agent Trace storage resolution, and local hook runtime path resolution consume this shared catalog rather than defining owned path literals in their own modules. - `cli/src/services/agent_trace.rs` is the Rust CLI owner for the SCE web base URL (`SCE_WEB_BASE_URL`) and exposes helpers for SCE-owned URL construction: Agent Trace conversation lookup URLs, persisted Agent Trace trace URLs, Agent Trace session URLs, and setup-created config schema URLs. Production Rust code should consume those helpers instead of repeating `sce.crocoder.dev` literals. The config resolver separately owns the `control_plane_base_url` runtime seam, whose baked `sce sync` default is `https://sce.crocoderlab.dev`; this control-plane host is not a web URL or schema owner. - `cli/src/services/config/mod.rs` is the config service facade and `sce config` orchestration surface (`show`, `validate`, `--help`), with bare `sce config` routed by `cli/src/app.rs` to the same help payload as `sce config --help`. Focused submodules own the implementation slices: `types.rs` owns shared config/runtime primitives, `schema.rs` owns generated schema embedding plus typed file parsing, `policy.rs` owns bash-policy semantic validation plus policy-specific formatting and runtime preset-catalog access for the Rust evaluator, `resolver.rs` owns deterministic config-file discovery, file-layer merging, explicit value precedence (`flags > env > config file > defaults` where flag-backed), shared auth-key resolution, observability-runtime resolution, attribution-hooks runtime gate and `agent_trace.auto_sync` resolution, database-retry config resolution and `DATABASE_RETRY_CONFIG` `OnceLock` initialization, default-discovered invalid-file degradation, strict invalid-discovered-layer errors for Agent Trace storage, and explicit-path fatal errors for `--config` / `SCE_CONFIG_FILE`, and private `render.rs` owns `sce config show` / `sce config validate` text and JSON output construction plus rendering-specific display-value helpers. The facade preserves existing `services::config` imports for startup/auth/hooks callers while delegating command execution to resolution plus rendering submodules. - `cli/src/services/output_format.rs` defines the canonical shared CLI output-format contract (`OutputFormat`) for supporting commands, with deterministic `text|json` parsing and command-scoped actionable invalid-value guidance. - `cli/src/services/config/types.rs` is the canonical owner for the shared runtime/config primitive seam used by the CLI: `LogLevel`, `LogFormat`, `SCE_LOG_LEVEL`, `SCE_LOG_FORMAT`, `SCE_LOG_DIR`, `DEFAULT_LOG_FILE_RETENTION_LIMIT`, and the shared bool parsing helpers used by both config resolution and observability bootstrap; `cli/src/services/config/mod.rs` re-exports those primitives through the facade. - `cli/src/services/capabilities.rs` defines the current broad CLI capability traits consumed by the borrowed, compile-time-typed `AppContext`: `FsOps` with `StdFsOps` for filesystem operations and `GitOps` with `ProcessGitOps` for git command execution plus repository-root/hooks-directory resolution. Existing service internals do not consume these traits directly yet; command execution uses narrow accessors and repo-root-scoped context derivation. - `cli/src/services/lifecycle.rs` defines the current compile-safe lifecycle seam. `ServiceLifecycle` has default no-op generic `diagnose`, `fix`, and `setup` methods over `C: HasRepoRoot`, with lifecycle-owned health, fix, and setup result types so the trait contract is not publicly anchored to doctor/setup module types or the full `AppContext` shape. The same module owns the static `LifecycleProvider` enum and shared `lifecycle_providers(include_hooks)` catalog/factory, returning providers in deterministic order (config → local_db → auth_db → agent_trace_db → hooks when requested); enum dispatch calls each concrete provider through generic context methods without boxed lifecycle-provider allocation or repo-root trait-object context erasure. Hooks exposes a `HooksLifecycle` provider in `cli/src/services/hooks/lifecycle.rs` for hook rollout diagnosis/fix/setup using lifecycle-owned health records plus the canonical required-hook installer. Config exposes a `ConfigLifecycle` provider in `cli/src/services/config/lifecycle.rs` for global/repo-local config validation and repo-local `.sce/config.json` bootstrap. local_db exposes a `LocalDbLifecycle` provider in `cli/src/services/local_db/lifecycle.rs` for canonical local DB path health, parent-directory readiness/bootstrap, and `LocalDb::new()` setup. auth_db exposes an `AuthDbLifecycle` provider in `cli/src/services/auth_db/lifecycle.rs` for canonical auth DB path health, parent-directory readiness/bootstrap, and `AuthDb::new()` setup. agent_trace_db exposes an `AgentTraceDbLifecycle` provider in `cli/src/services/agent_trace_db/lifecycle.rs` for setup-time repository-scoped Agent Trace storage initialization when a repo root is available and repository Agent Trace DB path health/fix from resolved repository identity, returning an actionable "requires a Git repository" diagnostic outside repository context (no global/checkout fallback path; the former fallback was removed by the `retire-legacy-agent-trace-db` plan). Doctor runtime aggregates the full provider catalog for `diagnose` and `fix` and adapts lifecycle records into doctor report/fix records at the orchestration boundary; setup command aggregates the shared catalog for `setup` with hooks included only when requested and adapts hook setup outcomes before rendering setup-owned messages. -- Agent Trace lifecycle setup resolves `agent_trace.repository_id` / `agent_trace.repository_remote`, creates/reuses checkout identity for diagnostics, and initializes the repository-scoped DB through `agent_trace_storage::resolve_agent_trace_storage(...)`; hook runtime uses the same storage resolver and `RepositoryAgentTraceDb` lazy fast-path-then-migrate open with bounded retry plus narrow migration-metadata repair for concurrent first-open races. +- Agent Trace lifecycle setup resolves `agent_trace.repository_id` / `agent_trace.repository_remote` and initializes the repository-scoped DB through `agent_trace_storage::resolve_agent_trace_storage(...)`; hook runtime uses the same storage resolver and `RepositoryAgentTraceDb` lazy fast-path-then-migrate open with bounded retry plus narrow migration-metadata repair for concurrent first-open races. - `cli/src/services/auth_command/mod.rs` defines the implemented auth command surface for `sce auth login|logout|whoami`, including device-flow login, stored-credential validation/renewal through login with device-flow fallback, idempotent logout state reporting, and Control Plane `/me`-backed whoami rendering in text/JSON formats; text mode uses flat `Email`, `First Name`, `Last Name`, `Role`, `Permissions`, and `Organization Name` labels, with optional names and missing role/permissions/workspace values handled deterministically. Missing credentials from logout/whoami are successful state queries with documented text/JSON reports; authenticated Control Plane failures are classified at this boundary as typed `CliError::User` entries (`NotAuthenticated` for authentication failures, `AuthStorageUnavailable` for token-storage plus `AuthError::Io`/`Storage` failures, and `UnexpectedFailure` for approved user-facing rendering/prompt fallbacks), with technical sources retained for observability; remaining auth-command failures also use `UnexpectedFailure` rather than a separate runtime mapping. Stored-credential login applies that classification inside `run_login_with_stored_credentials` and its renewal/device-login call path before returning to the command caller. Logged-out text returns exact state-aware guidance, unauthenticated whoami returns its documented state report, and renewal reports retain the `login` operation label. `cli/src/services/auth_command/command.rs` owns the `AuthCommand` payload used by the static `RuntimeCommand` enum. There is no public renewal or status subcommand. - `cli/src/services/db/mod.rs` provides the shared generic Turso infrastructure seam: `DbSpec` supplies a service-specific name, path, ordered embedded migrations, and config-file lookup key (`db_config_key()`), while `TursoDb` owns parent-directory creation, `Builder::new_local(...)` initialization (with `experimental_multiprocess_wal(true)` for safe concurrent access), Turso connection setup, tokio current-thread runtime bridging, retry-backed blocking `execute`/`query`/`query_values`/`query_map` wrappers, and generic migration execution with per-database `__sce_migrations` metadata. `TursoDb::new()` and `EncryptedTursoDb::new()` wrap only their local open/connect block in `run_with_retry_sync` using a config-driven connection-open policy resolved from the `DATABASE_RETRY_CONFIG` `OnceLock` with fallback to hardcoded defaults, while operation methods use a config-driven operation policy from the same source. `query_values()` returns fully fetched column names plus raw `turso::Value` rows for deterministic operator-facing rendering; `query_map()` retries the initial query and row-fetch loop, then applies caller row mapping after retry completion. Migration execution is not retried and uses batch execution so one migration file may contain multiple SQL statements while still recording one migration ID. The same module also provides `EncryptedTursoDb`, a structurally parallel encrypted adapter that resolves the encryption key through `encryption_key::get_or_create_encryption_key()`, enables Turso local encryption with strict `aegis256` cipher selection, and exposes retry-backed synchronous wrappers plus migration execution. `cli/src/services/db/encryption_key.rs` first derives a Turso-compatible 64-character hex key from non-empty `SCE_AUTH_DB_ENCRYPTION_KEY` env-secret text when present, otherwise falls back to keyring-backed credential-store get-or-create behavior; no plaintext auth DB fallback exists. - `cli/src/services/local_db/mod.rs` provides the concrete local DB spec and `LocalDb` type alias over the shared generic `TursoDb` adapter. `LocalDbSpec` resolves the deterministic persistent runtime DB target through the shared default-path seam and declares no local migrations; `TursoDb` supplies retry-backed blocking `execute`/`query`, parent-directory creation, Turso connection setup, tokio current-thread runtime bridging, and generic migration execution. @@ -129,7 +129,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `cli/src/services/setup/mod.rs` defines the setup command contract (`SetupMode`, `SetupTarget`, `SetupRequest`, CLI flag parser/validator), an `inquire`-backed interactive target prompter (`InquireSetupTargetPrompter`), setup dispatch outcomes (proceed/cancelled), additive durable-context bootstrap (`bootstrap_context_baseline` for standalone `--bootstrap-context` and every normal successful setup path), and compile-time embedded asset access (`EmbeddedAsset`, target-scoped iterators, required-hook asset iterators/lookups). It also owns the install-time optional-workflow seam: the `OptionalWorkflow` type plus the build-generated `OPTIONAL_WORKFLOWS` catalog, a per-target `WorkflowAssetLayout` built from the existing `default_paths` command/skill directory constants (`command`/`commands`/`prompts` plus `skills`), and `iter_embedded_assets_for_setup_target_with_selection(target, selection)`, which yields every embedded asset except the `{command_dir}/{command_slug}.md` file and `{skills_dir}/{skill_slug}/` subtree of each optional workflow the selection omits. Membership is derived from the catalog's slugs rather than an enumerated file list, so a new optional workflow needs no Rust change. This filtered iterator is the only way embedded assets are enumerated; setup installs through it and doctor inspects through it, so there is no unfiltered enumeration path that could reintroduce an unselected workflow. The non-interactive selection flows through the repeatable `--workflow ` flag into `SetupRequest.optional_workflows: Option>` (`None` meaning the flag was absent); `validate_optional_workflow_slugs` checks each slug against `OPTIONAL_WORKFLOWS` during request resolution, before any write, and `run_setup_for_mode` resolves `None` to the persisted `integrations.optional_workflows` (exported as `persisted_optional_workflows`) before installing through the filtered iterator and persisting the resolved selection. The interactive selection flows through the same seam: `SetupTargetPrompter` carries `prompt_target` plus `prompt_optional_workflows(defaults)` (returning `None` for a cancelled prompt), `SetupDispatch::Proceed { mode, optional_workflows }` carries a prompted selection alongside the resolved mode, and `resolve_setup_dispatch(mode, prompter, defaults)` runs the workflow prompt only after an interactive target prompt, mapping either cancellation to `SetupDispatch::Cancelled`. The prompt module builds its `inquire::MultiSelect` from `optional_workflow_prompt_inputs(catalog, defaults)`, which returns `None` for an empty catalog (skipping the prompt) and otherwise catalog-ordered rows plus the indices to pre-check, ignoring ids absent from the catalog. `setup/command.rs` therefore resolves the repository root before dispatch, mapping the setup domain's typed positive non-Git classification to `setup.not_git_repository` and other resolution failures to `general.unexpected_failure`, so a non-Git directory fails before any prompt without CLI-layer string matching. For repository builds, `cli/build.rs` validates the `SCE_CLI_GENERATED_INPUT_DIR` payload and canonical-input inventories, copies the payload into Cargo `OUT_DIR/pkl-generated`, stages `cli/assets/hooks/**` under `OUT_DIR/static`, requires the staged `config/optional-workflows.json`, and generates both the setup manifest and the optional-workflow catalog (`optional_workflows.rs`, rejecting a manifest whose `schemaVersion` is not 1 or whose entries lack a non-empty `id`/`title`/`description`/`commandSlug`/`skillSlug`) in `OUT_DIR`; focused internal seams separate install-flow from prompt-flow logic; `cli/src/services/setup/command.rs` owns the `SetupCommand` payload used by the static `RuntimeCommand` enum and executes against any context implementing repo-root scoping. Its install engine/orchestrator (`install_assets_for_concrete_target_with_rename`) installs each embedded asset individually: it stages the asset's content next to its final destination and swaps it into place by renaming the staging file directly over the destination — never unlinking the destination first, since `fs::rename` already replaces an existing file atomically — with deterministic recovery guidance naming the failing asset's path on swap failure (the pre-existing destination content, if any, is untouched) and no backup artifact creation; it never removes an integration target directory as a whole, so files a repository owns inside `.opencode`/`.claude`/`.pi` — including nested inside an SCE-owned subdirectory such as `skills/` or `commands/` — survive a setup run; Codex has no single target directory this way, since `CODEX_EMBEDDED_ASSETS` relative paths already carry their own `.agents/`/`.codex/` prefix and `InstallTargetPaths::codex_target_dir()` resolves to the repository root itself, but the same per-asset stage/atomic-swap and never-delete-what-it-did-not-author guarantees still apply file by file. For the three assets that are merge targets — the Claude target's `settings.json`, the OpenCode target's `opencode.json`, and Codex's user-owned `.codex/hooks.json` — the content staged is not always the embedded asset's bytes. Claude and OpenCode use `cli/src/services/setup/config_merge.rs`, while Codex uses the shared `cli/src/services/codex_hook_config.rs` service for structural validation and canonical-registration merging. For the Claude and OpenCode targets, the content staged is the result of `cli/src/services/setup/config_merge.rs::merge_or_create_claude_settings`/`merge_or_create_opencode_config(existing_bytes, generated_bytes, source_path)`: each returns the generated document verbatim when no file exists yet, otherwise parses the existing file as JSON (a parse failure is a hard error naming `source_path`, and nothing is written) and merges it with a pure per-shape function that copies `$schema` from the generated document and preserves every other key from the existing file untouched. `merge_claude_settings` replaces, per hook event key the generated document declares, only the entries whose command contains the ownership marker `run-sce-or-show-install-guidance.sh`, preserving every event key and non-SCE hook entry from the existing file. `merge_opencode_config` merges the `plugin` array as a set: existing entries whose path starts with the ownership marker `./plugins/sce-` are dropped structurally — so a stale plugin path an older or renamed catalog once installed is still recognized and pruned even after the current generated document stops declaring it — and the generated document's `plugin` entries are appended after the surviving entries. Codex's merge validates the document shape, recognizes ownership only when both the generated helper path and the `sce hooks codex` command contract are present, and replaces stale or duplicate SCE handlers with exactly one current handler for each `UserPromptSubmit`, `Stop`, `PreToolUse/Bash`, and `PostToolUse/apply_patch` registration while preserving unrelated valid Codex fields, groups, and handlers. After that per-asset install loop, `prune_stale_assets_for_concrete_target` deletes every path the full embedded catalog for the concrete target claims but the resolved selection did not install (a deselected optional workflow, or an asset a newer catalog renamed or dropped), and `remove_empty_ancestor_directories` removes any parent directory left empty by that deletion, stopping at the target root or at a directory that still holds something such as a user file. It formats deterministic completion messaging; required-hook install orchestration (`install_required_git_hooks`, backed by the rename-injectable `install_required_git_hooks_with_rename`) is a third content-computation seam alongside the two JSON merge targets: `install_single_required_hook_with_rename` computes the bytes to stage with `cli/src/services/setup/hook_merge.rs::merge_or_create_hook(existing_bytes, canonical_bytes, hook_name)` rather than writing `hook_asset.bytes` verbatim — a foreign hook (no SCE managed block, no legacy guidance-URL marker) is kept as an exact byte prefix with the canonical block appended after it, an SCE-owned hook has only its block spliced in place or left unchanged, and a legacy pre-marker hook is replaced wholesale — then follows the same per-file stage/atomic-swap choreography as config-asset install: the staging file is renamed directly over the existing hook without unlinking it first, so a rename failure leaves the prior hook's bytes and executable bit intact, with deterministic recovery guidance on swap failure. `Installed`/`Updated`/`Skipped` are decided against the merged bytes plus the executable bit rather than the canonical asset's raw bytes, so an already-current foreign-plus-block hook reports `Skipped`; `RequiredHookInstallResult.unreachable_block_advisory` is set, and rendered as a named advisory line in setup's hook output, when an appended block follows a foreign hook's zero-indent `exec`/`exit` and so would never run. After the Git gate, setup always ensures the context baseline; context-only requests return there, while normal modes derive a repo-root-scoped context before aggregating static lifecycle provider `setup` dispatch across providers (config → local_db → auth_db → agent_trace_db → hooks when requested), so setup providers consume only repo-root access from the scoped context. - `cli/src/services/setup/mod.rs` keeps those responsibilities inside one file for now, but the current ownership split is explicit: the inline `install` module owns repository-path normalization, staging/swap install behavior, required-hook installation, and filesystem safety guards, while the inline `prompt` module owns interactive target selection and prompt styling. - `cli/src/services/security.rs` provides shared security utilities for deterministic secret redaction (`redact_sensitive_text`) and directory write-permission probes (`ensure_directory_is_writable`) used by app/setup/observability surfaces. -- `cli/src/services/doctor/mod.rs` owns the current doctor request/report surface while focused submodules (`doctor/inspect.rs`, `doctor/render.rs`, `doctor/fixes.rs`, `doctor/types.rs`) split report fact collection, rendering, manual fix reporting, and doctor-owned domain types into smaller seams; `cli/src/services/doctor/command.rs` owns the `DoctorCommand` payload used by the static `RuntimeCommand` enum and executes against any context implementing repo-root scoping. Runtime doctor execution resolves a repository root, derives a scoped context, requests the shared static lifecycle provider catalog with hooks included for service-owned `diagnose` and `fix` behavior, adapts lifecycle-owned health/fix records into doctor-owned problem/fix records, and then renders stable text/JSON problem records with category/severity/fixability/remediation fields plus deterministic fix-result reporting in fix mode. Agent Trace database inspection is no longer a doctor-adjacent command surface; doctor owns repository-scoped DB health and checkout identity facts, while `sce sync` owns control-plane synchronization. Report fact collection preserves environment/repository/hook/integration display data and adds a non-launching `post_commit_auto_sync` fact based on canonical post-commit managed-block currency plus resolved `agent_trace.auto_sync` source/value; this fact does not launch `sce sync`, and existing hook problem/remediation/readiness semantics remain authoritative. Service-owned lifecycle providers own config validation, local DB and repository-scoped Agent Trace DB readiness/bootstrap, and hook rollout diagnosis/repair. Integration inspection in `doctor/inspect.rs` is scoped twice over: `resolve_doctor_integration_targets` picks which targets to inspect, and `persisted_optional_workflows` (reused from setup) resolves which optional workflows the repository selected, which the OpenCode/Claude/Pi/Codex child collectors apply through `iter_embedded_assets_for_setup_target_with_selection`. An unselected optional workflow therefore contributes no expected children at all, so no row and no missing/mismatch problem can be produced for it, while a selected one keeps the unchanged presence and content-hash checks. Codex's collector (`collect_codex_integration_groups`) is the one exception to the single-target-directory shape the other three share: since `CODEX_EMBEDDED_ASSETS` relative paths already carry their own `.agents/`/`.codex/` prefix, it resolves its integration root through `InstallTargetPaths::codex_target_dir()` (the repository root itself) rather than a `RepoPaths` per-target subdirectory, and splits assets into `Skills` (`.agents/skills/` prefix) and `Hooks` (`.codex/` prefix) groups instead of Pi's `Extensions`/`Prompts`/`Skills` split. Its `.codex/hooks.json` reporting is per-registration rather than one whole-file child: `codex_hook_config::diagnose_document` classifies each of the four required registrations as `PresentAndCurrent`/`Missing`/`Stale` (or the whole document as `Malformed` when it cannot be structurally validated) without writing anything, so unrelated user handlers never create a false whole-document mismatch. For a structurally current registration, `codex_hook_trust` separately reads (never writes) Codex's own durable `$CODEX_HOME/config.toml` hook-trust state — reproducing upstream's `hook_hash`/`hook_key`/`hook_trust_status` exactly — and reports `Trusted`/`Untrusted`/`Modified`/`Disabled`/`Unknown`; only `Trusted` renders healthy. `sce doctor --fix` repairs a structurally unhealthy `.codex/hooks.json` through the existing merge-install path, but a registration that is current yet not-yet-trusted is never "fixed", since SCE cannot grant Codex hook trust. +- `cli/src/services/doctor/mod.rs` owns the current doctor request/report surface while focused submodules (`doctor/inspect.rs`, `doctor/render.rs`, `doctor/fixes.rs`, `doctor/types.rs`) split report fact collection, rendering, manual fix reporting, and doctor-owned domain types into smaller seams; `cli/src/services/doctor/command.rs` owns the `DoctorCommand` payload used by the static `RuntimeCommand` enum and executes against any context implementing repo-root scoping. Runtime doctor execution resolves a repository root, derives a scoped context, requests the shared static lifecycle provider catalog with hooks included for service-owned `diagnose` and `fix` behavior, adapts lifecycle-owned health/fix records into doctor-owned problem/fix records, and then renders stable text/JSON problem records with category/severity/fixability/remediation fields plus deterministic fix-result reporting in fix mode. Agent Trace database inspection is no longer a doctor-adjacent command surface; doctor owns repository-scoped DB health (checkout identity facts were removed by the `remove-checkout-id` plan), while `sce sync` owns control-plane synchronization. Report fact collection preserves environment/repository/hook/integration display data and adds a non-launching `post_commit_auto_sync` fact based on canonical post-commit managed-block currency plus resolved `agent_trace.auto_sync` source/value; this fact does not launch `sce sync`, and existing hook problem/remediation/readiness semantics remain authoritative. Service-owned lifecycle providers own config validation, local DB and repository-scoped Agent Trace DB readiness/bootstrap, and hook rollout diagnosis/repair. Integration inspection in `doctor/inspect.rs` is scoped twice over: `resolve_doctor_integration_targets` picks which targets to inspect, and `persisted_optional_workflows` (reused from setup) resolves which optional workflows the repository selected, which the OpenCode/Claude/Pi/Codex child collectors apply through `iter_embedded_assets_for_setup_target_with_selection`. An unselected optional workflow therefore contributes no expected children at all, so no row and no missing/mismatch problem can be produced for it, while a selected one keeps the unchanged presence and content-hash checks. Codex's collector (`collect_codex_integration_groups`) is the one exception to the single-target-directory shape the other three share: since `CODEX_EMBEDDED_ASSETS` relative paths already carry their own `.agents/`/`.codex/` prefix, it resolves its integration root through `InstallTargetPaths::codex_target_dir()` (the repository root itself) rather than a `RepoPaths` per-target subdirectory, and splits assets into `Skills` (`.agents/skills/` prefix) and `Hooks` (`.codex/` prefix) groups instead of Pi's `Extensions`/`Prompts`/`Skills` split. Its `.codex/hooks.json` reporting is per-registration rather than one whole-file child: `codex_hook_config::diagnose_document` classifies each of the four required registrations as `PresentAndCurrent`/`Missing`/`Stale` (or the whole document as `Malformed` when it cannot be structurally validated) without writing anything, so unrelated user handlers never create a false whole-document mismatch. For a structurally current registration, `codex_hook_trust` separately reads (never writes) Codex's own durable `$CODEX_HOME/config.toml` hook-trust state — reproducing upstream's `hook_hash`/`hook_key`/`hook_trust_status` exactly — and reports `Trusted`/`Untrusted`/`Modified`/`Disabled`/`Unknown`; only `Trusted` renders healthy. `sce doctor --fix` repairs a structurally unhealthy `.codex/hooks.json` through the existing merge-install path, but a registration that is current yet not-yet-trusted is never "fixed", since SCE cannot grant Codex hook trust. - `cli/src/services/version/mod.rs` defines the version command parser/rendering contract (`parse_version_request`, `render_version`) with deterministic text output and stable JSON runtime-identification fields; `cli/src/services/version/command.rs` owns the `VersionCommand` payload used by the static `RuntimeCommand` enum. - `cli/src/services/completion/mod.rs` defines completion parser/rendering contract (`parse_completion_request`, `render_completion`) with deterministic Bash/Zsh/Fish script output aligned to current parser-valid command/flag surfaces; `cli/src/services/completion/command.rs` owns the `CompletionCommand` payload used by the static `RuntimeCommand` enum. - `cli/src/services/hooks/mod.rs` defines the current local hook runtime parsing/dispatch (`HookSubcommand`, `run_hooks_subcommand`), including the silent `claude-model-state` lifecycle intake delegated to `cli/src/services/hooks/claude_model_state.rs`, plus a commit-msg co-author policy seam (`apply_commit_msg_coauthor_policy`) that injects one canonical SCE trailer only when the enabled-by-default attribution-hooks config/env control is not opted out, `SCE_DISABLED` is false, and the staged-diff AI-overlap preflight confirms AI/editor evidence (`StagedDiffAiOverlapResult::Overlap`); the preflight is wired into `run_commit_msg_subcommand_in_repo` and logs `sce.hooks.commit_msg.ai_overlap_error` on error paths; `cli/src/services/hooks/command.rs` owns the `HooksCommand` payload used by the static `RuntimeCommand` enum. In the current attribution-only baseline, `pre-commit` and `post-rewrite` are deterministic no-op surfaces; `post-commit` requires validated `--remote-url`, threads that URL through the Agent Trace flow, prints it to stderr, and remains an active intersection + Agent Trace persistence entrypoint (captures current commit patch, queries recent repository-level `diff_traces` from the bounded past-7-days window, combines valid patches via `patch::combine_patches`, intersects with post-commit patch via `patch::intersect_patches`, persists result to `post_commit_patch_intersections`, then persists built Agent Trace payloads with range-level `content_hash` values to `agent_traces` in the repository-scoped Agent Trace DB without post-commit file artifacts); after successful validation and persistence, the setup-enabled config-file-only `agent_trace.auto_sync` gate launches one detached sync-owned `sync --format json` child when its resolved value is true (a newly created setup config supplies the explicit `true`; omitted configuration resolves to `false`), with launcher failures ignored and no high-frequency hook trigger; `diff-trace` performs STDIN JSON intake, validates required non-empty `sessionID`/`diff`/`tool_name`, optional `model_id` (absent/`null` → `None`), required nullable/non-empty `tool_version` plus required `u64` `time` (Unix epoch milliseconds), rejects values that cannot fit signed `time_ms` storage, prefixes the stored `diff_traces.session_id` before insert construction (`oc_` for OpenCode, `cc_` for Claude, `pi_` for Pi, same-tool idempotent), and inserts the parsed payload fields into `RepositoryAgentTraceDb` without creating a parsed-payload `context/tmp` artifact; Claude structured `PostToolUse` diff-trace intake resolves model attribution with `direct > exact transcript > exact session/agent state > bridge-derived chain state > NULL`: direct top-level or nested metadata wins, otherwise the event's `transcript_path` is scanned for the assistant envelope whose `tool_use.id` matches `tool_use_id`, then persistence performs one exact local `claude_model_state` lookup using canonical session and ephemeral agent scope. Only for a raw structured payload in the exact main-session scope, a state miss then resolves the newest `claude_model_state` observation across the transcript's bridge-linked chain members (bounded leading-record reads, one exact-scope read per member, winner by `observed_at_ms`) and, on a hit, writes a `claude_model_state` row for the current session (`source="bridge_inherited"`) before using that model — the only diff-trace resolution path that writes state. Model values normalize once through `claude/`, subagents do not inherit main-session state, and unresolved values remain nullable. `session-model` is no longer a supported hook route. @@ -140,7 +140,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `sce sync [--format text|json]` is implemented: `cli/src/services/sync/sync.rs` resolves repository-scoped Agent Trace storage, authenticates against the control plane with stored WorkOS credentials, uses the config-resolved `control_plane_base_url` with baked default `https://sce.crocoderlab.dev`, calls the ingestion `/state` endpoint once, then starts the `messages`/`parts`/`diff_traces`/`agent_traces` capture-stream state machines concurrently via `AgentTraceExportReader` and a shared per-stream reconciliation engine. Batches and cursor refreshes remain sequential within each stream, while fixed stream order is retained for final and stream-completion reporting; `cli/src/services/sync/render_sync.rs` renders the converged `AgentTraceSyncReport` as concise per-stream text or `camelCase` JSON without a nested subcommand field (see `context/cli/sync-command.md`). Local DB bootstrap and setup-time repository-scoped Agent Trace DB initialization otherwise still flow through lifecycle providers aggregated by setup, while repository-scoped DB health/repair flows through the doctor surface. The former trace database inspection and nested sync surfaces are unavailable. - `cli/src/services/patch.rs` defines the standalone patch domain model (`ParsedPatch`, `PatchFileChange`, `FileChangeKind`, `PatchHunk`, `TouchedLine`, `TouchedLineKind`) for in-memory parsed unified-diff representation, capturing only touched lines (added/removed) plus minimal per-file/per-hunk metadata while excluding non-hunk headers and unchanged context lines. All types are `serde`-serializable/deserializable with `snake_case` JSON field naming. The module also provides `parse_patch`, a public parser function that converts raw unified-diff text (both `Index:` SVN-style and `diff --git` git-style formats) into `ParsedPatch` structs, with `ParseError` for actionable malformed-input diagnostics. Storage-agnostic JSON load helpers (`load_patch_from_json` for string input, `load_patch_from_json_bytes` for byte input) reconstruct `ParsedPatch` from serialized JSON content with `PatchLoadError` for actionable deserialization diagnostics. Its patch-set operations now include deterministic ordered combination plus target-shaped intersection that prefers exact touched-line matches and falls back to historical `kind`+`content` matching when incremental diffs and canonical post-commit diffs have drifted line numbers; `parse_patch`, `combine_patches`, and `intersect_patches` are consumed by the active post-commit hook runtime. - `cli/src/services/structured_patch.rs` defines the synchronous structured editor-hook derivation seam. It derives Claude `PostToolUse` `Write` structured-update hunks, `Write` `tool_input.content` create fallback, and `Edit` structured-patch payloads into canonical `ParsedPatch` values plus Claude session/tool metadata, returning deterministic skip reasons for unsupported events/tools/payload shapes. The module is pure and side-effect-free. It is wired into `sce hooks diff-trace` for Claude payload classification at intake (T04) and into `RepositoryAgentTraceDb::recent_diff_trace_patches` for post-commit structured payload parsing dispatch at read time (T05). -- `cli/src/services/` contains module boundaries for command_registry, lifecycle, auth_command, config, setup, doctor, hooks, checkout identity, bash_policy, version, completion, help, patch, sync, SCE web URL helpers, shared database infrastructure, local DB adapters, encrypted auth DB adapters, and Agent Trace DB adapters with explicit trait seams for future implementations. `cli/src/services/checkout/` owns checkout ID file infrastructure (the per-checkout Agent Trace DB opener/path helper was removed by the `retire-legacy-agent-trace-db` plan); active setup/hooks use `agent_trace_storage` to establish checkout identity as diagnostics and initialize/open the repository-scoped DB, while `sce doctor` surfaces checkout identity facts plus credential-safe repository Agent Trace DB metadata. There is no checkout-scoped discovery or former trace inspection surface; any pre-migration `agent-trace-*.db` files on disk are never touched and no longer inspectable via the CLI. `cli/src/services/bash_policy.rs` owns both the CLI-agnostic evaluator logic and the hidden `sce policy bash` command adapter used by OpenCode, Claude, and Pi callers; Codex delegates in-process to the same evaluator for its native `PreToolUse(Bash)` response. `cli/src/services/command_registry.rs` defines the static `RuntimeCommand` enum, deterministic `CommandRegistry` name catalog, and `build_default_registry()` function for command dispatch metadata. Service-owned command modules own the runtime command payload structs for help/help-text, version, completion, auth, config, setup, doctor, hooks, policy, and sync. +- `cli/src/services/` contains module boundaries for command_registry, lifecycle, auth_command, config, setup, doctor, hooks, bash_policy, version, completion, help, patch, sync, SCE web URL helpers, shared database infrastructure, local DB adapters, encrypted auth DB adapters, and Agent Trace DB adapters with explicit trait seams for future implementations. `cli/src/services/checkout/` (checkout ID file infrastructure) was removed by the `remove-checkout-id` plan, alongside the per-checkout Agent Trace DB opener/path helper removed earlier by the `retire-legacy-agent-trace-db` plan; active setup/hooks initialize/open the repository-scoped DB through `agent_trace_storage` with no checkout-identity step, and `sce doctor` no longer surfaces checkout identity facts, only credential-safe repository Agent Trace DB metadata. There is no checkout-scoped discovery or former trace inspection surface; any pre-migration `agent-trace-*.db` files on disk are never touched and no longer inspectable via the CLI. `cli/src/services/bash_policy.rs` owns both the CLI-agnostic evaluator logic and the hidden `sce policy bash` command adapter used by OpenCode, Claude, and Pi callers; Codex delegates in-process to the same evaluator for its native `PreToolUse(Bash)` response. `cli/src/services/command_registry.rs` defines the static `RuntimeCommand` enum, deterministic `CommandRegistry` name catalog, and `build_default_registry()` function for command dispatch metadata. Service-owned command modules own the runtime command payload structs for help/help-text, version, completion, auth, config, setup, doctor, hooks, policy, and sync. - `cli/README.md` is the crate-local onboarding and usage source of truth for placeholder behavior, safety limitations, and roadmap mapping back to service contracts. - `flake.nix` applies `rust-overlay` (`oxalica/rust-overlay`) to nixpkgs, pins `rust-bin.stable.1.95.0.default` with `rustfmt` + `clippy`, and reads package/check version from repo-root `.version`. Its `cliGeneratedInput` derivation invokes the shared generated-input producer from a declarative source containing the producer and canonical inputs, then publishes the producer-validated handoff as its store output. Native, release, test, and Clippy Cargo derivations receive that same store path through `SCE_CLI_GENERATED_INPUT_DIR`; their Cargo environments exclude Pkl and assert that it is unavailable. Repository-mode `cli/build.rs` validates the handoff before copying it into `OUT_DIR`; published crates use the validated packaging-only fallback. The build script stages SQL under `OUT_DIR/static/migrations` and writes `OUT_DIR/generated_migrations.rs` with deterministic migration constants sorted by numeric filename prefix. - Crane dependency-only derivations and `cli-fmt` intentionally do not receive `SCE_CLI_GENERATED_INPUT_DIR`, so canonical generation changes invalidate the producer and compiling derivations while preserving host/musl dependency artifacts and formatting. The root flake runs `cli-tests`, `cli-clippy`, and `cli-fmt` through Crane-backed paths and exposes `cli-generated-input` as the focused payload/inventory integrity check. It also exposes directory-scoped JS validation derivations for `npm/` and `config/lib/`, while `pkl-generated` uses a narrow canonical-input source set plus maybe-missing forbidden paths so reintroduced generated repository artifacts fail the check. diff --git a/context/cli/agent-trace-storage.md b/context/cli/agent-trace-storage.md index d64f39c53..998696230 100644 --- a/context/cli/agent-trace-storage.md +++ b/context/cli/agent-trace-storage.md @@ -1,11 +1,11 @@ # Repository-scoped Agent Trace storage resolver -Module at `cli/src/services/agent_trace_storage/` (T04 of the `repository-scoped-agent-trace-db` plan) that resolves the active Agent Trace database for a Git repository checkout under the target invariant: one logical Git repository = one Agent Trace database at `/sce/repos//agent-trace.db`. Clones and linked worktrees of the same logical repository resolve to the same database path while keeping distinct checkout IDs. +Module at `cli/src/services/agent_trace_storage/` (T04 of the `repository-scoped-agent-trace-db` plan) that resolves the active Agent Trace database for a Git repository checkout under the target invariant: one logical Git repository = one Agent Trace database at `/sce/repos//agent-trace.db`. Clones and linked worktrees of the same logical repository resolve to the same database path. ## Public API - `AgentTraceStorageContext { repository_root, explicit_repository_id, repository_remote }` — borrowed inputs mirroring the `agent_trace.repository_id` / `agent_trace.repository_remote` config keys; callers pass already-resolved configuration values. Active hook runtime and Agent Trace lifecycle setup/health resolve these values through the config service before constructing the storage context. -- `ResolvedAgentTraceStorage { repository_identity, checkout_id, db_path, db, metadata }` — resolved repository identity (`ResolvedRepositoryIdentity` including source provenance), the checkout ID for diagnostics (never persisted on Agent Trace rows), the repository-scoped DB path, the open `RepositoryAgentTraceDb`, and the typed `RepositoryMetadata { repository_id, source_instance_id }` produced by the same verification/initialization call that opened `db` (see [../sce/agent-trace-db.md](../sce/agent-trace-db.md) for the `source_instance_id` identity contract). +- `ResolvedAgentTraceStorage { repository_identity, db_path, db, metadata }` — resolved repository identity (`ResolvedRepositoryIdentity` including source provenance), the repository-scoped DB path, the open `RepositoryAgentTraceDb`, and the typed `RepositoryMetadata { repository_id, source_instance_id }` produced by the same verification/initialization call that opened `db` (see [../sce/agent-trace-db.md](../sce/agent-trace-db.md) for the `source_instance_id` identity contract). - `resolve_agent_trace_storage(context)` — setup/lifecycle production entrypoint using the canonical state root from the default-path catalog; may run migrations, including migration `002`. - `resolve_agent_trace_storage_at_state_root(context, state_root)` — setup/lifecycle resolution core against an explicit state root; used by tests to exercise the full path without touching the real user state directory. - `resolve_agent_trace_storage_for_hook_runtime(context)` — hook-runtime production entrypoint using the canonical state root; never runs migrations, including migration `002`. @@ -14,9 +14,8 @@ Module at `cli/src/services/agent_trace_storage/` (T04 of the `repository-scoped ## Resolution flow 1. Agent Trace storage runtime config is resolved through the config service; invalid default-discovered config layers are skipped with the shared resolver's remaining values and validation diagnostics, while explicit `--config` / `SCE_CONFIG_FILE` failures remain fatal. Repository identity then uses `repository_identity::resolve` precedence (explicit config ID → configured remote URL, default `origin`); resolution errors carry `.sce/config.json` guidance and never echo URLs. A failed config or identity resolution creates no state directories. -2. Checkout identity reuse via `checkout::resolve_git_dir` + `get_or_create_checkout_id` (`/sce/checkout-id`). -3. DB path from `default_paths::agent_trace_db_path_for_repository{,_at}`, which rejects empty or path-unsafe repository IDs (separators, `.`, `..`). -4. DB open splits by caller through `agent_trace_db::repository::RepositoryAgentTraceDb`, sharing steps 1–3 through an internal `open_storage_with` helper parameterized by the DB-opener: +2. DB path from `default_paths::agent_trace_db_path_for_repository{,_at}`, which rejects empty or path-unsafe repository IDs (separators, `.`, `..`). +3. DB open splits by caller through `agent_trace_db::repository::RepositoryAgentTraceDb`, sharing steps 1–2 through an internal `open_storage_with` helper parameterized by the DB-opener: - Setup/lifecycle (`resolve_agent_trace_storage{,_at_state_root}`): a bounded concurrent-safe fast-path-then-migrate sequence — `open_without_migrations_at` + `ensure_schema_ready_for_hooks()` + `verify_or_initialize_repository_metadata(repository_id)`, falling back to migration-running `new_at` plus the same metadata validation. Directory creation rides on `TursoDb` parent-dir `create_dir_all`. If concurrent first open leaves all repository schema tables present but the one-file baseline migration record missing, the repository adapter records that missing metadata before rechecking readiness; otherwise the resolver retries the open/migrate sequence for a bounded window while SQLite/Turso locks clear. - Hook runtime (`resolve_agent_trace_storage_for_hook_runtime{,_at_state_root}`): `open_for_hooks_without_migrations_at` + `ensure_schema_ready_for_hooks()`, applying the same narrow concurrent-first-open metadata repair on failure but never falling back to `new_at`/migrations. A missing or baseline-only (pre-`002`) database fails with `ensure_schema_ready_for_hooks`'s existing `Run 'sce setup'.` guidance. Once readiness passes, `verify_or_initialize_repository_metadata(repository_id)` initializes `source_instance_id` when needed, matching setup's result for an already-set-up repository. `sce hooks` command call sites use this path via `open_agent_trace_db_for_hook_runtime` in `cli/src/services/hooks/mod.rs`. diff --git a/context/cli/checkout-identity.md b/context/cli/checkout-identity.md index 4716f37af..b1b9b4be7 100644 --- a/context/cli/checkout-identity.md +++ b/context/cli/checkout-identity.md @@ -1,31 +1,7 @@ -# Checkout Identity Service +# Checkout Identity Service (removed) -The checkout identity service lives in `cli/src/services/checkout/`. +The checkout identity service (`cli/src/services/checkout/`: `resolve_git_dir`, `read_checkout_id`, `get_or_create_checkout_id`) was removed by the `remove-checkout-id` plan. SCE no longer creates or reads `/sce/checkout-id`; there is no per-clone/worktree identity anywhere in the current code, and `agent_trace_storage::ResolvedAgentTraceStorage` no longer carries a `checkout_id` field. `sce setup` and `sce doctor` (text and JSON) no longer mention checkout identity. -It assigns a stable identity to a local Git checkout or linked Git worktree. Checkout identity remains per clone/worktree, but active Agent Trace persistence is now repository-scoped through `agent_trace_storage`: checkout ID is diagnostic metadata and is not stored on Agent Trace rows. The former per-checkout Agent Trace DB opener and its path helper were removed by the `retire-legacy-agent-trace-db` plan; there is no checkout-scoped DB code path. +Repository-scoped Agent Trace persistence never depended on checkout identity for correctness: it was diagnostic metadata only, never stored on Agent Trace rows and never used to select the active DB (see `context/cli/agent-trace-storage.md`). Any pre-existing `/sce/checkout-id` file left on disk from before this removal is inert and untouched by SCE, the same convention already applied to legacy `agent-trace-.db` files (see `context/sce/agent-trace-db.md`). -## Current code surface - -- `cli/src/services/checkout/mod.rs` - - `resolve_git_dir(repo_root)` runs `git rev-parse --git-dir` from the supplied repository root. - - `read_checkout_id(git_dir)` reads `/sce/checkout-id` and validates non-empty UUID syntax. - - `get_or_create_checkout_id(git_dir)` reuses an existing ID or writes a new UUIDv7 checkout ID to `/sce/checkout-id`. - -## Current integration state - -The module is registered through `cli/src/services/mod.rs` and is consumed by `agent_trace_storage` during repository-scoped storage resolution. - -During setup and hook runtime: - -- Config resolution provides `agent_trace.repository_id` and `agent_trace.repository_remote` (default `origin`). -- `agent_trace_storage::resolve_agent_trace_storage(...)` resolves repository identity, calls `checkout::resolve_git_dir(repo_root)`, and creates/reuses `/sce/checkout-id` for diagnostics. -- The active DB path is `/sce/repos//agent-trace.db`. -- `RepositoryAgentTraceDb` opens through the repository fast-path-then-migrate flow and validates `repository_metadata.repository_id`. - -`sce doctor` still displays checkout identity where available. The former Agent Trace list/status/status-all/shell UX is no longer available; `sce sync` operates on the current repository-scoped DB. Any pre-migration `/sce/agent-trace-*.db` checkout-scoped files left on disk are never touched by SCE and are no longer inspectable through the CLI. - -## Testing boundary - -No unit tests are currently included for this filesystem/Git-facing service. Filesystem, Git repository, and database behaviors should be covered in integration tests rather than unit tests per `context/patterns.md`. - -See also: `context/cli/agent-trace-storage.md`, `context/cli/default-path-catalog.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hooks-command-routing.md`. +See also: `context/glossary.md` (`checkout identity` entry), `context/cli/agent-trace-storage.md`, `context/sce/agent-trace-db.md`. diff --git a/context/cli/cli-command-surface.md b/context/cli/cli-command-surface.md index f08241de6..e1d856640 100644 --- a/context/cli/cli-command-surface.md +++ b/context/cli/cli-command-surface.md @@ -11,7 +11,7 @@ Operator onboarding currently comes from `sce --help`, command-local `--help` ou - Top-level command metadata catalog: `cli/src/cli_schema.rs` - Custom top-level help renderer and known-command classifier: `cli/src/command_surface.rs` - Turso adapters: `cli/src/services/auth_db/mod.rs`, `cli/src/services/local_db/mod.rs`, `cli/src/services/agent_trace_db/mod.rs`, and shared infrastructure in `cli/src/services/db/mod.rs` -- Service domains: `cli/src/services/{agent_trace,agent_trace_db,auth,auth_command,auth_db,checkout,completion,config,db,default_paths,hooks,local_db,observability,output_format,resilience,security,setup,style,sync,token_storage,version}` plus the split doctor module at `cli/src/services/doctor/{mod,command,inspect,render,fixes,types}.rs`; service-owned `command.rs` files own command payload structs for help/version/completion/auth/config/setup/doctor/hooks/policy/sync, and `cli/src/services/command_registry.rs` owns the static `RuntimeCommand` enum that dispatches them +- Service domains: `cli/src/services/{agent_trace,agent_trace_db,auth,auth_command,auth_db,completion,config,db,default_paths,hooks,local_db,observability,output_format,resilience,security,setup,style,sync,token_storage,version}` plus the split doctor module at `cli/src/services/doctor/{mod,command,inspect,render,fixes,types}.rs`; service-owned `command.rs` files own command payload structs for help/version/completion/auth/config/setup/doctor/hooks/policy/sync, and `cli/src/services/command_registry.rs` owns the static `RuntimeCommand` enum that dispatches them - Service lifecycle: `cli/src/services/lifecycle.rs` defines lifecycle-owned health/setup result types, the `ServiceLifecycle` trait with `diagnose`, `fix`, and `setup` methods for concrete providers, and the static `LifecycleProvider` enum used by `doctor`/`setup` to dispatch across `config`, `local_db`, `auth_db`, `agent_trace_db`, and `hooks` without boxed provider aggregation ## Onboarding documentation @@ -85,7 +85,7 @@ An interactive `setup` run instead resolves the selection through an `inquire` m `setup` now also exposes compile-time embedded config assets for OpenCode/Claude/Pi/Codex targets, sourced from the generated `config/.opencode/**`, `config/.claude/**`, `config/.pi/**`, and (via a build-time staging merge of `config/.agents/**` + `config/.codex/**`) `config/codex-target/**` trees via `cli/build.rs` with normalized forward-slash relative paths and target-scoped iteration APIs; the embedded asset set includes the OpenCode bash-policy plugin wrapper plus Claude settings `PreToolUse` Bash policy hook, both delegating to the Rust `sce policy bash` path. `setup` additionally includes a repository-root install engine (`install_embedded_setup_assets`) that installs each embedded asset individually into `.opencode/`/`.claude/`/`.pi/` — stage next to the final destination, remove only that destination file if present, swap into place, with deterministic recovery guidance naming the failing asset's path on swap failure — never removing an integration target directory as a whole, while treating bash-policy enforcement files as first-class SCE-managed assets. Codex is the one target whose embedded relative paths already carry their own output-root prefix (`.agents/...`, `.codex/...`), so its destination root is the repository root itself rather than a single `.codex/`-style subdirectory. Its generated hook command resolves that repository root at invocation time, so Codex events from nested cwd and repositories with spaces reach the installed helper safely; Git-root failure is a silent successful no-op, while the helper preserves missing-CLI stderr guidance and STDIN forwarding. See [setup non-destructive per-asset install policy](../sce/setup-no-backup-policy-seam.md) for the full contract, including the pending pruning gap for deselected/stale assets. `setup` now executes end-to-end and prints deterministic completion details including selected target(s) and per-target install count. -`doctor` now executes end-to-end with explicit diagnosis and repair-intent surfaces: `sce doctor` stays read-only and `sce doctor --fix` selects repair-intent mode. The former Agent Trace database inspection routes are unavailable; doctor owns repository-scoped Agent Trace DB health and checkout-identity diagnostics. The current `doctor` runtime aggregates `ServiceLifecycle::diagnose` and `ServiceLifecycle::fix` calls across all registered service providers (`config`, `local_db`, `auth_db`, `agent_trace_db`, `hooks`) plus integration checks, covering state-root resolution, global and repo-local `sce/config.json` readability/schema validation, local DB and repository-scoped Agent Trace DB path/health, DB-parent readiness barriers, the repo hook rollout slice when a repository target is detected, and post-commit Agent Trace auto-sync readiness derived from canonical managed-block currency plus resolved configuration. The readiness fact reports enabled/current, explicit disabled, not-ready, and not-applicable states in text and JSON without launching synchronization; Claude's inventory is only `Plugins`, `Commands`, and `Skills`, while OpenCode retains `Agents`. Fix mode delegates to each provider's `fix` implementation, which reuses the canonical setup hook install flow to repair missing/stale/non-executable required hooks and missing hooks directories, and it can bootstrap missing canonical database parent directories when the resolved paths match canonical owned locations. +`doctor` now executes end-to-end with explicit diagnosis and repair-intent surfaces: `sce doctor` stays read-only and `sce doctor --fix` selects repair-intent mode. The former Agent Trace database inspection routes are unavailable; doctor owns repository-scoped Agent Trace DB health (checkout-identity diagnostics were removed by the `remove-checkout-id` plan; see `context/cli/checkout-identity.md`). The current `doctor` runtime aggregates `ServiceLifecycle::diagnose` and `ServiceLifecycle::fix` calls across all registered service providers (`config`, `local_db`, `auth_db`, `agent_trace_db`, `hooks`) plus integration checks, covering state-root resolution, global and repo-local `sce/config.json` readability/schema validation, local DB and repository-scoped Agent Trace DB path/health, DB-parent readiness barriers, the repo hook rollout slice when a repository target is detected, and post-commit Agent Trace auto-sync readiness derived from canonical managed-block currency plus resolved configuration. The readiness fact reports enabled/current, explicit disabled, not-ready, and not-applicable states in text and JSON without launching synchronization; Claude's inventory is only `Plugins`, `Commands`, and `Skills`, while OpenCode retains `Agents`. Fix mode delegates to each provider's `fix` implementation, which reuses the canonical setup hook install flow to repair missing/stale/non-executable required hooks and missing hooks directories, and it can bootstrap missing canonical database parent directories when the resolved paths match canonical owned locations. `sce sync [--format text|json]` is the implemented user-invocable synchronization command: it synchronizes the current repository's Agent Trace DB with the control-plane ingestion API; local DB and Agent Trace DB bootstrap continue to happen through `setup`, and DB health/repair continues to happen through `doctor`. See [agent-trace-sync-command.md](agent-trace-sync-command.md) and [sync-command.md](sync-command.md). ## Command loop and error model @@ -102,7 +102,7 @@ An interactive `setup` run instead resolves the selection through an `inquire` m - Interactive `sce setup` prompt cancellation/interrupt exits cleanly with: `Setup cancelled. No files were changed.` - Command handlers return deterministic status messaging: - `setup`: `Context baseline ensured.` on every successful path; context-only `--bootstrap-context` stops there. Normal modes continue with `Setup completed successfully.` plus selected targets and per-target install destinations/counts. -- `doctor`: current runtime emits compact `SCE doctor` / `SCE doctor fix` human text headers plus ordered `Environment`, `Repository`, and `Integrations` domains with typed target-scoped integration areas, bracketed `[PASS]`/`[WARN]`/`[FAIL]`/`[MISS]` status tokens, shared-style colorization, healthy-row metadata suppression, a post-commit Agent Trace auto-sync readiness row, and a deterministic summary footer; JSON output carries stable problem/fixability records plus deterministic fix-result records in fix mode, reports `checkout_identity` plus the resolved repository-scoped Agent Trace DB record with credential-safe metadata, and includes the stable `post_commit_auto_sync` `state`/`enabled`/`source`/`config_source` object. Existing hook problems continue to own remediation and overall readiness, and launcher failures remain fail-open in the hook runtime. Use `--format json` for complete path and identity detail; there is no verbose text flag. +- `doctor`: current runtime emits compact `SCE doctor` / `SCE doctor fix` human text headers plus ordered `Environment`, `Repository`, and `Integrations` domains with typed target-scoped integration areas, bracketed `[PASS]`/`[WARN]`/`[FAIL]`/`[MISS]` status tokens, shared-style colorization, healthy-row metadata suppression, a post-commit Agent Trace auto-sync readiness row, and a deterministic summary footer; JSON output carries stable problem/fixability records plus deterministic fix-result records in fix mode, reports the resolved repository-scoped Agent Trace DB record with credential-safe metadata, and includes the stable `post_commit_auto_sync` `state`/`enabled`/`source`/`config_source` object. Existing hook problems continue to own remediation and overall readiness, and launcher failures remain fail-open in the hook runtime. Use `--format json` for complete path and identity detail; there is no verbose text flag. - `hooks`: deterministic hook subcommand status messaging for runtime entrypoint invocation and argument/STDIN contract validation. ## Service contracts @@ -110,7 +110,7 @@ An interactive `setup` run instead resolves the selection through an `inquire` m - `cli/src/services/setup/mod.rs` defines setup parsing/selection contracts, additive `bootstrap_context_baseline`, and runtime install orchestration (`run_setup_for_mode`) over the embedded asset install engine; its setup-owned repository-root resolver returns a typed classification that recognizes only a positively identified non-Git directory as `setup.not_git_repository`, while nonexistent, inaccessible, process, and malformed-output failures remain `general.unexpected_failure`. `cli/src/services/setup/command.rs` owns the setup runtime command handler, runs the Git-root plus effective named-remote preflights before prompts or writes, and maps those typed outcomes while preserving technical sources. Missing configured remotes become unit-variant `UserError::NotGitRemote`; the preserved source retains the resolved remote name for safe operator guidance. After both gates, setup always ensures the context baseline; context-only requests return there, while normal modes aggregate `ServiceLifecycle::setup` calls across registered providers (`config`, `local_db`, `auth_db`, `agent_trace_db`, `hooks`) in order, using a `ContextWithRepoRoot`-scoped context with resolved repository root. - `cli/src/services/setup/mod.rs` now keeps its larger internal responsibilities behind focused inline support modules: `install` owns repository canonicalization, staging/swap install flows, required-hook installation, and repo/writeability guards, while `prompt` owns interactive target selection and styled prompt labels. - `cli/src/services/config/mod.rs` defines config parser/runtime contracts (`show`, `validate`, `--help`), strict config-file key/type validation, deterministic text/JSON rendering, repo-configured bash-policy preset/custom validation and reporting under `policies.bash`, and shared auth-key metadata that declares env key, config-file key, and optional baked-default eligibility for supported auth runtime values starting with `workos_client_id` (`WORKOS_CLIENT_ID` vs `workos_client_id`); auth-key provenance/preference metadata stays on `show`, while `validate` stays trimmed to validation status plus issues/warnings. `cli/src/services/config/lifecycle.rs` implements `ServiceLifecycle` for config health checks and setup (global/local config validation and repo-local config bootstrap). - - `cli/src/services/doctor/mod.rs` defines the implemented doctor request/report contract (`DoctorRequest`, `DoctorMode`, `run_doctor_with_context`) while focused submodules under `cli/src/services/doctor/` handle runtime command dispatch (`command.rs`), diagnosis (`inspect.rs`), rendering (`render.rs`), fix execution (`fixes.rs`), and doctor-owned domain types (`types.rs`). Together they preserve explicit fix-mode parsing, checkout identity diagnostics, repository-scoped Agent Trace DB health, stable JSON problem and database-record rendering, deterministic fix-result reporting, and aggregation of `ServiceLifecycle::diagnose`/`ServiceLifecycle::fix` across registered providers (`config`, `local_db`, `auth_db`, `agent_trace_db`, `hooks`). The doctor module coordinates state-root/config/database reporting and validation, path-source detection plus required-hook presence/executable/content checks when a repository target is detected, canonical non-launching post-commit auto-sync readiness reporting from managed-block currency plus resolved config, repo-root installed OpenCode, Claude, Pi, and Codex integration inventory derived from embedded setup asset catalogs, typed target/area grouping with compact human status tokens (`[PASS]`, `[WARN]`, `[FAIL]`, `[MISS]`), and repair-mode delegation to service-owned fix implementations. The human renderer emits the compact `SCE doctor` / `SCE doctor fix` hierarchy, suppresses healthy paths and identity metadata, and expands only unhealthy branches; JSON retains the complete path, identity, problem, fix-result, and `post_commit_auto_sync` detail. Claude grouping is typed and path-based: `settings.json`/`hooks/**` as the `Plugins` area (including `.claude/hooks/run-sce-or-show-install-guidance.sh`), plus `Commands` and `Skills`; OpenCode retains `Plugins`, `Agents`, `Commands`, and `Skills`; Pi grouping includes `prompts/**`, `skills/**`, and `extensions/**` areas; Codex grouping includes `.agents/skills/**` as `Skills` and `.codex/hooks.json`/`.codex/hooks/**` as `Hooks` (the latter also carrying a Codex hook trust/review reminder when unhealthy). + - `cli/src/services/doctor/mod.rs` defines the implemented doctor request/report contract (`DoctorRequest`, `DoctorMode`, `run_doctor_with_context`) while focused submodules under `cli/src/services/doctor/` handle runtime command dispatch (`command.rs`), diagnosis (`inspect.rs`), rendering (`render.rs`), fix execution (`fixes.rs`), and doctor-owned domain types (`types.rs`). Together they preserve explicit fix-mode parsing, repository-scoped Agent Trace DB health, stable JSON problem and database-record rendering, deterministic fix-result reporting, and aggregation of `ServiceLifecycle::diagnose`/`ServiceLifecycle::fix` across registered providers (`config`, `local_db`, `auth_db`, `agent_trace_db`, `hooks`). The doctor module coordinates state-root/config/database reporting and validation, path-source detection plus required-hook presence/executable/content checks when a repository target is detected, canonical non-launching post-commit auto-sync readiness reporting from managed-block currency plus resolved config, repo-root installed OpenCode, Claude, Pi, and Codex integration inventory derived from embedded setup asset catalogs, typed target/area grouping with compact human status tokens (`[PASS]`, `[WARN]`, `[FAIL]`, `[MISS]`), and repair-mode delegation to service-owned fix implementations. The human renderer emits the compact `SCE doctor` / `SCE doctor fix` hierarchy, suppresses healthy paths and identity metadata, and expands only unhealthy branches; JSON retains the complete path, identity, problem, fix-result, and `post_commit_auto_sync` detail. Claude grouping is typed and path-based: `settings.json`/`hooks/**` as the `Plugins` area (including `.claude/hooks/run-sce-or-show-install-guidance.sh`), plus `Commands` and `Skills`; OpenCode retains `Plugins`, `Agents`, `Commands`, and `Skills`; Pi grouping includes `prompts/**`, `skills/**`, and `extensions/**` areas; Codex grouping includes `.agents/skills/**` as `Skills` and `.codex/hooks.json`/`.codex/hooks/**` as `Hooks` (the latter also carrying a Codex hook trust/review reminder when unhealthy). - `cli/src/services/version/mod.rs` defines the version parser/output contract (`parse_version_request`, `render_version`) with deterministic text/JSON output modes; `cli/src/services/version/command.rs` owns the version runtime command handler. - `cli/src/services/completion/mod.rs` defines the completion output contract (`render_completion`) using clap_complete to generate deterministic shell scripts for Bash, Zsh, and Fish; `cli/src/services/completion/command.rs` owns the completion runtime command handler. - `cli/src/services/hooks/mod.rs` defines production local hook runtime parsing/dispatch (`HookSubcommand`, `run_hooks_subcommand`) for `pre-commit`, `commit-msg`, `post-commit`, `post-rewrite`, `diff-trace`, `conversation-trace`, and `codex`; `cli/src/services/hooks/codex/` owns the Codex dispatcher (typed `CodexHookEvent` parsing plus `classify_codex_event`; `UserPromptSubmit`/`Stop` capture real conversation evidence, `PreToolUse(Bash)` delegates to the existing Bash policy engine, `PostToolUse(apply_patch)` parses/normalizes/persists a `diff_traces` row for provable Add/Update evidence; `PreToolUse(apply_patch)` is unregistered and falls open as a no-op like every other unsupported combination or malformed STDIN); `cli/src/services/hooks/command.rs` owns the hook runtime command handler. Current runtime behavior is commit-msg-only attribution behind the enabled-by-default attribution gate with explicit opt-out controls; `pre-commit` and `post-rewrite` are deterministic no-ops; `post-commit` requires validated `--remote-url`, threads that value through Agent Trace flow, prints it to stderr, and remains an active intersection + Agent Trace DB persistence path; `diff-trace` performs STDIN JSON intake, required-field validation, and best-effort AgentTraceDb insertion with tool-prefixed stored `session_id` values plus direct nullable `model_id` / `tool_version` attribution. Claude structured `PostToolUse` payloads may derive `model_id` from top-level or nested `model` metadata with `claude/` prefix normalization. `session-model` is no longer a supported hooks route. `cli/src/services/hooks/lifecycle.rs` implements `ServiceLifecycle` for hook health checks, fix, and setup (hook rollout integrity and required-hook installation). @@ -127,7 +127,7 @@ An interactive `setup` run instead resolves the selection through an `inquire` m - `cli/src/services/local_db/mod.rs` provides `LocalDb = TursoDb` with retry-backed `new()`, `execute()`, `query()`, and `query_map()` inherited from the shared Turso adapter. - `LocalDb::new()` resolves the canonical per-user DB path through `default_paths::local_db_path()`, creates parent directories, opens the local Turso database, and currently runs zero local migrations. - `cli/src/services/agent_trace_db/repository.rs` provides the sole `RepositoryAgentTraceDb = TursoDb` adapter plus `DiffTraceInsert<'_>`/`insert_diff_trace()` for parameterized writes to `diff_traces`, `AgentTraceInsert<'_>`/`insert_agent_trace()` for built `agent_traces`, and typed message/part insert helpers for conversation traces; the checkout-scoped `AgentTraceDb` adapter was removed by the `retire-legacy-agent-trace-db` plan. -- Active hook runtime resolves repository-scoped DB files through config-backed repository identity and `default_paths::agent_trace_db_path_for_repository(repository_id)` via `agent_trace_storage`; checkout ID is created/reused only as diagnostic metadata. +- Active hook runtime resolves repository-scoped DB files through config-backed repository identity and `default_paths::agent_trace_db_path_for_repository(repository_id)` via `agent_trace_storage`. - `cli/src/services/local_db/lifecycle.rs` implements `ServiceLifecycle` for local DB health checks and setup (DB path/health validation and DB bootstrap). - `cli/src/services/agent_trace_db/lifecycle.rs` implements `ServiceLifecycle` for Agent Trace repository storage setup, setup-time repository DB initialization, repository-scoped DB path-health validation, and parent bootstrap. - `sce setup` aggregates `ServiceLifecycle::setup` calls, which includes `LocalDbLifecycle::setup()` and `AgentTraceDbLifecycle::setup()` for DB initialization as part of local prerequisite bootstrap. diff --git a/context/cli/service-lifecycle.md b/context/cli/service-lifecycle.md index 912deeb5e..4727f4874 100644 --- a/context/cli/service-lifecycle.md +++ b/context/cli/service-lifecycle.md @@ -34,7 +34,7 @@ - `cli/src/services/agent_trace_db/lifecycle.rs` defines `AgentTraceDbLifecycle`, the Agent Trace DB-owned provider. - `AgentTraceDbLifecycle::diagnose` resolves repository identity from `ctx.repo_root()` plus `agent_trace.repository_id` / `agent_trace.repository_remote`, and emits repository-scoped Agent Trace DB path and parent-directory readiness lifecycle health problems. Outside repository context there is no global/checkout fallback path (removed by the `retire-legacy-agent-trace-db` plan); `resolve_lifecycle_agent_trace_db_path` returns an actionable "requires a Git repository" diagnostic, surfaced as a manual-only `UnableToResolveStateRoot` problem. - `AgentTraceDbLifecycle::fix` bootstraps the resolved repository Agent Trace DB parent directory for auto-fixable DB parent readiness problems; outside repository context it returns the same actionable no-repository diagnostic rather than probing a sentinel path. -- `AgentTraceDbLifecycle::setup` resolves repository storage from `ctx.repo_root()` when available, creates or reuses `/sce/checkout-id` for diagnostics, resolves `/sce/repos//agent-trace.db`, opens/creates that database through `RepositoryAgentTraceDb` so the repository schema is applied and metadata is validated, and returns setup messaging with the repository ID, checkout ID, and initialized DB path. Hook runtime uses the same repository storage resolver lazily when setup has not run or schema metadata is incomplete. +- `AgentTraceDbLifecycle::setup` resolves repository storage from `ctx.repo_root()` when available, resolves `/sce/repos//agent-trace.db`, opens/creates that database through `RepositoryAgentTraceDb` so the repository schema is applied and metadata is validated, and returns setup messaging with the repository ID and initialized DB path. Hook runtime uses the same repository storage resolver lazily when setup has not run or schema metadata is incomplete. - `doctor` runtime execution now aggregates lifecycle providers for diagnosis and repair: - `cli/src/services/doctor/command.rs` accepts any context implementing `ContextWithRepoRoot`. - `cli/src/services/doctor/mod.rs` resolves the repository root once, creates a repo-root-scoped borrowed context using `with_repo_root()`, and requests the full provider catalog with hooks included. diff --git a/context/context-map.md b/context/context-map.md index 0740494e3..a100ccecb 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -12,8 +12,8 @@ Feature/domain context: - `context/cli/cli-command-surface.md` (CLI command surface including top-level help with ASCII art banner and gradient rendering, setup install flow with the repeatable `sce setup --workflow ` optional-workflow selection and its interactive post-target multi-select, WorkOS device authorization flow + token storage behavior including stored-credential renewal through `sce auth login`, attribution-only hook routing with validated post-commit `--remote-url` plumbing plus DB-backed `diff-trace` dual persistence and post-commit Agent Trace payload persistence including range `content_hash`, setup-owned local DB + repository-scoped Agent Trace DB bootstrap plus doctor DB health coverage with credential-safe repository identity diagnostics, centralized Rust SCE web URL helpers in `services::agent_trace`, nested flake release package/app installability, Cargo local install + crates.io readiness policy, hidden `sce policy bash` command adapter for bash-policy hook callers, and top-level `sce sync` command wiring for current-repository Agent Trace synchronization; static `RuntimeCommand` enum dispatch lives in `services/command_registry.rs`, command payload structs for help/version/completion/auth/config/setup/doctor/hooks/policy/sync are owned by their respective `services/{name}/command.rs` files, and clap-to-runtime conversion lives in `services/parse/command_runtime.rs`) - `context/cli/default-path-catalog.md` (canonical production CLI path-ownership contract centered on `cli/src/services/default_paths.rs`, including persisted auth/config files, named DB paths for auth/local/repository-scoped Agent Trace databases, the default observability log-dir accessor consumed by config resolution with Linux `${XDG_STATE_HOME:-~/.local/state}/sce/logs` fallback semantics, repo-relative, embedded-asset, install, hook, and context-path families plus the regression guard that keeps production path ownership centralized) - `context/cli/repository-identity.md` (repository identity module in `cli/src/services/repository_identity/`: pure scheme-neutral `host[:port]/path` canonicalization for SCP/`ssh://`/HTTPS/`git://` remote URLs with credential stripping, hostname lowercasing, default-port removal, and query/fragment/trailing-`.git` cleanup, trim-only explicit-identity handling, `sha256("sce-repository-id-v1\0" + canonical_identity)` repository IDs, credential-safe fieldless errors, plus the `resolve` runtime submodule applying explicit-config-then-configured-remote precedence with `git config --get remote..url` lookup, `RepositoryIdentitySource` provenance, and `.sce/config.json`-guidance resolution errors that never echo URLs; consumed by the T04 `agent_trace_storage` resolver) -- `context/cli/agent-trace-storage.md` (repository-scoped Agent Trace storage resolver in `cli/src/services/agent_trace_storage/`: `AgentTraceStorageContext` inputs mirroring the `agent_trace.*` config keys, shared degradation of invalid default-discovered config before identity fallback while explicit selections remain fatal, `ResolvedAgentTraceStorage` carrying repository identity + checkout ID + `/sce/repos//agent-trace.db` path + open `RepositoryAgentTraceDb` + typed `RepositoryMetadata`, `resolve_agent_trace_storage{,_at_state_root}` setup/lifecycle entrypoints with idempotent concurrent-safe first open via bounded fast-path-then-migrate retry plus narrow one-file schema migration-metadata repair and repository metadata validation, a separate no-migration `resolve_agent_trace_storage_for_hook_runtime{,_at_state_root}` pair that high-frequency hook callers use exclusively and that never runs migration `002` or any migration, path-unsafe repository ID rejection in `default_paths::agent_trace_db_path_for_repository{,_at}`, strict never-touch boundary for any pre-migration checkout-scoped/global DB files, and active hook/runtime plus Agent Trace lifecycle setup call-site consumption after T08) -- `context/cli/checkout-identity.md` (current checkout identity infrastructure in `cli/src/services/checkout/`, including `/sce/checkout-id` UUIDv7 storage, setup/hook integration that creates/reuses checkout identity as repository-scoped Agent Trace diagnostic metadata, the removed per-checkout DB opener/path helper, `sce doctor` checkout identity display, and the never-touch on-disk handling of pre-migration checkout-scoped DB files that are no longer inspectable via the CLI) +- `context/cli/agent-trace-storage.md` (repository-scoped Agent Trace storage resolver in `cli/src/services/agent_trace_storage/`: `AgentTraceStorageContext` inputs mirroring the `agent_trace.*` config keys, shared degradation of invalid default-discovered config before identity fallback while explicit selections remain fatal, `ResolvedAgentTraceStorage` carrying repository identity + `/sce/repos//agent-trace.db` path + open `RepositoryAgentTraceDb` + typed `RepositoryMetadata`, `resolve_agent_trace_storage{,_at_state_root}` setup/lifecycle entrypoints with idempotent concurrent-safe first open via bounded fast-path-then-migrate retry plus narrow one-file schema migration-metadata repair and repository metadata validation, a separate no-migration `resolve_agent_trace_storage_for_hook_runtime{,_at_state_root}` pair that high-frequency hook callers use exclusively and that never runs migration `002` or any migration, path-unsafe repository ID rejection in `default_paths::agent_trace_db_path_for_repository{,_at}`, strict never-touch boundary for any pre-migration checkout-scoped/global DB files, and active hook/runtime plus Agent Trace lifecycle setup call-site consumption after T08) +- `context/cli/checkout-identity.md` (checkout identity infrastructure, `cli/src/services/checkout/` and its `/sce/checkout-id` UUIDv7 storage, was removed by the `remove-checkout-id` plan; documents the removal plus the never-touch on-disk handling of any pre-existing checkout-id/pre-migration checkout-scoped DB files left behind) - `context/cli/patch-service.md` (standalone patch domain model, parser, JSON load helpers, and set operations in `cli/src/services/patch.rs` for in-memory parsed unified-diff representation, capturing only touched lines plus minimal per-file/per-hunk metadata, supporting both `Index:` SVN-style and `diff --git` git-style formats, with `ParseError` for actionable malformed-input diagnostics, `PatchLoadError`/`load_patch_from_json`/`load_patch_from_json_bytes` for storage-agnostic JSON reconstruction, `intersect_patches` for target-shaped overlap with exact-match-first and historical `kind`+`content` fallback semantics plus matched-constructed-line `session_id` and matched-constructed-hunk `model_id` provenance inheritance, and `combine_patches` for ordered patch combination with later-wins conflict resolution plus winning-hunk `model_id` provenance inheritance; repository structured-row reconstruction supplies persisted hunk-model and canonical touched-line-session provenance before these operations; `parse_patch`, `intersect_patches`, and `combine_patches` are consumed by the active post-commit hook runtime) - `context/cli/structured-patch-service.md` (Claude structured editor-hook derivation in `cli/src/services/structured_patch.rs`, including `Write` structured-update hunks, `Write` `tool_input.content` create fallback, `Edit` structured patches, deterministic skip reasons, `ParsedPatch` output semantics, Rust golden fixture coverage, and repository read-time enrichment that assigns persisted row `model_id` to each hunk and canonical row `session_id` to each touched line) - `context/cli/styling-service.md` (CLI text-mode output styling with `owo-colors`, TTY/`NO_COLOR` policy, shared helper API for human-facing surfaces including sync completion markers, and per-column right-to-left RGB gradient banner rendering) @@ -22,7 +22,7 @@ Feature/domain context: - `context/cli/agent-trace-sync-command.md` (composed local-to-control-plane `sce sync` architecture: the `hooks/plugins → repository Agent Trace DB → AgentTraceExportReader → sce sync → HTTPS + WorkOS Bearer → control plane` data flow, the `sce auth login` / `cd ` / `sce sync` user flow, the no-local-cursor/no-`agent-trace-sync.db`/no-Turso-Sync/no-`BridgeLock`/no-local-DWH invariants, and `401`/`409`/ambiguous-batch-failure recovery semantics) - `context/cli/config-precedence-contract.md` (implemented `sce config` show/validate command contract, deterministic `flags > env > config file > defaults` resolution order, focused `config/resolver.rs` ownership for config discovery/merge/runtime precedence plus default-discovered invalid-file degradation, focused `config/render.rs` ownership for `show`/`validate` text+JSON output construction, canonical `$schema` acceptance for startup-loaded `sce/config.json` files, shared auth-key env/config/optional baked-default support starting with `workos_client_id`, shared runtime resolution for flat logging observability keys including config-file/default `log_to_file`, `log_dir` / `SCE_LOG_DIR` with `/sce/logs` defaulting plus config-file/default-only positive `log_file_retention_limit`, config-file-only `agent_trace.repository_id`/`agent_trace.repository_remote` repository-identity keys with default remote `origin`, `agent_trace.auto_sync` resolution with omitted-value fallback `false`, explicit-false opt-out, and setup-written explicit `true` bootstrap behavior for the post-commit trigger boundary, the catalog-derived `integrations.optional_workflows` optional-workflow selection key, JSON-pointer-prefixed schema-validation errors, canonical Pkl-generated `sce/config.json` schema ownership plus CLI embedding/reuse contract including `policies.attribution_hooks.enabled` default-true/explicit-false opt-out metadata, config-file selection order, `show` provenance output, and trimmed `validate` output contract) - `context/cli/capability-traits.md` (current broad CLI capability seam in `cli/src/services/capabilities.rs`, including `FsOps`/`StdFsOps`, `GitOps`/`ProcessGitOps`, git root/hooks resolution behavior, compile-time-typed borrowed AppContext wiring with associated-type narrow capability accessors plus `ContextWithRepoRoot` repo-root-scoped context derivation, generic command execution bounds, and test-only unimplemented stubs; current service internals do not consume fs/git traits until later lifecycle migration tasks) -- `context/cli/service-lifecycle.md` (current compile-safe lifecycle seam in `cli/src/services/lifecycle.rs`, including default no-op `ServiceLifecycle` diagnose/fix/setup methods against narrow `HasRepoRoot`, lifecycle-owned health/fix/setup result types with generic setup messages, doctor/setup adapter boundaries, the static `LifecycleProvider` enum catalog/dispatcher, hook/config/local_db/auth_db/agent_trace_db lifecycle providers including setup-time repository-scoped Agent Trace DB initialization plus checkout identity diagnostics, implemented doctor aggregation over diagnose/fix providers, and implemented setup aggregation over `setup` providers in order config → local_db → auth_db → agent_trace_db → hooks when requested) +- `context/cli/service-lifecycle.md` (current compile-safe lifecycle seam in `cli/src/services/lifecycle.rs`, including default no-op `ServiceLifecycle` diagnose/fix/setup methods against narrow `HasRepoRoot`, lifecycle-owned health/fix/setup result types with generic setup messages, doctor/setup adapter boundaries, the static `LifecycleProvider` enum catalog/dispatcher, hook/config/local_db/auth_db/agent_trace_db lifecycle providers including setup-time repository-scoped Agent Trace DB initialization, implemented doctor aggregation over diagnose/fix providers, and implemented setup aggregation over `setup` providers in order config → local_db → auth_db → agent_trace_db → hooks when requested) - `context/sce/cli-exit-code-contract.md` (stable class-based `sce` process exit-code contract in `cli/src/app.rs`, so automation can branch on failure category without parsing error text) - `context/sce/cli-error-code-taxonomy.md` (stable user-facing `SCE-ERR-*` diagnostic code classes rendered by `cli/src/app.rs`, complementing the numeric exit-code classes) - `context/sce/cli-stdout-stderr-contract.md` (implemented stream contract in `cli/src/app.rs`: command payloads on stdout only, redacted diagnostics on stderr) @@ -50,7 +50,7 @@ Feature/domain context: - `context/sce/agent-trace-pre-commit-staged-checkpoint.md` (historical pre-commit staged-checkpoint contract; current runtime baseline has replaced this path with a deterministic no-op) - `context/sce/agent-trace-commit-msg-coauthor-policy.md` (current commit-msg canonical co-author trailer policy with enabled-by-default attribution hooks, explicit opt-out controls, `SCE_DISABLED` kill switch, caller-provided `ai_contribution_present` transformer seam wired from staged-diff AI-overlap preflight, idempotent dedupe, the `agent_trace::patches_have_overlap` pure overlap seam, the `StagedDiffAiOverlapResult` three-valued evidence gate, and `sce.hooks.commit_msg.ai_overlap_error` error logging) - `context/sce/agent-trace-post-commit-dual-write.md` (historical post-commit no-op/dual-write reference; current post-commit behavior is documented in `agent-trace-hooks-command-routing.md`) -- `context/sce/agent-trace-hook-doctor.md` (approved operator-environment contract for broadening `sce doctor` into the canonical health-and-repair entrypoint, including stable problem taxonomy, `--fix` semantics, checkout-aware Agent Trace DB reporting, post-commit Agent Trace auto-sync readiness proof and opt-out behavior, setup-to-doctor alignment rules, canonical Git-hook payload restoration, and the approved downstream human text-mode layout/status/integration contract) +- `context/sce/agent-trace-hook-doctor.md` (approved operator-environment contract for broadening `sce doctor` into the canonical health-and-repair entrypoint, including stable problem taxonomy, `--fix` semantics, repository-scoped Agent Trace DB reporting, post-commit Agent Trace auto-sync readiness proof and opt-out behavior, setup-to-doctor alignment rules, canonical Git-hook payload restoration, and the approved downstream human text-mode layout/status/integration contract) - `context/sce/doctor-human-text-contract.md` (implemented compact `sce doctor` human text contract: Environment/Repository/Integrations hierarchy, post-commit Agent Trace auto-sync readiness labels, `[PASS]`/`[WARN]`/`[FAIL]`/`[MISS]` status vocabulary, healthy-row metadata suppression, typed Claude Code/OpenCode/Pi/Codex target and area ordering, configured/detected/empty target resolution, selection-scoped optional-workflow inventory, the Codex hook trust/review reminder, no-installed-integrations guidance, and JSON as the full-detail route) - `context/sce/setup-githooks-install-contract.md` (canonical `sce setup --hooks` install contract for target-path resolution, all-hook non-blocking missing-CLI bootstrap behavior, foreign-hook preservation and managed-block merge/idempotent outcomes, atomic-swap replacement behavior, and doctor-readiness alignment) - `context/sce/setup-no-backup-policy-seam.md` (non-destructive per-asset install policy: config install writes/swaps each embedded asset individually by atomic rename over the destination, without ever unlinking it first, and never removes an integration target directory as a whole, then prunes catalog-derived stale/deselected asset paths and any parent directory left empty by that pruning; required-hook install uses the same per-file stage/atomic-swap choreography and, like the two JSON merge targets, computes its staged content ahead of the swap — a foreign hook's bytes are kept as an exact prefix with the SCE managed block appended; `.claude/settings.json` and `.opencode/opencode.json` are merge targets whose staged content is computed by JSON-merging the generated document into the user's existing one before the shared stage/swap step; no backup creation; a swap failure leaves prior destination content untouched, with deterministic recovery guidance naming the failing asset) diff --git a/context/glossary.md b/context/glossary.md index f4b7b78d3..2f70f2cbb 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -36,12 +36,12 @@ - `atomic commit bypass mode`: The `/commit` mode selected when the exact first argument token is `oneshot` or `skip` (case-insensitive; the two aliases are behaviorally identical). It skips the staging-confirmation prompt and the context-file guidance gate, requires exactly one commit message covering all staged files with no split proposals, makes plan citations best-effort instead of blocking, and lets the command run exactly one `git commit`. Any other first token selects proposal-only regular mode. See `context/sce/atomic-commit-workflow.md`. - `split commit references`: The generated `sce-commit/references/atomic-commit.md`, `references/commit-message-style.md`, and `references/output.md` documents. The atomic reference owns staged-diff procedure, internal result branching, and commit boundaries; the style reference owns commit-message wording; and the output reference owns human-visible layouts. No `commit-contract.yaml` artifact or YAML result-contract section is generated. - `canonical OpenCode plugin registration source`: Shared Pkl-authored plugin-registration definition in `config/pkl/base/opencode.pkl`, re-exported from `config/pkl/renderers/common.pkl` as the canonical plugin list/path JSON consumed by OpenCode renderers before they emit generated `opencode.json` manifests; the current entries are `sce-bash-policy` and `sce-agent-trace`. -- `checkout identity`: Stable UUIDv7 identifier assigned to a cloned repository or linked Git worktree, stored in `/sce/checkout-id` (never committed) and resolved via `git rev-parse --git-dir`. The identity is created or reused by `sce setup` through `AgentTraceDbLifecycle::setup()` and also auto-created by hook runtime when `sce setup` has not been run. Checkout identity is now diagnostic metadata for repository-scoped Agent Trace storage; it does not select the active DB and is not stored on Agent Trace rows. Any pre-migration per-checkout DB files at `/sce/agent-trace-{checkout_id}.db` are never touched by SCE and are no longer inspectable via the CLI (the checkout-scoped DB surface was removed by the `retire-legacy-agent-trace-db` plan). See `context/cli/checkout-identity.md`. -- `repository identity`: Stable identity of a logical Git repository used to select the active repository-scoped Agent Trace DB path `/sce/repos//agent-trace.db` through the `agent_trace_storage` resolver. Resolved by `cli/src/services/repository_identity/` with precedence: explicit `agent_trace.repository_id` config value, then the URL of the configured Git remote (`agent_trace.repository_remote`, default `origin`), otherwise an actionable `.sce/config.json` error. Remote URLs canonicalize to a scheme-neutral, credential-free `host[:port]/path` form so equivalent SSH/SCP/HTTPS remotes converge, and the repository ID is `sha256("sce-repository-id-v1\0" + canonical_identity)` hex. Distinct from `checkout identity`, which stays per clone/worktree for diagnostics. Setup's named-remote preflight uses a strict lookup that returns `Ok(None)` only for a missing/empty URL, preserves other failures as runtime errors, and discards the URL, retaining only the remote name in technical failure sources. See `context/cli/repository-identity.md`. +- `checkout identity` (removed): Stable UUIDv7 identifier assigned to a cloned repository or linked Git worktree, stored in `/sce/checkout-id` (never committed) and resolved via `git rev-parse --git-dir`. It was diagnostic metadata only for repository-scoped Agent Trace storage — never used to select the active DB, never stored on Agent Trace rows — and was removed by the `remove-checkout-id` plan: `sce` no longer creates or reads `/sce/checkout-id`, and `sce setup`/`sce doctor` no longer mention it. Any pre-existing `checkout-id` file left on disk from before this removal is inert and untouched by SCE, the same convention already applied to legacy per-checkout `/sce/agent-trace-{checkout_id}.db` files (the checkout-scoped DB surface was removed earlier by the `retire-legacy-agent-trace-db` plan). See `context/cli/checkout-identity.md`. +- `repository identity`: Stable identity of a logical Git repository used to select the active repository-scoped Agent Trace DB path `/sce/repos//agent-trace.db` through the `agent_trace_storage` resolver. Resolved by `cli/src/services/repository_identity/` with precedence: explicit `agent_trace.repository_id` config value, then the URL of the configured Git remote (`agent_trace.repository_remote`, default `origin`), otherwise an actionable `.sce/config.json` error. Remote URLs canonicalize to a scheme-neutral, credential-free `host[:port]/path` form so equivalent SSH/SCP/HTTPS remotes converge, and the repository ID is `sha256("sce-repository-id-v1\0" + canonical_identity)` hex. Distinct from the removed `checkout identity`, which was per clone/worktree. Setup's named-remote preflight uses a strict lookup that returns `Ok(None)` only for a missing/empty URL, preserves other failures as runtime errors, and discards the URL, retaining only the remote name in technical failure sources. See `context/cli/repository-identity.md`. - `refresh single-flight guard`: Client-owned async coordination for Agent Trace control-plane authentication. Concurrent callers whose stored access token is expired, or whose request rejected the same token, serialize only refresh-and-save work, re-check credentials after acquiring the guard, and reuse the token saved by the first refresher; valid-token requests do not acquire the guard. - `repository-scoped Agent Trace DB`: Active Agent Trace storage shape where one logical Git repository maps to `/sce/repos//agent-trace.db`. The current seam is `RepositoryAgentTraceDb = TursoDb` in `cli/src/services/agent_trace_db/repository.rs`, backed by the fresh multi-statement `001_repository_schema` baseline plus the additive `002_repository_source_instance_id` migration, with `repository_metadata` (`repository_id` plus `source_instance_id`) plus repository-level trace tables, no `checkout_id` columns, and typed repository-level insert helpers for diff traces, post-commit intersections, Agent Trace rows, messages, and parts. Hook runtime, Agent Trace setup/lifecycle, and `sce sync` resolve repository-scoped storage through `agent_trace_storage`. This is the sole Agent Trace DB adapter; the checkout-scoped adapter and former trace inspection surface were removed by the `retire-legacy-agent-trace-db` plan. - `source_instance_id`: Physical-database identity column on `repository_metadata`, independent of the logical `repository_id`. Added by the additive `002_repository_source_instance_id` migration (existing/placeholder rows default to an empty string); generated once per physical `agent-trace.db` by application code (`generate_source_instance_id()`, UUID v4 today) and validated with `is_valid_source_instance_id()` (non-empty once trimmed) — never generated in SQL and never derived from `repository_id`, remote URL, checkout ID, filesystem path, hostname, or user/workspace identity. `RepositoryAgentTraceDb::verify_or_initialize_repository_metadata` claims it with a concurrency-safe `UPDATE ... WHERE source_instance_id = ''`, so concurrent first opens of the same physical database converge on one winner and an already-valid value is never overwritten; the value stays stable across reopen and repeated `sce setup` runs. Two independently created databases for the same logical repository (for example two clones) get different `source_instance_id` values. See `context/sce/agent-trace-db.md`. -- `checkout registry` (removed): The central JSON registry at `/sce/checkout-registry.json` was removed in the `remove-checkout-registry` plan. The later trace inspection surface was also removed; current checkout IDs are diagnostic metadata only and are not discovered as command targets. Any pre-migration `/sce/agent-trace-*.db` files remain untouched. See `context/cli/checkout-identity.md`. +- `checkout registry` (removed): The central JSON registry at `/sce/checkout-registry.json` was removed in the `remove-checkout-registry` plan. The later trace inspection surface was also removed, and checkout identity itself was later removed by the `remove-checkout-id` plan. Any pre-migration `/sce/agent-trace-*.db` files remain untouched. See `context/cli/checkout-identity.md`. - `generated OpenCode plugin registration contract`: Current generated-config contract where `config/.opencode/opencode.json` serializes the OpenCode `plugin` field from canonical Pkl sources for SCE-managed plugins only; the current registered paths are `./plugins/sce-bash-policy.ts` and `./plugins/sce-agent-trace.ts`. Claude does not use an OpenCode-style plugin manifest; Claude bash-policy enforcement is registered through generated `.claude/settings.json` as a `PreToolUse` `Bash` command hook running `sce policy bash`. - `root Biome contract`: Repository-root formatting/linting contract owned by `biome.json`, currently scoped only to `npm/**` and the shared `config/lib/**` plugin package root with package-local `node_modules/**` excluded; the canonical execution path is the root Nix dev shell (`nix develop -c biome ...`). - `cli flake checks`: Check derivations in root `flake.nix` (`checks..cli-tests`, `cli-clippy`, `cli-fmt`), plus `pkl-generated`, `workflow-actionlint`, split npm/config-lib JS checks, and Linux-only Flatpak checks; invoked via `nix flake check` at repo root. @@ -111,7 +111,7 @@ - `setup required-hook install orchestration`: Setup-service flow in `cli/src/services/setup/mod.rs` (`install_required_git_hooks`, backed by the rename-injectable `install_required_git_hooks_with_rename`) that resolves repository root + effective hooks directory via git truth, then for each hook computes the bytes to stage with the `setup hook-merge seam` (`hook_merge::merge_or_create_hook`) instead of writing the canonical asset verbatim, reports deterministic per-hook outcomes (`Installed`, `Updated`, `Skipped`) against that merged content plus the executable bit, enforces executable permissions, sets `RequiredHookInstallResult.unreachable_block_advisory` (rendered as a named advisory line) when an appended block would be unreachable, and uses the `setup atomic-swap` policy (see `setup atomic-swap`) — staged content is renamed directly over an existing hook without unlinking it first — with deterministic recovery guidance on swap failure. - `setup hooks CLI mode`: `sce setup` behavior activated by `--hooks` (with optional `--repo `), supporting both hooks-only runs and composable target+hooks runs in one invocation; implemented through `cli/src/services/setup/command.rs` + `cli/src/services/setup/mod.rs`, enforces deterministic compatibility validation (`--repo` requires `--hooks`; target flags remain mutually exclusive), and emits stable setup/hook status output. - `setup repo gate`: Preflight check in `cli/src/services/setup/command.rs` that calls `cli/src/services/setup/mod.rs` (`ensure_git_repository`) and validates the effective named-remote URL before any setup writes begin; all `sce setup` modes (config-only, hooks-only, combined, interactive, and `--bootstrap-context`) require a Git repository and configured remote. The setup-owned `GitRepositoryResolutionError` maps only a positively identified non-Git directory to `setup.not_git_repository`, while missing/empty configured remote URLs map to `setup.not_git_remote`; nonexistent, inaccessible, process, malformed-output, and other Git/remote-lookup failures map to `general.unexpected_failure`, with technical sources preserved through the CLI user-error boundary. -- `setup local bootstrap`: Pre-install setup bootstrap behavior now owned by lifecycle providers: `ConfigLifecycle::setup` creates missing `.sce/config.json` with the canonical versioned schema-only payload (`{"$schema": "https://sce.crocoder.dev/v/config.json"}`, using the CLI release version), `LocalDbLifecycle::setup` initializes the canonical local DB via `LocalDb::new()`, and `AgentTraceDbLifecycle::setup` creates/reuses checkout identity, resolves repository identity, initializes the repository-scoped Agent Trace DB via `agent_trace_storage`, and records repository ID, checkout ID, and `database_path`; the setup command aggregates these calls before config/hooks dispatch across all normal setup modes after context baseline bootstrap. +- `setup local bootstrap`: Pre-install setup bootstrap behavior now owned by lifecycle providers: `ConfigLifecycle::setup` creates missing `.sce/config.json` with the canonical versioned schema-only payload (`{"$schema": "https://sce.crocoder.dev/v/config.json"}`, using the CLI release version), `LocalDbLifecycle::setup` initializes the canonical local DB via `LocalDb::new()`, and `AgentTraceDbLifecycle::setup` resolves repository identity, initializes the repository-scoped Agent Trace DB via `agent_trace_storage`, and records repository ID and `database_path`; the setup command aggregates these calls before config/hooks dispatch across all normal setup modes after context baseline bootstrap. - `setup context baseline bootstrap`: Additive durable-context tree bootstrap in `cli/src/services/setup/mod.rs` (`bootstrap_context_baseline`) that create-if-missing writes neutral baseline Markdown files, working directories, and `context/tmp/.gitignore` via `RepoPaths` accessors. `sce setup --bootstrap-context` is the dedicated context-only mode and must be used alone; every normal successful setup path also ensures the same baseline after the Git gate and before lifecycle/config install work without overwriting existing content. - `CLI redaction-safe diagnostics contract`: baseline security behavior implemented via `cli/src/services/security.rs` (`redact_sensitive_text`) and applied to app-level errors, setup git-diagnostic surfacing, and observability output sinks so common secret-bearing token forms are masked before emission. - `setup directory write-permission probe`: deterministic pre-write guard implemented in `cli/src/services/security.rs` (`ensure_directory_is_writable`) and used by setup install/hook flows to fail fast with actionable remediation when target directories are not writable. @@ -133,7 +133,7 @@ - `RunOutcome`: Generic final render payload in `cli/src/services/app_support.rs` (`RunOutcome`) carrying a command result, optional startup diagnostic, and optional logger implementing the logger trait boundary. Production construction in `cli/src/app.rs` uses the concrete observability logger, while rendering is not hardcoded to that production type. - `AppContext`: Generic borrowed dependency view in `cli/src/app.rs` passed through static command dispatch. `AppRuntime` owns the concrete production logger, telemetry, filesystem, and git implementations; `AppContext` stores references to those dependencies plus an optional `repo_root: Option`, not owned `Arc` trait objects. Because it borrows from `AppRuntime`, `AppContext` is a lightweight, short-lived view and must not be stored long-term (e.g., in structs or across await points). The `repo_root` field is `None` at startup and command paths can derive a repo-root-scoped context with `AppContext::with_repo_root(...)` / `ContextWithRepoRoot`, preserving the borrowed runtime dependencies while attaching the resolved root. Narrow accessor traits (`HasLogger`, `HasTelemetry`, `HasFs`, `HasGit`, `HasRepoRoot`) let command and lifecycle call sites express capability requirements without depending on the full production context type; logger/telemetry/fs/git accessors use associated concrete capability types and return `&Self::{Capability}` rather than object-erased `&dyn ...` values. - `CommandRegistry`: Static command-name catalog in `cli/src/services/command_registry.rs`; populated by `build_default_registry()` and carried by `AppRuntime` during command dispatch. It exposes deterministic command-name membership for the current top-level command catalog (`help`, `auth`, `config`, `setup`, `doctor`, `hooks`, `policy`, `sync`, `version`, and `completion`) while actual parsed command payloads are represented by the `RuntimeCommand` enum rather than zero-arg boxed constructors. -- `ServiceLifecycle`: Compile-safe lifecycle trait seam in `cli/src/services/lifecycle.rs` with default no-op generic `diagnose`, `fix`, and `setup` methods over `C: HasRepoRoot`; it exposes lifecycle-owned health, fix, and setup result types, while doctor/setup adapt those records at orchestration boundaries before rendering command-owned output. The hooks service has `HooksLifecycle` for hook rollout diagnosis/fix/setup, the config service has `ConfigLifecycle` for global/repo-local config validation plus repo-local config bootstrap, local_db has `LocalDbLifecycle` for canonical local DB path health/bootstrap/setup, auth_db has `AuthDbLifecycle` for canonical auth DB path health/bootstrap/setup, and agent_trace_db has `AgentTraceDbLifecycle` for repository identity resolution, checkout identity setup for diagnostics, setup-time repository-scoped Agent Trace DB initialization, and repository Agent Trace DB path health/bootstrap, returning an actionable "requires a Git repository" diagnostic outside repository context (the former global parent fallback was removed by the `retire-legacy-agent-trace-db` plan). Doctor runtime aggregates the static provider catalog for `diagnose` and `fix`; setup command aggregates providers for `setup` in order (config → local_db → auth_db → agent_trace_db → hooks when requested). +- `ServiceLifecycle`: Compile-safe lifecycle trait seam in `cli/src/services/lifecycle.rs` with default no-op generic `diagnose`, `fix`, and `setup` methods over `C: HasRepoRoot`; it exposes lifecycle-owned health, fix, and setup result types, while doctor/setup adapt those records at orchestration boundaries before rendering command-owned output. The hooks service has `HooksLifecycle` for hook rollout diagnosis/fix/setup, the config service has `ConfigLifecycle` for global/repo-local config validation plus repo-local config bootstrap, local_db has `LocalDbLifecycle` for canonical local DB path health/bootstrap/setup, auth_db has `AuthDbLifecycle` for canonical auth DB path health/bootstrap/setup, and agent_trace_db has `AgentTraceDbLifecycle` for repository identity resolution, setup-time repository-scoped Agent Trace DB initialization, and repository Agent Trace DB path health/bootstrap, returning an actionable "requires a Git repository" diagnostic outside repository context (the former global parent fallback was removed by the `retire-legacy-agent-trace-db` plan). Doctor runtime aggregates the static provider catalog for `diagnose` and `fix`; setup command aggregates providers for `setup` in order (config → local_db → auth_db → agent_trace_db → hooks when requested). - `lifecycle provider catalog`: Shared factory in `cli/src/services/lifecycle.rs` (`lifecycle_providers(include_hooks)`) that returns static `LifecycleProvider` enum values in deterministic config → local_db → auth_db → agent_trace_db → hooks order, used by doctor with hooks included and by setup with hooks included only when requested. The enum owns concrete-provider dispatch for `id`, `diagnose`, `fix`, and `setup` without boxed provider trait objects or `&dyn HasRepoRoot` lifecycle context erasure. - `sce config command surface`: Implemented top-level CLI command routed by `cli/src/app.rs` to `cli/src/services/config/mod.rs` (with schema/file-parsing delegated to `cli/src/services/config/schema.rs`, runtime precedence resolution delegated to `cli/src/services/config/resolver.rs`, and text/JSON output construction delegated to `cli/src/services/config/render.rs`), exposing `show`, `validate`, and `--help` for deterministic runtime config inspection and validation; `show` reports resolved flat logging observability values with provenance, while `validate` reports pass/fail plus validation issues and warnings only. - `sce version command surface`: Implemented top-level CLI command routed by `cli/src/app.rs` to `cli/src/services/version/mod.rs` plus `cli/src/services/version/command.rs`, exposing deterministic runtime identification output in text form by default and JSON form via `--format json`. @@ -186,7 +186,7 @@ - `agent trace commit-msg co-author policy`: Current contract in `cli/src/services/hooks/mod.rs` (`apply_commit_msg_coauthor_policy`) that applies exactly one canonical trailer (`Co-authored-by: SCE `) only when attribution hooks are enabled, SCE is not disabled, and the staged-diff AI-overlap preflight confirms AI/editor evidence (`StagedDiffAiOverlapResult::Overlap`); `NoOverlap` and `Error` both suppress the trailer, with `Error` logged via `sce.hooks.commit_msg.ai_overlap_error`; duplicate canonical trailers are deduped idempotently. - `local DB migration contract`: `cli/src/services/local_db/mod.rs` delegates migration execution to `TursoDb` through the `DbSpec::migrations()` contract. The current `LocalDbSpec` migration list is empty, so `LocalDb::new()` opens/creates the canonical local DB without creating local tables. - `hook no-op baseline`: Current `cli/src/services/hooks/mod.rs` runtime posture where `pre-commit` and `post-rewrite` return deterministic no-op status text, `commit-msg` is a gated mutating path, `post-commit` persists intersections and built Agent Trace payloads without post-commit file artifacts, `diff-trace` validates STDIN payloads, resolves Claude `model_id` with `direct > exact transcript > exact session/agent state > bridge-derived chain state > NULL` precedence, persists direct `tool_version`, applies stored `session_id` prefixes (`oc_`/`cc_`/`pi_`), and inserts DB-only AgentTraceDb rows, `conversation-trace` is the active message/part intake path, and `codex` (`cli/src/services/hooks/codex/`) classifies its own raw hook JSON into four supported dispatch arms — `UserPromptSubmit` and `Stop` capture real conversation evidence, `PreToolUse(Bash)` delegates to the existing Bash policy engine, `PostToolUse(apply_patch)` parses/normalizes/persists a `diff_traces` row using event-scoped synthetic line identities — with every other event/tool combination (including `PreToolUse(apply_patch)`) and malformed STDIN failing open as a no-op. `session-model` is no longer a supported hook route. -- `sce doctor` operator-health contract: `cli/src/services/doctor/mod.rs` is the stable doctor entrypoint, with focused `doctor/{inspect,render,fixes,types}.rs` submodules implementing the current approved operator-health surface in `context/sce/agent-trace-hook-doctor.md`: `sce doctor --fix` selects repair intent, Agent Trace DB discovery is repository-scoped only (the checkout-scoped `sce trace --legacy` surface was removed by the `retire-legacy-agent-trace-db` plan), and output exposes deterministic doctor mode, readiness, stable problem taxonomy/fixability fields, checkout/database records, and fix-result records. The runtime validates state-root resolution, global and repo-local `sce/config.json` readability/schema health, local DB and repository-scoped Agent Trace DB path/health, DB-parent readiness barriers, git availability, non-repo vs bare-repo targeting failures, effective hook-path source resolution, required hook presence/executable/content drift against canonical embedded hook assets, and repo-root installed OpenCode, Claude, Pi, plus Codex integration content health. Human text mode uses the approved sectioned layout (`Environment`, `Configuration` with checkout identity plus repository-scoped Agent Trace DB rows when available, `Repository`, `Git Hooks`, `Integrations`), `SCE doctor diagnose` / `SCE doctor fix` headers, bracketed `[PASS]`/`[FAIL]`/`[MISS]` status tokens with shared-style green/red colorization when enabled, simplified `label (path)` row formatting, top-level-only hook rows, and integration parent/child rows where missing files surface as `[MISS]`, mismatches/read failures as `[FAIL]`, and affected parent groups as `[FAIL]`. Agent Trace DB rows include repository ID, identity source, safe canonical identity, configured remote name, and never raw remote URLs. Current integration groups include `OpenCode plugins`, `OpenCode agents`, `OpenCode commands`, `OpenCode skills`, `ClaudeCode plugins`, `ClaudeCode commands`, `ClaudeCode skills`, `Pi prompts`, `Pi skills`, `Pi extensions`, `Codex skills`, and `Codex hooks`; Claude `settings.json` plus `hooks/**` belong to `ClaudeCode plugins`, including `.claude/hooks/run-sce-or-show-install-guidance.sh`, Pi `prompts/**` and `skills/**` map to the Pi groups, and Codex's `.agents/skills/**` plus `.codex/hooks.json`/`.codex/hooks/**` map to the Codex groups (the latter also carrying a Codex hook trust/review reminder when unhealthy). Fix mode reuses canonical setup hook installation for missing/stale/non-executable required hooks and missing hooks directories and can bootstrap canonical missing SCE-owned DB parent directories. +- `sce doctor` operator-health contract: `cli/src/services/doctor/mod.rs` is the stable doctor entrypoint, with focused `doctor/{inspect,render,fixes,types}.rs` submodules implementing the current approved operator-health surface in `context/sce/agent-trace-hook-doctor.md`: `sce doctor --fix` selects repair intent, Agent Trace DB discovery is repository-scoped only (the checkout-scoped `sce trace --legacy` surface was removed by the `retire-legacy-agent-trace-db` plan), and output exposes deterministic doctor mode, readiness, stable problem taxonomy/fixability fields, database records, and fix-result records. The runtime validates state-root resolution, global and repo-local `sce/config.json` readability/schema health, local DB and repository-scoped Agent Trace DB path/health, DB-parent readiness barriers, git availability, non-repo vs bare-repo targeting failures, effective hook-path source resolution, required hook presence/executable/content drift against canonical embedded hook assets, and repo-root installed OpenCode, Claude, Pi, plus Codex integration content health. Human text mode uses the approved sectioned layout (`Environment`, `Configuration` with repository-scoped Agent Trace DB rows when available, `Repository`, `Git Hooks`, `Integrations`), `SCE doctor diagnose` / `SCE doctor fix` headers, bracketed `[PASS]`/`[FAIL]`/`[MISS]` status tokens with shared-style green/red colorization when enabled, simplified `label (path)` row formatting, top-level-only hook rows, and integration parent/child rows where missing files surface as `[MISS]`, mismatches/read failures as `[FAIL]`, and affected parent groups as `[FAIL]`. Agent Trace DB rows include repository ID, identity source, safe canonical identity, configured remote name, and never raw remote URLs. Current integration groups include `OpenCode plugins`, `OpenCode agents`, `OpenCode commands`, `OpenCode skills`, `ClaudeCode plugins`, `ClaudeCode commands`, `ClaudeCode skills`, `Pi prompts`, `Pi skills`, `Pi extensions`, `Codex skills`, and `Codex hooks`; Claude `settings.json` plus `hooks/**` belong to `ClaudeCode plugins`, including `.claude/hooks/run-sce-or-show-install-guidance.sh`, Pi `prompts/**` and `skills/**` map to the Pi groups, and Codex's `.agents/skills/**` plus `.codex/hooks.json`/`.codex/hooks/**` map to the Codex groups (the latter also carrying a Codex hook trust/review reminder when unhealthy). Fix mode reuses canonical setup hook installation for missing/stale/non-executable required hooks and missing hooks directories and can bootstrap canonical missing SCE-owned DB parent directories. - `compact doctor text report`: Current human-readable `sce doctor` rendering collapses healthy state, repository, and typed integration-area checks into the `Environment`/`Repository`/`Integrations` hierarchy, suppresses healthy paths and identity metadata, and uses `--format json` as the full-detail route. The canonical current contract is [doctor human text](sce/doctor-human-text-contract.md). - `cli warnings-denied lint policy`: `cli/Cargo.toml` sets `warnings = "deny"`, so plain `cargo clippy --manifest-path cli/Cargo.toml` already fails on warnings without needing an extra `-- -D warnings` tail. - `agent trace local DB schema migration contract`: Retired `apply_core_schema_migrations` behavior removed from the current runtime during `agent-trace-removal-and-hook-noop-reset` T01; the local DB baseline is now file open/create only. diff --git a/context/overview.md b/context/overview.md index eb1a3d9da..d5ddfd0cd 100644 --- a/context/overview.md +++ b/context/overview.md @@ -25,7 +25,7 @@ The same runtime also emits stable CLI stderr diagnostics: internal failures use The app runtime now also includes a structured observability baseline in `cli/src/services/observability.rs`: deterministic env-controlled log threshold/format (`SCE_LOG_LEVEL` defaults to `error`; `SCE_LOG_FORMAT` defaults to `text`), default-backed log-directory routing (`SCE_LOG_DIR` / config-file `log_dir` / `/sce/logs`) with per-operation machine-local dated file selection, optional session filename partitioning, and creation-triggered retention of direct regular `*.log` files to 10 entries, stable lifecycle event IDs, normal logger records routed only to files when file logging is enabled and only error-level logger records routed to `stderr` when file logging is disabled, direct file-write diagnostics remaining on `stderr`, and `observability::traits` boundaries for logger and telemetry behavior. The app command dispatcher now enforces a centralized stdout/stderr stream contract in `cli/src/app.rs`: command success payloads are emitted on stdout only, while redacted user-facing diagnostics and text-mode sync progress are emitted on stderr; JSON sync remains silent. `cli/src/app.rs` also now runs through explicit startup phases — dependency check, observability config resolution, runtime initialization, command parse/execute, and output rendering — with the app runtime carrying logger/telemetry plus static command-catalog state across those phases while preserving the existing exit-code and degraded-startup contracts. Within that lifecycle, `parse_command_phase` delegates clap-to-runtime conversion to `cli/src/services/parse/command_runtime.rs`, which returns a static `RuntimeCommand` enum, `services::app_support::execute_command_phase` logs around enum-owned `execute_with_stderr(...)` dispatch, and generic `RunOutcome` rendering logs classified errors through the logger trait boundary without coupling render support to the production logger type. Command payload structs for `help`, `version`, `completion`, `auth`, `config`, `setup`, `doctor`, `hooks`, and `sync` live in service-owned `command.rs` files; `cli/src/services/command_registry.rs` owns the deterministic static command-name catalog and enum variants instead of boxed command trait objects. The CLI now also enforces a shared output-format parser contract in `cli/src/services/output_format.rs`, with canonical `--format ` parsing and command-specific actionable invalid-value guidance reused by `config` and `version` services. A compile-safe service lifecycle seam also exists in `cli/src/services/lifecycle.rs`: `ServiceLifecycle` exposes default no-op `diagnose`, `fix`, and `setup` methods against the narrow `HasRepoRoot` accessor, uses lifecycle-owned health/fix/setup result types, and owns the shared static `LifecycleProvider` enum catalog/factory with deterministic config → local*db → auth_db → agent_trace_db → hooks ordering and no boxed provider aggregation. Hooks has a `services/hooks/lifecycle.rs` provider for hook rollout diagnosis/fix/setup, config has a `services/config/lifecycle.rs` provider for global/repo-local config validation plus repo-local config bootstrap, local_db has a `services/local_db/lifecycle.rs` provider for canonical local DB path health, parent-directory readiness/bootstrap, and `LocalDb::new()` setup, auth_db has a `services/auth_db/lifecycle.rs` provider for canonical auth DB path health, parent-directory readiness/bootstrap, and `AuthDb::new()` setup, and agent_trace_db has a `services/agent_trace_db/lifecycle.rs` provider for repository-scoped Agent Trace DB setup and repository DB path health/parent readiness from resolved repository identity, returning an actionable "requires a Git repository" diagnostic outside repository context (no global/checkout fallback path). Doctor runtime aggregates the full shared provider catalog for `diagnose` and `fix` and adapts lifecycle records into doctor-owned output records; setup command aggregates the shared provider catalog for `setup` with hooks included only when requested and adapts lifecycle setup outcomes before rendering setup-owned messages. -Agent Trace lifecycle setup now resolves repository storage, creates/reuses checkout identity for diagnostics, and initializes `/sce/repos//agent-trace.db` via `RepositoryAgentTraceDb`; hook runtime lazy initialization uses the same repository storage resolver when setup has not prepared the DB or schema metadata is incomplete. +Agent Trace lifecycle setup now resolves repository storage and initializes `/sce/repos//agent-trace.db` via `RepositoryAgentTraceDb`; hook runtime lazy initialization uses the same repository storage resolver when setup has not prepared the DB or schema metadata is incomplete. The CLI now also includes a shared text styling service in `cli/src/services/style.rs` that provides deterministic color enablement via `owo-colors`, automatic TTY detection, and `NO_COLOR` compliance for human-facing text output; stdout help/text surfaces, stderr diagnostics, and interactive prompt-adjacent text now reuse that shared styling policy while JSON, completion, and other non-interactive/machine-readable flows remain unstyled. The service exports color-detection, conditional styling, help/diagnostic/label/prompt styling, and `banner_with_gradient()` helpers for use across command surfaces while preserving pipe-safe output for non-interactive environments. The `setup` command includes an `inquire`-backed target-selection flow: default interactive selection for OpenCode/Claude/Pi/Codex/All with required-hook installation in the same run, explicit non-interactive target flags (`--opencode`, `--claude`, `--pi`, `--codex`, `--all`), standalone `--bootstrap-context` for additive durable-context baseline creation without integration installs, deterministic mutually-exclusive validation, and non-destructive cancellation exits; the former `--both` flag was removed in favor of `--all` (opencode+claude+pi+codex). Every normal successful setup path also ensures the same context baseline after the Git gate. Workflows the catalog marks optional are installed only when a repository opts in: the repeatable `sce setup --workflow ` flag names the selection for a run, an omitted flag reuses the selection persisted in `integrations.optional_workflows`, and the resolved selection filters the installed target assets and is written back to repo-local config. Interactive runs ask for the selection instead: a multi-select prompt follows target selection with every row unchecked on a first run and pre-checked from the persisted selection afterwards, cancelling it exits non-destructively like the target prompt, and the prompt is skipped when the catalog marks no workflow optional. `brownfield` is currently the only optional workflow, so a default run installs the five core workflows and no brownfield assets. `sce doctor` scopes its integration checks to that same recorded selection, so it never reports an unselected optional workflow's files as missing. For repository generation consumers, `config/pkl/generator-inputs.txt` declares the canonical Pkl/plugin input set and `scripts/produce-cli-generated-input.sh` owns its discovery, two-pass `config/pkl/generate.pkl` evaluation, determinism comparison, payload/input inventories, in-flight input-mutation rejection, atomic handoff publication, and staging cleanup. `scripts/run-cli-cargo.sh` creates a fresh temporary destination, delegates generation to that producer, invokes the requested Cargo workflow with `SCE_CLI_GENERATED_INPUT_DIR`, and removes the handoff after Cargo success, failure, or handled signals. `config/pkl/check-generated.sh` delegates the same production mechanics while retaining contract and path assertions. The root flake also runs `codex-hook-command`, which generates the Codex assets and verifies root, nested-cwd, spaced-path, stdin-forwarding, and fail-open invocation behavior against a stub `sce`. `scripts/prepare-cli-generated-assets.sh` moves the producer-validated Pkl payload and checksums into the unchanged package fallback, adds hooks, migrations, and the Agent Trace schema, and appends only those static checksums to the combined inventory. The root flake's pre-Cargo `cliGeneratedInput` derivation invokes the same producer from a declarative source containing the producer plus its declared inputs. `cli/build.rs` rejects missing, incomplete, modified, or stale repository handoffs, copies the validated payload into Cargo `OUT_DIR/pkl-generated`, stages static inputs under `OUT_DIR/static`, and writes setup-asset, optional-workflow-catalog, and migration Rust manifests into `OUT_DIR`; it never invokes Pkl. Published crates carry the ignored packaging-only fallback, and unpacked downstream builds validate and copy it into their own `OUT_DIR` without requiring Pkl or parent repository paths. diff --git a/context/plans/remove-checkout-id.md b/context/plans/remove-checkout-id.md new file mode 100644 index 000000000..80c063455 --- /dev/null +++ b/context/plans/remove-checkout-id.md @@ -0,0 +1,83 @@ +# Plan: remove-checkout-id + +## Change summary + +Remove the checkout identity feature: the `cli/src/services/checkout/` module (`resolve_git_dir`, `read_checkout_id`, `get_or_create_checkout_id`), the `/sce/checkout-id` file it creates/reads, and every diagnostic-only consumer of it in `agent_trace_storage`, `agent_trace_db` setup lifecycle, and `sce doctor`. Checkout ID is currently never persisted on Agent Trace rows and does not select the active database; it exists solely as a diagnostic label in setup output and `sce doctor --format json`. This replaces existing behavior by dropping that diagnostic surface, mirroring the repository's completed `remove-checkout-registry` removal of the related checkout registry feature. + +## Acceptance criteria + +- [ ] AC1: `sce` no longer creates or reads `/sce/checkout-id`, and no code references the removed `cli/src/services/checkout/` module. + - Validate: `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id" cli/src` returns no matches. +- [ ] AC2: `sce setup` and `sce doctor` (text and JSON) no longer mention checkout identity. + - Validate: run `sce setup` in a scratch repo and confirm the output has no "Agent Trace checkout identity" line; run `sce doctor --format json` and confirm the payload has no `checkout_identity` key. +- [ ] AC3: Durable context describes checkout identity as removed, with no remaining claim that current runtime creates, reads, or reports it. + - Validate: `rg -in "checkout identity|checkout_id|checkout-id" context/` shows only past-tense/removed framing, consistent with the existing `checkout registry` (removed) glossary entry. + +### Full validation + +- `nix flake check` +- `nix run .#pkl-check-generated` + +### Context sync + +- `context/cli/checkout-identity.md` +- `context/cli/agent-trace-storage.md` +- `context/sce/agent-trace-db.md` +- `context/sce/agent-trace-hook-doctor.md` +- `context/cli/cli-command-surface.md` +- `context/cli/service-lifecycle.md` +- `context/context-map.md` +- `context/glossary.md` +- `context/architecture.md` +- `context/overview.md` + +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + +## Constraints and non-goals + +- **In scope:** `cli/src/services/checkout/` (deletion) and its module declaration in `cli/src/services/mod.rs`; `checkout_id` field and usage in `cli/src/services/agent_trace_storage/mod.rs` (including affected tests); `checkout_id` field, `setup.checkout_id` usage, and the "Agent Trace checkout identity" message line in `cli/src/services/agent_trace_db/lifecycle.rs` (including affected tests); `CheckoutIdentityHealth`, `checkout_identity` field, `collect_checkout_identity_health`, and JSON rendering in `cli/src/services/doctor/{types,inspect,render}.rs`; the ten context files listed under Context sync. +- **Out of scope:** repository identity resolution (`repository_identity`), Agent Trace DB schema/migrations, `sce sync`, and how the repository-scoped Agent Trace DB path is selected or opened. The already-completed `checkout registry` removal (`checkout-registry.json`, `sce doctor dbs`) is unrelated prior work and stays untouched. +- **Constraints:** `nix flake check` (cli-tests, cli-clippy, cli-fmt) and `nix run .#pkl-check-generated` must keep passing; no replacement identity mechanism is introduced. +- **Non-goal:** deleting or migrating any `/sce/checkout-id` file already on disk. SCE never touches pre-existing on-disk artifacts (the same convention already applied to legacy `agent-trace-.db` files); a stray file is simply left inert. + +## Assumptions + +- "Remove checkout id" refers to the checkout identity infrastructure in `context/cli/checkout-identity.md` / `cli/src/services/checkout/`, not the separate, already-removed `checkout registry` feature (`remove-checkout-registry` plan). +- Removing `checkout_identity` from `sce doctor --format json` output is an acceptable diagnostic-surface change; no consumer depends on that field for correctness, only for human inspection. + +## Task stack + +- [x] T01: `Remove checkout identity code and its call sites` (status:done) + - Task ID: T01 + - Scope: In — delete `cli/src/services/checkout/mod.rs` and its `pub mod checkout;` declaration in `cli/src/services/mod.rs`; remove the `checkout_id` field and the `get_or_create_checkout_id`/`resolve_git_dir` calls (plus the now-invalid `use` import) from `cli/src/services/agent_trace_storage/mod.rs`, updating or removing tests that assert on `checkout_id`; remove the `checkout_id` field from `RepositoryDatabaseSetup`, the `setup.checkout_id` usage, and the "Agent Trace checkout identity: {}" segment of the setup message in `cli/src/services/agent_trace_db/lifecycle.rs`, updating its tests; remove `CheckoutIdentityHealth`, the `checkout_identity` field on `HookDoctorReport`, `collect_checkout_identity_health`, its two call sites, and the `checkout_identity` JSON key from `cli/src/services/doctor/{types,inspect,render}.rs`. Out — repository identity resolution, Agent Trace DB schema/migrations, `sce sync`, durable context files. + - Dependencies: none + - Done when: `cli/src/services/checkout/` no longer exists; `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id|CheckoutIdentityHealth" cli/src` returns no matches; `nix flake check` passes. + - Verify: `nix flake check` (cli-tests, cli-clippy, cli-fmt); `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id|CheckoutIdentityHealth" cli/src` returns nothing. + - Completed: 2026-09-13 + - Files changed: `cli/src/services/mod.rs`, `cli/src/services/checkout/mod.rs` (deleted), `cli/src/services/agent_trace_storage/mod.rs`, `cli/src/services/agent_trace_db/lifecycle.rs`, `cli/src/services/doctor/types.rs`, `cli/src/services/doctor/inspect.rs`, `cli/src/services/doctor/render.rs` + - Result: Deleted `cli/src/services/checkout/` and its module declaration; removed `checkout_id`/`resolve_git_dir`/`get_or_create_checkout_id` from `agent_trace_storage::open_storage_with` (and the now-unused `context` parameter threaded through `open_storage`/`open_storage_for_hook_runtime`/`open_storage_with`, plus the resulting unused `anyhow::Context` import), the `checkout_id` field and setup-message segment in `agent_trace_db::lifecycle::RepositoryDatabaseSetup`, and `CheckoutIdentityHealth`/`collect_checkout_identity_health`/its two call sites/the `checkout_identity` JSON key in the doctor module. Updated four `agent_trace_storage` tests that asserted on `checkout_id`, renaming two whose names referenced "checkout_ids"/"checkout_id" since that behavior no longer exists. + - Verify (actual): `nix build .#checks.x86_64-linux.cli-tests .#checks.x86_64-linux.cli-clippy .#checks.x86_64-linux.cli-fmt` — all three passed (665 tests passed, 0 failed; clippy and fmt clean). `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id|CheckoutIdentityHealth" cli/src` returns only `cli/src/services/agent_trace_db/repository.rs` (a pre-existing, out-of-scope schema-invariant test/comment documenting the unrelated absence of a `checkout_id` DB column — not the checkout identity service removed by this task). + - Context impact: `domain` (diagnostic-only feature removal, no architecture/ownership/terminology-boundary change). During this task's own context-synchronization pass, all ten durable context files listed under "Context sync", plus `context/sce/shared-turso-db.md` (found by sweep, not originally listed), were updated to stop describing checkout identity as active runtime behavior; see the Task context synchronization phase report for this task. T02 remains to do a final targeted review/verification pass against its own Done-when/Verify criteria. + - Assumptions/deviations: Per the reviewed assumption, the bare `rg ... cli/src` done-check text is read as excluding the noted out-of-scope `repository.rs` residual match, which is unrelated prior work (DB schema invariant) rather than a remnant of the removed checkout identity service. + - Context synchronization: synced + +- [ ] T02: `Update durable context to reflect checkout identity removal` (status:todo) + - Task ID: T02 + - Scope: In — `context/cli/checkout-identity.md`, `context/cli/agent-trace-storage.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hook-doctor.md`, `context/cli/cli-command-surface.md`, `context/cli/service-lifecycle.md`, `context/context-map.md`, `context/glossary.md`, `context/architecture.md`, `context/overview.md`: rewrite or remove every claim that checkout identity is created, read, resolved, or reported by current runtime behavior, following the past-tense "removed" framing the `checkout registry` glossary entry already uses for the related prior removal. Out — code changes, plan files, decision records. + - Dependencies: T01 + - Done when: none of the ten files claim checkout identity is created, stored, resolved, or surfaced by current behavior; `rg -in "checkout identity|checkout_id|checkout-id" context/` shows only past-tense/removed framing. + - Verify: `rg -in "checkout identity|checkout_id|checkout-id" context/cli context/sce context/context-map.md context/glossary.md context/architecture.md context/overview.md`, reviewed by hand against the T01 code state. + - Context synchronization: pending + +## Open questions + +None. The removal is fully precedented by the completed `remove-checkout-registry` plan and scoped to a diagnostic-only surface with no persisted-data implications. diff --git a/context/sce/agent-trace-db.md b/context/sce/agent-trace-db.md index a430f4121..8e9d2aa1b 100644 --- a/context/sce/agent-trace-db.md +++ b/context/sce/agent-trace-db.md @@ -59,7 +59,6 @@ The active repository-scoped Agent Trace DB path is resolved from repository ide - Function: `agent_trace_db_path_for_repository(repository_id)` in `cli/src/services/default_paths.rs` - Path template: `/sce/repos//agent-trace.db` - Repository ID source: explicit `agent_trace.repository_id` config, otherwise canonicalized configured Git remote URL (`agent_trace.repository_remote`, default `origin`) -- Checkout ID source: `/sce/checkout-id`, where `` comes from `git rev-parse --git-dir`; checkout ID is diagnostic metadata only and is not stored on Agent Trace rows. Example state layout: @@ -92,7 +91,7 @@ Repositories created before `003_claude_model_state` must rerun `sce setup` to a The shared `TursoDb` runner records applied IDs in the database-local `__sce_migrations` table. Migration SQL is executed with `execute_batch`, so the one-file repository baseline can contain multiple statements while still recording one migration ID. -Repository-scoped storage resolution first resolves `agent_trace.repository_id` / `agent_trace.repository_remote` through config, then splits by caller into two resolution paths in `agent_trace_storage`, both sharing the same identity/checkout-ID setup and returning the same `ResolvedAgentTraceStorage { metadata: RepositoryMetadata, .. } `: +Repository-scoped storage resolution first resolves `agent_trace.repository_id` / `agent_trace.repository_remote` through config, then splits by caller into two resolution paths in `agent_trace_storage`, both sharing the same identity setup and returning the same `ResolvedAgentTraceStorage { metadata: RepositoryMetadata, .. } `: - **Setup/lifecycle** (`resolve_agent_trace_storage(...)`): tries `RepositoryAgentTraceDb::open_without_migrations_at(path)` + `ensure_schema_ready_for_hooks()` + repository metadata validation first. If the repository DB has not been initialized, metadata is absent, or migrations are incomplete, it falls back to migration-running `RepositoryAgentTraceDb::new_at(path)` and validates/seeds `repository_metadata.repository_id` before returning. This is the only path that may run migration `002` (or any migration). It retries the fast-path/migration sequence for a bounded window during concurrent first opens; if another opener completed the one-file schema but the baseline migration record is missing, the repository adapter records that metadata only after verifying all required repository schema tables already exist. When the fallback also fails, the error context includes the fast-path failure reason (`(fast-path attempt: {fast_error})`) so both failure causes are visible in diagnostics. - **Hook runtime** (`resolve_agent_trace_storage_for_hook_runtime(...)`): opens with `RepositoryAgentTraceDb::open_for_hooks_without_migrations_at(path)` and never falls back to `new_at`/migrations. If `ensure_schema_ready_for_hooks()` fails, it applies only the same narrow concurrent-first-open migration-metadata repair the setup path uses, then re-checks readiness; a missing database or a baseline-only (pre-`002`) database surfaces the existing `Run 'sce setup'.` guidance instead of migrating. Once readiness passes, it calls the same `verify_or_initialize_repository_metadata(repository_id)` to initialize `source_instance_id` when needed. `sce hooks conversation-trace`/`diff-trace`/`commit-msg` (and any other high-frequency hook caller) use this path exclusively via `open_agent_trace_db_for_hook_runtime` in `cli/src/services/hooks/mod.rs`. @@ -182,8 +181,8 @@ Both triggers compare `OLD.*` vs `NEW.*` for all mutable columns (excluding `upd - `diagnose()` resolves repository identity from config or the configured Git remote and reports the repository-scoped Agent Trace DB path and parent-directory readiness. When the DB file exists, it opens the file via `RepositoryAgentTraceDb::open_without_migrations_at` and verifies schema readiness via `ensure_schema_ready_for_hooks`, reporting `AgentTraceDbConnectionFailed` if open fails or `AgentTraceDbSchemaNotReady` if the schema is incomplete. Missing repository identity is a manual problem with `.sce/config.json` / remote guidance. Outside repository context there is no repository identity to select a DB and no global/checkout fallback path; the lifecycle returns an actionable "requires a Git repository" diagnostic, surfaced by `diagnose_agent_trace_db_health` as a manual-only `UnableToResolveStateRoot` problem. - `fix()` bootstraps the resolved repository DB parent directory for auto-fixable parent-readiness problems, with the same global parent fallback outside repository context. -- `setup()` resolves repository storage through `agent_trace_storage`, creates/reuses the current checkout identity for diagnostics, opens/creates `/sce/repos//agent-trace.db` with the repository schema, validates `repository_metadata.repository_id`, and emits setup messaging with the repository ID, checkout ID, `source_instance_id` (`RepositoryMetadata` from the same resolution call), and initialized DB path. Hook runtime lazy initialization remains available for repositories where setup has not run or schema metadata is incomplete. -- `sce doctor` surfaces checkout identity facts where available and lifecycle-owned repository Agent Trace DB health in the `Configuration` section, with `[PASS]`/`[FAIL]`/`[MISS]` status tokens. Outside repository context the lifecycle reports the actionable "requires a Git repository" diagnostic instead of probing a sentinel path. JSON output includes `checkout_identity` when available plus the resolved `agent_trace_db` field. +- `setup()` resolves repository storage through `agent_trace_storage`, opens/creates `/sce/repos//agent-trace.db` with the repository schema, validates `repository_metadata.repository_id`, and emits setup messaging with the repository ID, `source_instance_id` (`RepositoryMetadata` from the same resolution call), and initialized DB path. Hook runtime lazy initialization remains available for repositories where setup has not run or schema metadata is incomplete. +- `sce doctor` surfaces lifecycle-owned repository Agent Trace DB health in the `Configuration` section, with `[PASS]`/`[FAIL]`/`[MISS]` status tokens. Outside repository context the lifecycle reports the actionable "requires a Git repository" diagnostic instead of probing a sentinel path. JSON output includes the resolved `agent_trace_db` field; checkout identity is no longer reported (see `context/cli/checkout-identity.md`). - `sce sync` reads repository DB rows through the read-only export boundary before sending them to the control plane. See [context/cli/sync-command.md](../cli/sync-command.md). ## Runtime writers diff --git a/context/sce/agent-trace-hook-doctor.md b/context/sce/agent-trace-hook-doctor.md index d08f57cd8..28ae84ca1 100644 --- a/context/sce/agent-trace-hook-doctor.md +++ b/context/sce/agent-trace-hook-doctor.md @@ -34,7 +34,7 @@ The runtime in `cli/src/services/doctor/mod.rs` exposes the approved doctor comm - default global/local config-file location reporting, plus validation of existing global and repo-local `sce/config.json` readability and schema compliance (delegated to `ConfigLifecycle::diagnose`) - startup config resolution no longer blocks doctor on invalid default-discovered config files; doctor reaches its own config-validation path, reports those files as problems, and keeps invalid-config remediation manual-only - local DB location reporting, DB parent-directory readiness checks, and existing-DB health validation (delegated to `LocalDbLifecycle::diagnose`) -- local DB reporting plus checkout-aware Agent Trace DB diagnostics; healthy identity and path metadata stay in JSON while human text summarizes them under `Environment` → `Repository identity` +- local DB reporting plus repository-scoped Agent Trace DB diagnostics; healthy identity and path metadata stay in JSON while human text summarizes them under `Environment` → `Repository identity` - explicit git-unavailable, outside-repo, and bare-repo repository-targeting failures - effective hook-path source (`default`, local `core.hooksPath`, global `core.hooksPath`) - repository root and hooks directory resolution when a repository target is detected @@ -129,7 +129,7 @@ The broadened contract for `sce doctor` must cover the following problem invento - local DB and repository-scoped Agent Trace DB parent directories are missing or not writable - local DB and repository-scoped Agent Trace DB bootstrap or health is broken - Agent Trace DB file exists but cannot be opened (connection failure) or has incomplete schema (missing/unapplied migrations) — reported as `AgentTraceDbConnectionFailed` / `AgentTraceDbSchemaNotReady` with manual-only remediation directing to `sce setup` -- Agent Trace checkout ID plus repository-scoped DB path/health remain in the complete report and JSON; human text summarizes them under `Environment` → `Repository identity` without exposing healthy identity metadata. Repository DB records include repository ID, identity source, safe canonical identity, configured remote name, and never raw remote URLs +- Repository-scoped DB path/health remains in the complete report and JSON; human text summarizes it under `Environment` → `Repository identity` without exposing healthy identity metadata. Repository DB records include repository ID, identity source, safe canonical identity, configured remote name, and never raw remote URLs. Checkout identity was removed by the `remove-checkout-id` plan and is no longer reported (see `context/cli/checkout-identity.md`) ### Repository targeting and git readiness @@ -213,7 +213,7 @@ Services implementing `ServiceLifecycle`: - `HooksLifecycle` in `cli/src/services/hooks/lifecycle.rs`: checks hook rollout integrity, required-hook presence/executability/content - `LocalDbLifecycle` in `cli/src/services/local_db/lifecycle.rs`: validates DB path/health, bootstraps DB parent directory - `AuthDbLifecycle` in `cli/src/services/auth_db/lifecycle.rs`: validates encrypted auth DB path/health, bootstraps DB parent directory -- `AgentTraceDbLifecycle` in `cli/src/services/agent_trace_db/lifecycle.rs`: validates repository-scoped Agent Trace DB path/health from resolved repository identity, reports checkout identity as diagnostics when available, and bootstraps the resolved DB parent directory +- `AgentTraceDbLifecycle` in `cli/src/services/agent_trace_db/lifecycle.rs`: validates repository-scoped Agent Trace DB path/health from resolved repository identity, and bootstraps the resolved DB parent directory The `doctor` command aggregates `diagnose` and `fix` across all registered providers. The `setup` command aggregates `setup` across all registered providers in order (config → local_db → auth_db → agent_trace_db → hooks). diff --git a/context/sce/shared-turso-db.md b/context/sce/shared-turso-db.md index 32411bcce..6f1030562 100644 --- a/context/sce/shared-turso-db.md +++ b/context/sce/shared-turso-db.md @@ -61,7 +61,7 @@ The shared module is exported from `cli/src/services/mod.rs` and compile-checked - `cli/src/services/agent_trace_db/mod.rs`: owns the shared Agent Trace insert payloads/helpers. Active hook/runtime paths use the sole `RepositoryAgentTraceDb = TursoDb` adapter from `cli/src/services/agent_trace_db/repository.rs`, selected by `agent_trace_storage` at `/sce/repos//agent-trace.db`, with a one-file repository schema containing repository metadata plus repository-level `diff_traces`, `post_commit_patch_intersections`, `agent_traces`, `messages`, and `parts` tables. The checkout-scoped `AgentTraceDb`/`AgentTraceDbSpec` adapter, its `agent_trace_db_path()` global fallback, and the 15-file migration chain were removed by the `retire-legacy-agent-trace-db` plan. - `cli/src/services/auth_db/mod.rs`: `AuthDb = EncryptedTursoDb`, with `AuthDbSpec` resolving `auth_db_path()` and loading ordered auth migrations where baseline SQL creates `auth_credentials` without `user_id`, with `updated_at`, and a trigger that auto-refreshes `updated_at` on row updates. -All three database areas (local DB, auth DB, Agent Trace DB) have lifecycle providers. `lifecycle_providers(include_hooks)` registers database providers in order `LocalDbLifecycle` → `AuthDbLifecycle` → `AgentTraceDbLifecycle` before optional hooks. Setup initializes local/auth DBs, establishes Agent Trace checkout identity for diagnostics, initializes the repository-scoped Agent Trace DB with migrations/metadata, and reports credential-safe repository identity metadata; hook runtime keeps lazy repository DB initialization/upgrade as a fallback when setup has not run or schema metadata is incomplete. Doctor diagnoses/fixes DB parent/path readiness through lifecycle providers. +All three database areas (local DB, auth DB, Agent Trace DB) have lifecycle providers. `lifecycle_providers(include_hooks)` registers database providers in order `LocalDbLifecycle` → `AuthDbLifecycle` → `AgentTraceDbLifecycle` before optional hooks. Setup initializes local/auth DBs, initializes the repository-scoped Agent Trace DB with migrations/metadata, and reports credential-safe repository identity metadata; hook runtime keeps lazy repository DB initialization/upgrade as a fallback when setup has not run or schema metadata is incomplete. Doctor diagnoses/fixes DB parent/path readiness through lifecycle providers. ## Migration metadata From 3e3d6ee89631fca63092b0136348f2efc122fd67 Mon Sep 17 00:00:00 2001 From: David Abram Date: Sun, 13 Sep 2026 13:52:51 +0200 Subject: [PATCH 2/2] context: Validate checkout identity removal plan Record T02 completion after confirming durable context already reflects the removed checkout identity and the full repository and generated-config validations pass. Plan: remove-checkout-id (T02) --- context/plans/remove-checkout-id.md | 40 +++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/context/plans/remove-checkout-id.md b/context/plans/remove-checkout-id.md index 80c063455..6dbc24531 100644 --- a/context/plans/remove-checkout-id.md +++ b/context/plans/remove-checkout-id.md @@ -6,11 +6,11 @@ Remove the checkout identity feature: the `cli/src/services/checkout/` module (` ## Acceptance criteria -- [ ] AC1: `sce` no longer creates or reads `/sce/checkout-id`, and no code references the removed `cli/src/services/checkout/` module. +- [x] AC1: `sce` no longer creates or reads `/sce/checkout-id`, and no code references the removed `cli/src/services/checkout/` module. - Validate: `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id" cli/src` returns no matches. -- [ ] AC2: `sce setup` and `sce doctor` (text and JSON) no longer mention checkout identity. +- [x] AC2: `sce setup` and `sce doctor` (text and JSON) no longer mention checkout identity. - Validate: run `sce setup` in a scratch repo and confirm the output has no "Agent Trace checkout identity" line; run `sce doctor --format json` and confirm the payload has no `checkout_identity` key. -- [ ] AC3: Durable context describes checkout identity as removed, with no remaining claim that current runtime creates, reads, or reports it. +- [x] AC3: Durable context describes checkout identity as removed, with no remaining claim that current runtime creates, reads, or reports it. - Validate: `rg -in "checkout identity|checkout_id|checkout-id" context/` shows only past-tense/removed framing, consistent with the existing `checkout registry` (removed) glossary entry. ### Full validation @@ -70,14 +70,44 @@ Persist this field in every plan; this is durable plan state, not chat state: - Assumptions/deviations: Per the reviewed assumption, the bare `rg ... cli/src` done-check text is read as excluding the noted out-of-scope `repository.rs` residual match, which is unrelated prior work (DB schema invariant) rather than a remnant of the removed checkout identity service. - Context synchronization: synced -- [ ] T02: `Update durable context to reflect checkout identity removal` (status:todo) +- [x] T02: `Update durable context to reflect checkout identity removal` (status:done) - Task ID: T02 - Scope: In — `context/cli/checkout-identity.md`, `context/cli/agent-trace-storage.md`, `context/sce/agent-trace-db.md`, `context/sce/agent-trace-hook-doctor.md`, `context/cli/cli-command-surface.md`, `context/cli/service-lifecycle.md`, `context/context-map.md`, `context/glossary.md`, `context/architecture.md`, `context/overview.md`: rewrite or remove every claim that checkout identity is created, read, resolved, or reported by current runtime behavior, following the past-tense "removed" framing the `checkout registry` glossary entry already uses for the related prior removal. Out — code changes, plan files, decision records. - Dependencies: T01 - Done when: none of the ten files claim checkout identity is created, stored, resolved, or surfaced by current behavior; `rg -in "checkout identity|checkout_id|checkout-id" context/` shows only past-tense/removed framing. - Verify: `rg -in "checkout identity|checkout_id|checkout-id" context/cli context/sce context/context-map.md context/glossary.md context/architecture.md context/overview.md`, reviewed by hand against the T01 code state. - - Context synchronization: pending + - Completed: 2026-09-13 + - Files changed: `context/plans/remove-checkout-id.md` (this completion record only — no further edits to the ten scoped files were required) + - Result: Confirmed all ten scoped files (plus `context/sce/shared-turso-db.md`, updated by T01's own sweep) already carry only past-tense "removed"/"no longer" framing for checkout identity, written during T01's context-synchronization pass and committed in `b525f5e84`. Read every match returned by the scoped verify command by hand, including `context/cli/service-lifecycle.md` (one hit, already past-tense: "no global/checkout fallback path (removed by the `retire-legacy-agent-trace-db` plan)"). No residual live-behavior claim was found, so no rewrite was needed beyond T01's existing work. + - Verify (actual): `rg -in "checkout identity|checkout_id|checkout-id" context/cli context/sce context/context-map.md context/glossary.md context/architecture.md context/overview.md` — 20 matches across 8 files (`context/overview.md`, `context/architecture.md` x3, `context/glossary.md` x5, `context/context-map.md` x2, `context/cli/cli-command-surface.md`, `context/cli/checkout-identity.md` x3, `context/cli/repository-identity.md`, `context/cli/agent-trace-storage.md` x2, `context/sce/agent-trace-db.md` x3, `context/sce/agent-trace-hook-doctor.md`); every match reviewed by hand and confirmed past-tense/removed framing (e.g. "was removed by the `remove-checkout-id` plan", "no longer mention", "no longer reported"). + - Context impact: `domain` (durable-context-only verification pass; no code or architecture change). No new context impact beyond T01's own sync, which already covered these files. + - Assumptions/deviations: Per the reviewed assumption, T01's context-synchronization pass already satisfied this task's Done-when/AC3 criteria; T02 served as the planned final targeted review/verification pass rather than a fresh rewrite, and found no gap to close. + - Context synchronization: synced ## Open questions None. The removal is fully precedented by the completed `remove-checkout-registry` plan and scoped to a diagnostic-only surface with no persisted-data implications. + +## Validation Report + +**Status:** validated +**Date:** 2026-09-13 + +### Commands run + +- `nix flake check` -> exit 0 (all checks passed; includes cli-tests, cli-clippy, cli-fmt, pkl-generated, and every other repository check) +- `nix run .#pkl-check-generated` -> exit 0 (Ephemeral Pkl generation passed: 141 files, inventory sha256 `39fe28cef0c034871a003e31f4342e405afd75263d692bf4e3bacbd1dc970fc8`) + +### Success-criteria verification + +- [x] AC1: `sce` no longer creates or reads `/sce/checkout-id`, and no code references the removed `cli/src/services/checkout/` module -> `cli/src/services/checkout/` does not exist; `rg -n "services::checkout|get_or_create_checkout_id|resolve_git_dir|checkout_id" cli/src` returns only pre-existing, out-of-scope matches in `cli/src/services/agent_trace_db/repository.rs` (a doc-comment and test asserting the schema has *no* `checkout_id` column — documented in T01's completion record as unrelated to the removed checkout identity service). +- [x] AC2: `sce setup` and `sce doctor` (text and JSON) no longer mention checkout identity -> built `.#sce` and ran it in a scratch Git repo with a fake `origin` remote: `sce setup --non-interactive --claude` output contains no "Agent Trace checkout identity" line; `sce doctor --format json` payload contains no `checkout_identity` key. +- [x] AC3: Durable context describes checkout identity as removed, with no remaining claim that current runtime creates, reads, or reports it -> `rg -in "checkout identity|checkout_id|checkout-id" context/` matches only past-tense/removed framing across every context file (root, `context/cli/`, `context/sce/`); the `context/plans/` and `context/decisions/` matches are historical plan/decision records, not current-state claims. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified.