From 4d4aed4208fe960b65e7e6984e85e9ec3494d4bd Mon Sep 17 00:00:00 2001 From: Ivan Ivic Date: Fri, 4 Sep 2026 15:42:48 +0200 Subject: [PATCH 1/3] config+setup: Allow degraded discovered config during setup and Agent Trace storage Align setup and repository-scoped Agent Trace storage with ordinary startup behavior for invalid default-discovered configuration. Reuse the shared skipped-layer result, preserve validation diagnostics, keep explicit selections fatal, and leave invalid repo-local files untouched while skipping persistence. Co-authored-by: SCE --- cli/src/services/config/resolver.rs | 7 - cli/src/services/setup/command.rs | 1 - cli/src/services/setup/mod.rs | 26 ++- context/architecture.md | 4 +- context/cli/agent-trace-storage.md | 4 +- context/cli/config-precedence-contract.md | 4 +- context/context-map.md | 5 +- ...orage-degrade-invalid-discovered-config.md | 93 +++++++++++ context/glossary.md | 2 +- context/overview.md | 2 +- context/patterns.md | 2 +- ...tup-degraded-invalid-config-agent-trace.md | 153 ++++++++++++++++++ .../sce/agent-trace-hooks-command-routing.md | 1 + .../sce/setup-repo-local-config-bootstrap.md | 16 +- 14 files changed, 276 insertions(+), 44 deletions(-) create mode 100644 context/decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md create mode 100644 context/plans/setup-degraded-invalid-config-agent-trace.md diff --git a/cli/src/services/config/resolver.rs b/cli/src/services/config/resolver.rs index 0d07e793f..77c604b39 100644 --- a/cli/src/services/config/resolver.rs +++ b/cli/src/services/config/resolver.rs @@ -187,13 +187,6 @@ where resolve_global_config_path, )?; - if !runtime.validation_errors.is_empty() { - bail!( - "Agent Trace storage config resolution failed because a discovered config file is invalid: {}", - runtime.validation_errors.join(" | ") - ); - } - Ok(ResolvedAgentTraceStorageRuntimeConfig { repository_id: runtime.agent_trace_repository_id.value, repository_remote: runtime.agent_trace_repository_remote.value, diff --git a/cli/src/services/setup/command.rs b/cli/src/services/setup/command.rs index 8fe6a616a..668c23236 100644 --- a/cli/src/services/setup/command.rs +++ b/cli/src/services/setup/command.rs @@ -23,7 +23,6 @@ impl SetupCommand { // The repository root is resolved before any prompt so the interactive // optional-workflow prompt can pre-check the persisted selection. let repository_root = resolve_setup_repository(&setup_start_path)?; - setup::validate_existing_repo_local_config(&repository_root).map_err(CliError::runtime)?; let setup_dispatch = if self.request.context_only { None diff --git a/cli/src/services/setup/mod.rs b/cli/src/services/setup/mod.rs index 66d58f713..e59247973 100644 --- a/cli/src/services/setup/mod.rs +++ b/cli/src/services/setup/mod.rs @@ -485,23 +485,6 @@ pub fn ensure_git_remote(repository_root: &Path, remote_name: &str) -> Result<() })) } -/// Validates an existing repo-local `.sce/config.json` before setup performs -/// any other repository or lifecycle work. An absent config remains eligible -/// for the normal bootstrap path. -pub fn validate_existing_repo_local_config(repository_root: &Path) -> Result<()> { - let config_file = RepoPaths::new(repository_root).sce_config_file(); - if !config_file.exists() { - return Ok(()); - } - - crate::services::config::validate_config_file(&config_file).with_context(|| { - format!( - "Setup preflight rejected invalid repo-local config file '{}'", - config_file.display() - ) - }) -} - /// Bootstraps the repo-local `.sce/config.json` file if it does not already exist. /// /// Creates the `.sce/` parent directory as needed, then writes the canonical @@ -845,6 +828,15 @@ pub fn persist_integration_targets( let repo_paths = RepoPaths::new(repository_root); let config_file = repo_paths.sce_config_file(); + // Default-discovered invalid config is intentionally degradable during + // setup. Do not rewrite it while recording the installed target: the + // startup resolver already reported the invalid layer and setup must leave + // the user's file byte-for-byte unchanged. + if config_file.exists() && crate::services::config::validate_config_file(&config_file).is_err() + { + return Ok(()); + } + // Read existing config or start with bootstrap payload. let raw = if config_file.exists() { fs::read_to_string(&config_file) diff --git a/context/architecture.md b/context/architecture.md index 81d50b217..a36310f2e 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -115,12 +115,12 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `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/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/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 consumed by startup, setup, and 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 creates or migrates the repository-scoped DB through `agent_trace_storage::resolve_agent_trace_storage(...)`; hook runtime uses the same storage identity/path resolution and no-migration open path, with missing or stale schema failing open through the existing `Run 'sce setup'.` guidance. +- Agent Trace lifecycle setup resolves `agent_trace.repository_id` / `agent_trace.repository_remote`, creates/reuses checkout identity for diagnostics, and creates or migrates the repository-scoped DB through `agent_trace_storage::resolve_agent_trace_storage(...)`; setup and hook runtime consume the shared degraded result for invalid default-discovered config, while explicit config failures remain fatal. Hook runtime uses the same storage identity/path resolution and no-migration open path, with missing or stale schema failing open through the existing `Run 'sce setup'.` guidance. - `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, logout, 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. Logged-out text returns exact login guidance 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. diff --git a/context/cli/agent-trace-storage.md b/context/cli/agent-trace-storage.md index 1b55f589b..d64f39c53 100644 --- a/context/cli/agent-trace-storage.md +++ b/context/cli/agent-trace-storage.md @@ -13,7 +13,7 @@ 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; any invalid discovered config layer is an error at this boundary rather than a skipped layer with fallback values. For valid input, repository identity 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. +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: @@ -28,4 +28,4 @@ The resolver never selects, creates, or touches pre-migration checkout-scoped `< Registered in `cli/src/services/mod.rs` and consumed by hook runtime, Agent Trace lifecycle setup, and `sce sync`. T05 changed the resolved DB handle to the repository-scoped adapter and validates the stored `repository_metadata.repository_id` before returning storage; T08 wired hooks/lifecycle to pass resolved config values into this context; the former trace UX was later removed by the `retire-legacy-agent-trace-db` plan. The `agent-trace-source-instance-id` plan's T03 split hook-runtime resolution into its own no-migration entrypoint, switching `open_agent_trace_db_for_hook_runtime` in `cli/src/services/hooks/mod.rs` off the setup/lifecycle resolver so hook runtime never runs migration `002` (or any migration). Covered by in-module tests: repository separation, SSH/HTTPS clone consolidation, linked-worktree consolidation, explicit-ID override, idempotent re-resolution, missing-identity guidance, path-segment validation, pre-migration checkout DB byte preservation/non-selection, empty fresh repository DB state, repository-level row sharing across equivalent clone checkouts, credential-safe remote canonicalization, concurrent first-open convergence, and hook-runtime resolution (fails before setup on a missing DB, fails before setup on a baseline-only pre-`002` schema without recording migration `002`, and matches setup's `RepositoryMetadata` once setup has run) (`nix build .#checks..cli-tests`). -See also: [repository-identity.md](repository-identity.md), [checkout-identity.md](checkout-identity.md), [default-path-catalog.md](default-path-catalog.md), [../sce/agent-trace-db.md](../sce/agent-trace-db.md), and [the fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). +See also: [repository-identity.md](repository-identity.md), [checkout-identity.md](checkout-identity.md), [default-path-catalog.md](default-path-catalog.md), [../sce/agent-trace-db.md](../sce/agent-trace-db.md), [the degraded discovered-config boundary decision](../decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md), and the superseded [fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). diff --git a/context/cli/config-precedence-contract.md b/context/cli/config-precedence-contract.md index 4c2096479..033d73680 100644 --- a/context/cli/config-precedence-contract.md +++ b/context/cli/config-precedence-contract.md @@ -69,7 +69,7 @@ Config file selection follows this deterministic order: When both discovered defaults exist, they are merged in memory in deterministic order `global -> local`, and local values override global values per key. -When a default-discovered global or repo-local config file exists but fails JSON parsing, top-level-object validation, or schema validation, runtime resolution now skips that file, collects the failure text in `validation_errors`, and continues with remaining discovered layers plus defaults. Explicit `--config ` and `SCE_CONFIG_FILE` selections remain fatal on those errors. This means normal command startup still reaches dispatch for commands such as `sce version`, `sce doctor`, and `sce hooks commit-msg` even when discovered config is invalid. Setup and Agent Trace storage are deliberate stricter consumers: setup validates an existing repo-local file after Git-root resolution and before prompts, context bootstrap, lifecycle work, or asset installation, while storage resolution errors on any invalid discovered layer instead of using fallback identity values. See [the fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). +When a default-discovered global or repo-local config file exists but fails JSON parsing, top-level-object validation, or schema validation, runtime resolution now skips that file, collects the failure text in `validation_errors`, and continues with remaining discovered layers plus defaults. Explicit `--config ` and `SCE_CONFIG_FILE` selections remain fatal on those errors. This means normal command startup still reaches dispatch for commands such as `sce version`, `sce doctor`, and `sce hooks commit-msg` even when discovered config is invalid. Setup and Agent Trace storage consume the same degraded result: setup continues after Git-root and remote preflight, preserves an invalid local file, and skips local target/optional-workflow persistence for that run, while storage uses remaining identity values or the default remote. See [the degraded discovered-config boundary decision](../decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md). ## Validation contract @@ -79,7 +79,7 @@ When a default-discovered global or repo-local config file exists but fails JSON - Each reported schema-validation error is prefixed with the failing value's JSON-pointer location when it has one (for example `/integrations/optional_workflows/0: "nonesuch" is not one of "brownfield"`), so a rejected value names the key it came from; root-level errors keep their unprefixed text. Errors remain sorted and joined with ` | `. - After schema validation, `cli/src/services/config/schema.rs` deserializes top-level and nested config structure (`policies`, `policies.bash`, `policies.attribution_hooks`) into typed serde DTOs and applies focused Rust-owned mapping helpers for enum conversion and source attribution; policy-specific semantic checks are owned by `cli/src/services/config/policy.rs`. - The canonical top-level schema declaration `"$schema": "https://sce.crocoder.dev/v/config.json"` (where `` is the CLI release version) is a supported config key for both explicit and discovered `sce/config.json` files, including command-startup paths like `sce version` and other config-loading commands that parse config before normal command dispatch. -- Startup/runtime config resolution now degrades gracefully only for default-discovered files: invalid discovered files are skipped and reported via collected `validation_errors`, while explicit `--config` / `SCE_CONFIG_FILE` targets still fail immediately on the same parse or validation errors. +- Startup/runtime config resolution degrades gracefully for default-discovered files across ordinary startup, setup, and Agent Trace storage: invalid discovered files are skipped and reported via collected `validation_errors`, while explicit `--config` / `SCE_CONFIG_FILE` targets still fail immediately on the same parse or validation errors. - Config file content must be valid JSON with a top-level object. - Allowed keys: `$schema`, `log_level`, `log_format`, `log_to_file`, `log_dir`, `log_file_retention_limit`, `workos_client_id`, `control_plane_base_url`, `agent_trace`, `policies`, `integrations`. diff --git a/context/context-map.md b/context/context-map.md index 1595d1de9..78328619a 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -12,7 +12,7 @@ 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, strict rejection of invalid discovered config before identity fallback, `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/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/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) @@ -57,7 +57,7 @@ Feature/domain context: - `context/sce/setup-githooks-hook-asset-packaging.md` (compile-time `sce setup --hooks` required-hook template packaging contract, including all-hook non-blocking missing-`sce` install guidance, available-CLI argument forwarding, post-commit-only origin remote lookup plus remote-URL forwarding/fallback behavior, setup-service accessor surface, and current validation posture) - `context/sce/setup-githooks-install-flow.md` (setup-service required-hook install orchestration with git-truth hooks-path resolution, managed-block merge content computation that preserves foreign hook content, per-hook installed/updated/skipped outcomes decided against the merged content, the unreachable-block advisory, and atomic-swap replacement with recovery guidance) - `context/sce/setup-githooks-cli-ux.md` (T04 composable `sce setup` target+`--hooks` / `--repo` command-surface contract, option compatibility validation, and deterministic setup/hook output semantics) -- `context/sce/setup-repo-local-config-bootstrap.md` (setup local bootstrap behavior: Git-root-gated validation of existing repo-local config before prompts, context, lifecycle, hooks, or assets, additive durable-context baseline via `sce setup --bootstrap-context` and every normal setup path, repo-local `.sce/config.json` create-if-missing via config lifecycle, additive `integrations.target` persistence after successful target installs, `integrations.optional_workflows` selection persistence with prompt-over-flag-over-persisted precedence, plus lifecycle-owned local DB initialization before hooks/config asset dispatch) +- `context/sce/setup-repo-local-config-bootstrap.md` (setup local bootstrap behavior: Git-root and named-remote preflights before prompts/context/lifecycle/hooks/assets, shared degraded handling for invalid default-discovered repo-local config with byte-preserving skip of integration persistence, explicit-config fatality, additive durable-context baseline via `sce setup --bootstrap-context` and every normal setup path, repo-local `.sce/config.json` create-if-missing via config lifecycle, additive `integrations.target` persistence after successful target installs, `integrations.optional_workflows` selection persistence with prompt-over-flag-over-persisted precedence, plus lifecycle-owned local DB initialization before hooks/config asset dispatch) - `context/sce/cli-security-hardening-contract.md` (T06 CLI redaction contract, setup `--repo` canonicalization/validation, and setup write-permission probe behavior) - `context/sce/agent-trace-post-rewrite-local-remap-ingestion.md` (current post-rewrite no-op baseline plus historical remap-ingestion reference) - `context/sce/agent-trace-rewrite-trace-transformation.md` (current post-rewrite no-op baseline plus historical rewrite-transformation reference) @@ -108,6 +108,7 @@ Recent decision records: - `context/decisions/2026-09-01-claude-model-attribution-state.md` (accepts a Claude-specific local latest-model-state register as a bounded exception to the prior no-session-level-cache attribution constraint, with best-effort local observation-time ordering and no export/sync scope) - `context/decisions/2026-09-01-claude-post-model-switch-compatibility.md` (accepts unconditional installation of the Claude `PostModelSwitch` registration after Claude Code 2.1.250/2.1.251 compatibility smoke showed unknown-event tolerance and settings preservation) - `context/decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md` (keeps general startup degradation for invalid discovered config while making setup and Agent Trace storage fail closed before side effects or fallback identity selection) +- `context/decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md` (supersedes the prior fail-closed boundary: setup and Agent Trace storage now consume shared degradation for invalid default-discovered config while explicit selections remain fatal) - `context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md` (the completed task record — `Completed`/`Files changed`/`Result`/`Verify`/`Context impact`/`Context synchronization`, identified only by plan path and task ID — is the sole durable input for immediate and cross-session task synchronization, with no separate persisted `Context synchronization handoff` structure; supersedes only the handoff-shape portion of `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md`, whose `pending`/`synced`/`blocked` lifecycle-state invariant remains in force) - `context/decisions/2026-08-12-decision-gate-semantics.md` (nonqualifying/skipped decision gates are non-blocking; ADRs are immutable, active-only reuse is allowed, changed decisions create new dated records, and `Deprecated`/`Superseded` are creation-time-only statuses) - `context/decisions/2026-08-12-observational-final-validation.md` diff --git a/context/decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md b/context/decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md new file mode 100644 index 000000000..d226ad34f --- /dev/null +++ b/context/decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md @@ -0,0 +1,93 @@ +# Decision: Degrade Invalid Default-Discovered Config at Setup and Storage Boundaries + +Date: 2026-09-04 +Status: Accepted +Plan: `context/plans/setup-degraded-invalid-config-agent-trace.md` +Task: `T01` +Supersedes: `context/decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md` + +## Context + +Ordinary startup already skips invalid default-discovered global or repo-local +configuration layers, preserves the existing `sce.config.invalid_config` +warning, and continues with remaining layers or defaults. Setup and Agent Trace +storage had retained a stricter boundary: setup rejected an invalid local file +before its normal flow, while storage rejected any invalid discovered layer +before repository identity resolution. This divergence prevented setup and +repository-scoped hook tracing from using the same degraded configuration +behavior. Focused resolver, setup, and hook-runtime tests establish that the +invalid local file can remain untouched while valid remaining configuration or +the default remote continues to provide the required values. + +## Decision + +` sce setup` and Agent Trace hook storage shall skip invalid default-discovered +configuration layers and continue with the shared resolver's remaining-layer or +default values; explicit `--config` and `SCE_CONFIG_FILE` selections remain +fatal. + +## Rationale + +Using the shared resolver result keeps startup, setup, and hook-runtime +configuration behavior aligned without weakening explicit operator intent. +Setup can complete its Git/remote preflight, lifecycle, and asset flow without +repairing a user's invalid file, and repository-scoped tracing can continue to +the same identity and database path selected by valid remaining configuration +or the default `origin` remote. + +## Alternatives considered + +- **Keep setup and storage fail-closed** — preserves the previous safety boundary + but needlessly blocks normal setup and hook tracing when a lower-priority + discovered layer is invalid. +- **Make explicit configuration degradable too** — would discard an explicit + operator selection and weaken the fatal configuration contract. +- **Repair invalid local configuration during setup** — would mutate user-owned + bytes as a side effect and could destroy information needed for manual repair. + +## Compatibility and risks + +- Ordinary and setup consumers may proceed using a remaining layer or default + after a discovered configuration failure; the existing warning and validation + error reporting remain in place. +- Invalid local configuration is intentionally not rewritten, so target and + optional-workflow persistence may be omitted for that run. +- Genuine Agent Trace database, identity, Git, and remote failures retain their + existing diagnostics and fail-open behavior. + +## Guardrails + +- Only default-discovered global and repo-local layers are degradable. +- Explicit `--config` and `SCE_CONFIG_FILE` parse or validation failures remain + fatal. +- The generated schema, precedence rules, repository identity canonicalization, + database schema, and no-migration hook opening contract do not change. +- Setup never repairs, deletes, or rewrites an invalid discovered config file. + +## Consequences + +- `sce setup` can reach its normal Git/remote preflight, bootstrap, lifecycle, + and requested asset-install flow despite invalid discovered configuration. +- Agent Trace hook-runtime DB opening uses the same degraded repository identity + inputs as the shared runtime resolver and can persist representative hook data. +- Operators still receive the established invalid-config warning and must repair + the file separately when they want its settings or setup persistence restored. + +## Follow-up + +- `/validate` must verify the plan's setup, resolver, Agent Trace storage, hook, + and explicit-config acceptance criteria and repository-wide checks. + +## References + +- Plan: [`setup-degraded-invalid-config-agent-trace`](../plans/setup-degraded-invalid-config-agent-trace.md) +- Task: `T01` +- Current-state context: [`CLI config precedence contract`](../cli/config-precedence-contract.md) +- Current-state context: [`SCE setup local bootstrap`](../sce/setup-repo-local-config-bootstrap.md) +- Current-state context: [`Repository-scoped Agent Trace storage resolver`](../cli/agent-trace-storage.md) +- Current-state context: [`Agent Trace hooks command routing`](../sce/agent-trace-hooks-command-routing.md) +- Evidence: [`config resolver`](../../cli/src/services/config/resolver.rs) +- Evidence: [`setup command`](../../cli/src/services/setup/command.rs) +- Evidence: [`setup service`](../../cli/src/services/setup/mod.rs) +- Evidence: [`hooks service`](../../cli/src/services/hooks/mod.rs) +- Related decision: [`Fail Closed at Setup and Agent Trace Storage Boundaries for Invalid Discovered Config`](2026-08-26-setup-storage-fail-closed-on-invalid-config.md) diff --git a/context/glossary.md b/context/glossary.md index 92cfd21d6..e23eb5e0b 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -3,7 +3,7 @@ - `repo-level verification preference`: Current repository guidance that contributor-facing validation/check flows should prefer `nix flake check`; direct Cargo verification commands are secondary and used only when explicitly requested or for narrow targeted debugging, while `cargo fmt` remains the explicit autofix path. - lightweight post-task verification baseline: Required quick checks after each completed task in this repo: `nix run .#pkl-check-generated` and `nix flake check`. - disposable plan lifecycle: Policy where `context/plans/` holds active execution artifacts only; completed plans are disposable and durable outcomes must be reflected in current-state context files and/or `context/decisions/`. -- important change (context sync): A completed task change that affects cross-cutting behavior, repository-wide policy/contracts, architecture boundaries, or canonical terminology; these changes require root context edits in `context/overview.md`, `context/architecture.md`, and/or `context/glossary.md` instead of verify-only handling. `setup config preflight` is the Git-root-gated check that validates an existing repo-local `.sce/config.json` before prompts, context bootstrap, lifecycle initialization, hooks, or target asset installation; invalid config fails setup closed, absent config remains eligible for create-if-missing bootstrap, Agent Trace storage has the parallel strict rule for invalid discovered config layers, and ordinary startup consumers retain degraded-default behavior. See [the fail-closed boundary decision](decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). +- important change (context sync): A completed task change that affects cross-cutting behavior, repository-wide policy/contracts, architecture boundaries, or canonical terminology; these changes require root context edits in `context/overview.md`, `context/architecture.md`, and/or `context/glossary.md` instead of verify-only handling. `setup config handling` is the Git-root-gated setup path that keeps an invalid default-discovered repo-local `.sce/config.json` untouched while allowing normal preflight, bootstrap, lifecycle, hooks, and target installation to continue; absent config remains eligible for create-if-missing bootstrap, Agent Trace storage consumes the same degraded result for invalid discovered layers, explicit `--config` and `SCE_CONFIG_FILE` failures remain fatal, and ordinary startup consumers retain degraded-default behavior. See [the degraded discovered-config boundary decision](decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md). - verify-only root context pass: Context-sync mode for localized tasks where root-level behavior, architecture, and terminology are unchanged; root shared files are checked against code truth but are not edited by default. - ephemeral generated payload: Files materialized by `config/pkl/generate.pkl` using payload-relative `config/.opencode/**`, `config/.claude/**`, `config/.pi/**`, `config/.agents/**`, `config/.codex/**`, and `config/schema/sce-config.schema.json` paths beneath Cargo `OUT_DIR`, temporary previews, or packaging fallbacks. These layouts are installed by `sce setup` but are never committed as repository target trees; `config/automated/.opencode/**` remains a forbidden generator surface. - `Codex root-aware hook invocation`: Generated `.codex/hooks.json` command contract that resolves the Git repository root at hook runtime, invokes the installed helper through quoted path expansion from root or nested event cwd, preserves JSON STDIN, and exits silently successfully when Git-root resolution fails. The existing helper remains responsible for missing-`sce` stderr guidance; the contract forbids install-time absolute paths and `eval`. diff --git a/context/overview.md b/context/overview.md index f7ba0b261..8153fd8b5 100644 --- a/context/overview.md +++ b/context/overview.md @@ -12,7 +12,7 @@ The generated `/next-task` workflow persists task-level context-synchronization - **Stderr diagnostics:** stable `SCE-ERR-{PARSE,VALIDATION,RUNTIME,DEPENDENCY}` codes with class-default `Try:` remediation (see `context/sce/cli-error-code-taxonomy.md`). - **Stdout/stderr:** command payloads on stdout only; redacted diagnostics and text-mode `sce sync` progress on stderr, while JSON sync remains silent (see `context/sce/cli-stdout-stderr-contract.md`). - **Observability:** config-resolved logging with tracing, explicit config-file/default `log_to_file` control, error-specific stderr suppression when file logging is enabled, and optional dated/session-partitioned `log_dir` / `SCE_LOG_DIR` files with retention (see `context/sce/cli-observability-contract.md`). -- **Config precedence:** `flags > env > config file > defaults` (see `context/cli/config-precedence-contract.md`); invalid default-discovered config remains degradable for ordinary startup, while setup and Agent Trace storage fail closed before side effects or fallback identity selection. The config-file-only `agent_trace.auto_sync` setting defaults to `true` and is resolved with source metadata for the post-commit trigger and doctor readiness boundaries. Its asynchronous post-commit behavior and doctor capability reporting are documented in `context/cli/agent-trace-auto-sync.md`. +- **Config precedence:** `flags > env > config file > defaults` (see `context/cli/config-precedence-contract.md`); invalid default-discovered config remains degradable for ordinary startup, setup, and Agent Trace storage, while explicit `--config` and `SCE_CONFIG_FILE` failures remain fatal. Setup preserves invalid repo-local files and may omit target/optional-workflow persistence for that run. The config-file-only `agent_trace.auto_sync` setting defaults to `true` and is resolved with source metadata for the post-commit trigger and doctor readiness boundaries. Its asynchronous post-commit behavior and doctor capability reporting are documented in `context/cli/agent-trace-auto-sync.md`. - **Attribution hooks:** enabled by default, gated by staged-diff AI-overlap preflight; `SCE_ATTRIBUTION_HOOKS_DISABLED` opt-out (see `context/sce/agent-trace-commit-msg-coauthor-policy.md`). - **Install channels:** repo-flake Nix, Cargo, npm, and source-built Flatpak (`dev.crocoder.sce`); Homebrew deferred (see `context/sce/cli-first-install-channels-contract.md`). diff --git a/context/patterns.md b/context/patterns.md index 1b8e72265..ff5cc5d03 100644 --- a/context/patterns.md +++ b/context/patterns.md @@ -131,7 +131,7 @@ - For default-enabled automatic Agent Trace synchronization, keep the post-commit launcher as a one-shot reuse of `sce sync`: start the current executable after local Agent Trace persistence, use the repository root and null child streams, do not wait, and fail open on launcher errors. Keep manual sync and the control-plane cursor authority as the retry path; do not add daemons, watchers, polling, queues, or high-frequency hook triggers. - For commands that support text/JSON dual output, centralize `--format ` parsing in one shared contract and pass command-specific `--help` guidance into invalid-value errors instead of duplicating parser logic per command. - For setup-style command contracts, keep interactive mode as the zero-flag default and enforce mutually-exclusive explicit target flags for non-interactive automation. -- For setup config safety, validate an existing repo-local config immediately after resolving the Git root and before prompts, context bootstrap, lifecycle providers, hooks, or target assets; preserve create-if-missing behavior for absent config and keep general startup degradation scoped away from setup and Agent Trace storage identity resolution. +- For setup config safety, resolve invalid default-discovered config through the shared degraded layer behavior, preserve the invalid repo-local file byte-for-byte, and allow Git/remote preflight, context bootstrap, lifecycle providers, hooks, and target assets to continue; preserve create-if-missing behavior for absent config, skip target/optional-workflow persistence when the local file is invalid, and keep explicit `--config` / `SCE_CONFIG_FILE` failures fatal. - For durable-context bootstrap, keep create-if-missing additive semantics: ensure baseline paths on every successful setup path, offer a dedicated standalone `--bootstrap-context` mode, and never overwrite existing context content. - For security-sensitive CLI UX, redact common secret-bearing token/value forms before emitting diagnostics/log lines, including app-level errors, setup git stderr diagnostics, and observability sink output. - For user-supplied setup repository paths (`sce setup --hooks --repo `), canonicalize/validate the path as an existing directory before git command execution, and run deterministic write-permission probes on setup write targets before staging/swap operations. diff --git a/context/plans/setup-degraded-invalid-config-agent-trace.md b/context/plans/setup-degraded-invalid-config-agent-trace.md new file mode 100644 index 000000000..31f9cfeaa --- /dev/null +++ b/context/plans/setup-degraded-invalid-config-agent-trace.md @@ -0,0 +1,153 @@ +# Plan: setup-degraded-invalid-config-agent-trace + +## Change summary + +Align `sce setup` and Agent Trace hook-runtime storage with ordinary startup +configuration behavior. When a default-discovered global or repo-local +`.sce/config.json` is invalid, the shared resolver should skip that layer, +retain the existing `sce.config.invalid_config` warning, and continue with any +valid remaining layer or degraded defaults. An outdated `$schema` URL must not +abort setup. + +The same degraded values must reach +`open_agent_trace_db_for_hook_runtime()` so invalid discovered configuration does +not block conversation tracing, diff tracing, commit attribution, post-commit +processing, Claude model-state intake, or other Agent Trace DB-backed hook work. +Explicit `--config` and `SCE_CONFIG_FILE` selections remain fatal, and invalid +repo-local configuration is not repaired or rewritten as a side effect of +continuing setup. + +## Acceptance criteria + +How this plan is proven complete. Each criterion is observable and names the +check that proves it. `/validate` runs these checks; no task in the stack +performs final validation. + +- [x] AC1: `sce setup` completes its normal Git/remote preflight, bootstrap, + lifecycle, and requested asset-install flow when a default-discovered global + or repo-local config file is invalid, including an outdated `$schema` URL; + valid remaining layers and defaults continue to determine setup values, and + the invalid repo-local file remains byte-for-byte unchanged. + - Validate: Focused setup tests plus an integration-style setup case with + invalid global/local discovered config assert successful continuation, + unchanged invalid-file content, and the existing startup warning event. +- [x] AC2: `resolve_agent_trace_storage_runtime_config()` and + `open_agent_trace_db_for_hook_runtime()` continue past invalid + default-discovered config, use the remaining valid layer or default remote, + and expose an open repository-scoped DB to DB-backed hook flows. + - Validate: Focused resolver, Agent Trace storage, and hooks tests cover + invalid global/local layers, default/remaining-layer identity selection, + successful hook-runtime DB opening, and representative conversation/diff + persistence paths. +- [x] AC3: Invalid explicit `--config` and `SCE_CONFIG_FILE` selections remain + fatal, while the existing config schema, repository identity canonicalization, + remote precedence, hook no-migration behavior, and genuine DB fail-open + diagnostics remain unchanged. + - Validate: Focused resolver/setup/hooks tests assert explicit-config failure, + identity and migration invariants, and unchanged hook diagnostics. + +### Full validation + +Repository-wide checks `/validate` runs after the last task, regardless of +which criterion they map to. + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/overview.md` +- `context/architecture.md` +- `context/glossary.md` +- `context/cli/config-precedence-contract.md` +- `context/sce/setup-repo-local-config-bootstrap.md` +- `context/cli/agent-trace-storage.md` +- `context/sce/agent-trace-hooks-command-routing.md` +- A new dated decision record superseding `context/decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.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:** The default-discovered invalid-layer boundary in the shared + runtime resolver, setup preflight and integration-persistence behavior, + `open_agent_trace_db_for_hook_runtime()` and its hook callers, focused Rust + regression tests, and the listed durable context. +- **Out of scope:** Changes to the JSON Schema, startup warning identifier, + explicit-config strictness, repository identity canonicalization or remote + precedence, Agent Trace DB schema/migrations, generated integrations, or + unrelated setup behavior. +- **Constraints:** Reuse the existing resolver and schema seams; preserve + credential-safe deterministic diagnostics, startup warning behavior, setup Git + and remote preflights, no-migration hook opening, existing hook fail-open + handling for genuine DB failures, and Nix-based verification. Add no + dependencies. +- **Non-goal:** Do not make invalid configuration silently valid, delete or + repair user files, introduce a new storage fallback database, or make explicit + config selections degradable. + +## Assumptions + +- “Follow startup behavior” means only default-discovered invalid layers are + skipped; explicit `--config` and `SCE_CONFIG_FILE` inputs remain fatal. +- Continuing setup with an invalid repo-local file must not rewrite that file; + setup may omit integration-target/optional-workflow persistence for that run + rather than mutating invalid user configuration. +- The existing repository-scoped identity fallback (`agent_trace.repository_id`, + configured remote, then default `origin`) and no-migration hook DB path are + sufficient; no new identity or database fallback is needed. + +## Task stack + +- [x] T01: `Align setup and Agent Trace hook storage with degraded discovered config` (status:done) + - Task ID: T01 + - Scope: In — remove setup's default-discovered invalid-config hard-fail, make Agent Trace storage runtime configuration use the shared skipped-layer result, keep invalid repo-local config untouched when setup continues, and add focused resolver/setup/storage/hooks regressions for `open_agent_trace_db_for_hook_runtime()` and representative DB-backed hook writes. Preserve explicit-config failures, startup warning emission, Git/remote preflight order, repository identity precedence, no-migration opening, and genuine DB failure diagnostics. Out — schema changes, config repair or migration, new storage fallbacks, repository identity canonicalization, generated assets, and unrelated setup/hook behavior. + - Dependencies: none + - Done when: Invalid default-discovered global or local config no longer aborts setup or blocks repository-scoped Agent Trace hook DB opening; setup completes without rewriting the invalid local file; remaining-layer/default precedence and explicit-config failure behavior are covered by passing focused tests. + - Verify: `nix develop -c sh -c './scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::config::resolver && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::setup && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::agent_trace_storage && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::hooks'`. + - Completed: 2026-09-04 + - Files changed: `cli/src/services/config/resolver.rs`; `cli/src/services/setup/command.rs`; `cli/src/services/setup/mod.rs`; `cli/src/services/hooks/mod.rs` + - Result: Agent Trace storage now consumes the shared degraded resolver result instead of failing on invalid default-discovered layers. Setup no longer rejects invalid repo-local config during preflight, and target persistence skips invalid local files without rewriting them. Added resolver precedence regressions, byte-preserving setup persistence coverage, and hook-runtime DB opening/conversation-write coverage with invalid discovered local config. Explicit configuration failures and existing DB/hook behavior remain unchanged. + - Verify: `nix develop -c sh -c './scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::config::resolver && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::setup && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::agent_trace_storage && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::hooks'` — passed (20 resolver, 67 setup, 14 Agent Trace storage, and 181 hooks tests). + - Context impact: Material cross-cutting behavior change to shared config resolution, setup persistence, and Agent Trace hook storage; durable context synchronization is required for the listed config/setup/Agent Trace contracts and a superseding decision record. + - Context synchronization: synced + +## Open questions + +None. The requested boundary is explicit, the repository already owns the +degraded resolver and hook-runtime opener, and the non-destructive persistence +choice follows the existing setup asset/config safety rules. + +## Validation Report + +**Status:** validated +**Date:** 2026-09-04 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral Pkl generation passed: 141 files; inventory parity matched) +- `nix flake check` -> exit 0 (all flake checks passed) +- `nix develop -c sh -c './scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::config::resolver && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::setup && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::agent_trace_storage && ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml services::hooks'` -> exit 0 (20 resolver, 67 setup, 14 Agent Trace storage, and 181 hooks tests passed) + +### Success-criteria verification + +- [x] AC1: `sce setup` completes its normal Git/remote preflight, bootstrap, lifecycle, and requested asset-install flow when a default-discovered global or repo-local config file is invalid, including an outdated `$schema` URL; valid remaining layers and defaults continue to determine setup values, and the invalid repo-local file remains byte-for-byte unchanged. -> Resolver and setup regression tests passed, including invalid discovered-layer continuation and byte-preserving invalid repo-local persistence. +- [x] AC2: `resolve_agent_trace_storage_runtime_config()` and `open_agent_trace_db_for_hook_runtime()` continue past invalid default-discovered config, use the remaining valid layer or default remote, and expose an open repository-scoped DB to DB-backed hook flows. -> Resolver, Agent Trace storage, and hooks suites passed, including invalid-layer precedence, hook-runtime DB opening, conversation writes, diff persistence, and post-commit flows. +- [x] AC3: Invalid explicit `--config` and `SCE_CONFIG_FILE` selections remain fatal, while the existing config schema, repository identity canonicalization, remote precedence, hook no-migration behavior, and genuine DB fail-open diagnostics remain unchanged. -> Focused resolver, setup, Agent Trace storage, and hooks suites passed, including explicit-config, identity, remote, migration, and fail-open regression coverage. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/sce/agent-trace-hooks-command-routing.md b/context/sce/agent-trace-hooks-command-routing.md index 13e33cf3e..10984ad6e 100644 --- a/context/sce/agent-trace-hooks-command-routing.md +++ b/context/sce/agent-trace-hooks-command-routing.md @@ -32,6 +32,7 @@ - config `policies.attribution_hooks.enabled` - precedence: env over config file - default: enabled +- Hook-runtime Agent Trace DB opening resolves repository identity through the shared config resolver. Invalid default-discovered global or repo-local config layers are skipped in favor of the remaining valid layer or the default `origin` remote; explicit `--config` / `SCE_CONFIG_FILE` failures remain fatal. This degradation does not change no-migration schema readiness, repository identity canonicalization, or genuine DB fail-open diagnostics. - `commit-msg` is the only active attribution path. - Reads the message file as UTF-8. - Applies exactly one canonical trailer: `Co-authored-by: SCE `. diff --git a/context/sce/setup-repo-local-config-bootstrap.md b/context/sce/setup-repo-local-config-bootstrap.md index 10f38ffd1..116db2769 100644 --- a/context/sce/setup-repo-local-config-bootstrap.md +++ b/context/sce/setup-repo-local-config-bootstrap.md @@ -11,14 +11,14 @@ Task `setup-repo-gate-and-local-config-bootstrap` T02, `turso-local-db-sync` T04 - If `.sce/config.json` already exists, the bootstrap step returns `Ok(())` immediately and leaves the file untouched — no merge, no reformat, no overwrite. - The parent `.sce/` directory is created via `fs::create_dir_all` if missing. - The setup flow also bootstraps the canonical local DB through `LocalDbLifecycle::setup` and the Agent Trace DB through `AgentTraceDbLifecycle::setup`; both use the shared `TursoDb` adapter. -- After both repository preflights (`ensure_git_repository` and the effective named-remote URL check), setup validates an existing repo-local `.sce/config.json` before prompts, context baseline bootstrap, lifecycle providers, hooks, or target assets. Invalid config stops the run without those side effects; an absent config continues through the normal bootstrap path. -- Config/DB bootstrap runs after those preflights and config validation, and after context baseline bootstrap, before config/hooks dispatch, so it applies to all normal setup modes: config-only, hooks-only, combined, and interactive. +- After both repository preflights (`ensure_git_repository` and the effective named-remote URL check), setup consumes the shared resolver's degraded result for an invalid default-discovered repo-local `.sce/config.json`; the file remains untouched and the run continues through prompts, context baseline bootstrap, lifecycle providers, hooks, and target assets. An absent config continues through the normal bootstrap path, while explicit config selections remain fatal. +- Config/DB bootstrap runs after those preflights and after context baseline bootstrap, before config/hooks dispatch, so it applies to all normal setup modes: config-only, hooks-only, combined, and interactive. If the repo-local config is invalid, target and optional-workflow persistence is skipped rather than rewriting it. ## Context baseline bootstrap - `sce setup --bootstrap-context` is a non-interactive context-only mode and must be used alone (no target, hooks, non-interactive, or `--repo` flags). -- Context-only setup ensures both repository preflights, validates an existing repo-local config, then creates the baseline durable-context tree and exits without lifecycle providers, integration installs, or prompts. -- Every normal successful setup path also calls the same additive context bootstrap after both repository preflights and config validation, before lifecycle/config install work. +- Context-only setup ensures both repository preflights, then creates the baseline durable-context tree and exits without lifecycle providers, integration installs, or prompts; invalid default-discovered repo-local config remains untouched. +- Every normal successful setup path also calls the same additive context bootstrap after both repository preflights, before lifecycle/config install work. - Baseline paths: `context/overview.md`, `context/architecture.md`, `context/patterns.md`, `context/glossary.md`, `context/context-map.md`, `context/plans/`, `context/handovers/`, `context/decisions/`, `context/tmp/`, and `context/tmp/.gitignore`. - Create-if-missing only: existing files and directory contents are left untouched; missing individual paths are restored even when `context/` already exists. - New Markdown files use neutral headings/placeholders; `context-map.md` links baseline entry points without inventing repository details; `context/tmp/.gitignore` ignores scratch content while retaining itself (`*\n!.gitignore\n`). @@ -54,14 +54,14 @@ The same write also records the run's resolved optional-workflow selection under - `cli/src/services/agent_trace_db/lifecycle.rs` implements `AgentTraceDbLifecycle::setup()` for Agent Trace DB initialization. - Repo-local config bootstrap uses `RepoPaths::sce_config_file()` and `RepoPaths::sce_dir()`; context baseline bootstrap uses the shared context accessors including `RepoPaths::context_tmp_gitignore_file()`. - The canonical payload constant is `REPO_LOCAL_CONFIG_BOOTSTRAP_PAYLOAD`. -- `cli/src/services/setup/command.rs` resolves the effective `agent_trace.repository_remote`, runs both repository preflights, and validates an existing repo-local config before `bootstrap_context_baseline`. Context-only requests return after the baseline. Normal modes then derive a repo-root-scoped `AppContext` and aggregate lifecycle providers in config → local_db → auth_db → agent_trace_db → hooks order; `ConfigLifecycle::setup()` calls `bootstrap_repo_local_config(...)`, `LocalDbLifecycle::setup()` initializes the local DB, `AuthDbLifecycle::setup()` initializes the auth DB, and `AgentTraceDbLifecycle::setup()` initializes the Agent Trace DB. +- `cli/src/services/setup/command.rs` resolves the effective `agent_trace.repository_remote` and runs both repository preflights before `bootstrap_context_baseline`; default-discovered invalid config is handled by the shared resolver and is not rewritten. Context-only requests return after the baseline. Normal modes then derive a repo-root-scoped `AppContext` and aggregate lifecycle providers in config → local_db → auth_db → agent_trace_db → hooks order; `ConfigLifecycle::setup()` calls `bootstrap_repo_local_config(...)`, `LocalDbLifecycle::setup()` initializes the local DB, `AuthDbLifecycle::setup()` initializes the auth DB, and `AgentTraceDbLifecycle::setup()` initializes the Agent Trace DB. ## Relationship to other setup contracts -- The Git-repo gate (`ensure_git_repository`), effective named-remote URL preflight, and existing-config validation remain the preconditions for every setup write path, including context-only bootstrap. The gate classifies only an explicit Git `not a git repository` result and an actually missing/empty named-remote URL as typed user errors; Git/process/configuration and remote-lookup execution failures remain runtime errors with technical sources preserved. -- The repo-local config preflight is fail-closed only for an existing invalid config, while absent config remains create-if-missing. +- The Git-repo gate (`ensure_git_repository`) and effective named-remote URL preflight remain the preconditions for every setup write path, including context-only bootstrap. The gate classifies only an explicit Git `not a git repository` result and an actually missing/empty named-remote URL as typed user errors; Git/process/configuration and remote-lookup execution failures remain runtime errors with technical sources preserved. +- Default-discovered invalid repo-local config is degradable and never rewritten; explicit `--config` / `SCE_CONFIG_FILE` selections remain fatal, while absent local config remains create-if-missing. - Context baseline bootstrap is independent of config/DB/hooks install and runs before those steps on normal setup paths. - Local bootstrap (repo config + local DB init) is independent of config install and hook install; it runs before both after context baseline bootstrap. - The bootstrap payload matches the `$schema` declaration accepted by startup config loading and the Pkl-authored JSON Schema embedded from Cargo `OUT_DIR`. -See also [the fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). +See also [the degraded discovered-config boundary decision](../decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md) and the superseded [fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). From ea2c895b6f123333ef5812373aade6135d0774a2 Mon Sep 17 00:00:00 2001 From: Ivan Ivic Date: Mon, 7 Sep 2026 15:04:07 +0200 Subject: [PATCH 2/3] config+setup: Separate setup and runtime auto-sync defaults Make newly created repo-local configs explicitly opt into Agent Trace auto-sync while keeping omitted runtime values disabled. Preserve explicit values and precedence, and update tests and supporting records. Co-authored-by: SCE --- cli/src/services/config/resolver.rs | 6 +- cli/src/services/setup/mod.rs | 10 +- context/architecture.md | 4 +- context/cli/agent-trace-auto-sync.md | 12 +- context/cli/config-precedence-contract.md | 6 +- context/context-map.md | 5 +- .../2026-09-07-split-auto-sync-defaults.md | 71 +++++++++ context/glossary.md | 4 +- context/overview.md | 2 +- context/patterns.md | 4 +- .../update-auto-sync-default-behavior.md | 136 ++++++++++++++++++ .../sce/agent-trace-hooks-command-routing.md | 2 +- context/sce/doctor-human-text-contract.md | 15 +- .../sce/setup-repo-local-config-bootstrap.md | 4 +- 14 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 context/decisions/2026-09-07-split-auto-sync-defaults.md create mode 100644 context/plans/update-auto-sync-default-behavior.md diff --git a/cli/src/services/config/resolver.rs b/cli/src/services/config/resolver.rs index 77c604b39..dc8db4e7c 100644 --- a/cli/src/services/config/resolver.rs +++ b/cli/src/services/config/resolver.rs @@ -557,7 +557,7 @@ where source: ValueSource::ConfigFile(value.source), }, None => ResolvedValue { - value: true, + value: false, source: ValueSource::Default, }, }; @@ -900,10 +900,10 @@ mod tests { } #[test] - fn agent_trace_auto_sync_defaults_to_true() { + fn agent_trace_auto_sync_defaults_to_false_when_missing() { let runtime = resolve_runtime_with_config(None).unwrap(); - assert!(runtime.agent_trace_auto_sync.value); + assert!(!runtime.agent_trace_auto_sync.value); assert_eq!(runtime.agent_trace_auto_sync.source, ValueSource::Default); } diff --git a/cli/src/services/setup/mod.rs b/cli/src/services/setup/mod.rs index e59247973..f21bd79a3 100644 --- a/cli/src/services/setup/mod.rs +++ b/cli/src/services/setup/mod.rs @@ -56,10 +56,11 @@ pub(crate) fn is_missing_git_remote_error(error: &anyhow::Error) -> bool { } /// Canonical JSON payload for a newly bootstrapped repo-local `.sce/config.json`. -/// Contains only the `$schema` declaration pointing to the SCE config JSON Schema. +/// Declares the SCE config JSON Schema and explicitly opts new repositories into +/// Agent Trace post-commit synchronization. fn repo_local_config_bootstrap_payload() -> String { format!( - "{{\n \"$schema\": \"{}\"\n}}\n", + "{{\n \"$schema\": \"{}\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }}\n}}\n", crate::services::agent_trace::sce_config_schema_url() ) } @@ -488,7 +489,8 @@ pub fn ensure_git_remote(repository_root: &Path, remote_name: &str) -> Result<() /// Bootstraps the repo-local `.sce/config.json` file if it does not already exist. /// /// Creates the `.sce/` parent directory as needed, then writes the canonical -/// schema-only JSON payload. If the file already exists, it is left untouched. +/// schema and Agent Trace bootstrap JSON payload. If the file already exists, it +/// is left untouched. pub fn bootstrap_repo_local_config(repository_root: &Path) -> Result<()> { let repo_paths = RepoPaths::new(repository_root); let config_file = repo_paths.sce_config_file(); @@ -1948,7 +1950,7 @@ mod tests { assert_eq!( payload, format!( - "{{\n \"$schema\": \"https://sce.crocoder.dev/v{}/config.json\"\n}}\n", + "{{\n \"$schema\": \"https://sce.crocoder.dev/v{}/config.json\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }}\n}}\n", env!("CARGO_PKG_VERSION") ) ); diff --git a/context/architecture.md b/context/architecture.md index a36310f2e..db5d2a103 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -115,7 +115,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `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/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 consumed by startup, setup, and 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/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 (omitted values fall back to `false`; setup bootstrap supplies an explicit `true`), database-retry config resolution and `DATABASE_RETRY_CONFIG` `OnceLock` initialization, default-discovered invalid-file degradation consumed by startup, setup, and 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. @@ -132,7 +132,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `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/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 default-enabled config-file-only `agent_trace.auto_sync` gate launches one detached sync-owned `sync --format json` child unless explicitly disabled in config, 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 > 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; 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. +- `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 > 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; 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. - Generated Claude settings register `SessionStart` and `PostModelSwitch` only for the local model-state hook; `sce hooks session-model` is no longer a supported hook command. Claude Code 2.1.250 and 2.1.251 compatibility smoke passed with the unknown `PostModelSwitch` registration, so SCE installs it unconditionally without a raised minimum or capability gate. The `session_models` table/API and generic session-level fallback lookup were removed in T02 of the `remove-session-models-direct-claude-model-id` plan; the separate `sce hooks claude-model-state` command writes Claude lifecycle observations into the non-exported exact-scope register through the no-migration hook-runtime DB path, without restoring that generic abstraction. `diff-trace` uses direct-first/event-transcript-second Claude `model_id` resolution and consults the exact local lifecycle state only as its final fallback, with direct `tool_version` values. - `cli/src/services/resilience.rs` defines bounded retry/timeout/backoff execution policy (`RetryPolicy`, `run_with_retry`) for transient operation hardening with deterministic failure messaging and retry observability. - `context/cli/agent-trace-auto-sync.md` documents the sync-owned, one-shot post-commit launcher boundary: it reuses `sce sync`, has no daemon or local retry machinery, and fails open when child startup cannot be completed; doctor reports this capability without invoking the launcher. diff --git a/context/cli/agent-trace-auto-sync.md b/context/cli/agent-trace-auto-sync.md index be66f21be..d7651765b 100644 --- a/context/cli/agent-trace-auto-sync.md +++ b/context/cli/agent-trace-auto-sync.md @@ -2,16 +2,18 @@ ## Purpose -Automatic synchronization is a default-enabled convenience layered on the existing +Automatic synchronization is a setup-enabled convenience layered on the existing `sce sync` command. It does not replace explicit synchronization or introduce a -second synchronization engine. +second synchronization engine. A newly created repo-local config opts in +explicitly; a config layer that omits the setting remains disabled at runtime. ## Configuration `agent_trace.auto_sync` is a config-file-only boolean resolved through the normal -global-then-local config merge. It defaults to `true`, and `sce config show` -reports the resolved value and its source. Set it explicitly to `false` to opt -out. There is no environment variable or CLI flag for this setting. +global-then-local config merge. The runtime fallback is `false`, while `sce setup` +writes an explicit `true` when it creates a missing repo-local `.sce/config.json`. +`sce config show` reports the resolved value and its source. Set it explicitly to +`false` to opt out. There is no environment variable or CLI flag for this setting. ## Trigger boundary diff --git a/context/cli/config-precedence-contract.md b/context/cli/config-precedence-contract.md index 033d73680..1109c5b6d 100644 --- a/context/cli/config-precedence-contract.md +++ b/context/cli/config-precedence-contract.md @@ -4,7 +4,7 @@ This contract documents the implemented `sce config` command behavior, runtime resolver, renderer, and canonical Pkl-authored `sce/config.json` schema. The schema is emitted to payload-relative `config/schema/sce-config.schema.json` under Cargo `OUT_DIR` or packaging fallbacks and embedded by `cli/src/services/config/schema.rs` as `SCE_CONFIG_SCHEMA_JSON`; no generated schema is committed. -The current implementation resolves flat logging keys and Agent Trace runtime keys with deterministic precedence and source metadata, exposes resolved-value inspection through `sce config show`, and keeps `sce config validate` focused on validation status plus errors/warnings. File logging is explicitly controlled by the config-file/default `log_to_file` boolean, which defaults to `true`; `log_to_file` and `log_dir` resolve independently, with omitted `log_dir` falling back to the default location. Threshold, format, directory, and `log_file_retention_limit` values are consumed by runtime logging; the concrete logger uses the retention value for primary and v2 creation-triggered cleanup. The default-enabled `agent_trace.auto_sync` value is consumed by the post-commit trigger boundary and by doctor readiness reporting, and can be disabled explicitly. +The current implementation resolves flat logging keys and Agent Trace runtime keys with deterministic precedence and source metadata, exposes resolved-value inspection through `sce config show`, and keeps `sce config validate` focused on validation status plus errors/warnings. File logging is explicitly controlled by the config-file/default `log_to_file` boolean, which defaults to `true`; `log_to_file` and `log_dir` resolve independently, with omitted `log_dir` falling back to the default location. Threshold, format, directory, and `log_file_retention_limit` values are consumed by runtime logging; the concrete logger uses the retention value for primary and v2 creation-triggered cleanup. The config-file-only `agent_trace.auto_sync` value is consumed by the post-commit trigger boundary and by doctor readiness reporting: omitted values resolve to `false`, while `sce setup` writes an explicit `true` for a newly created repo-local config and explicit `false` remains the opt-out. ## Command surface @@ -29,7 +29,7 @@ Agent Trace repository identity keys are also config-file only with per-key `glo - `agent_trace.repository_id` — optional explicit repository identity; resolves as an optional value with no default. - `agent_trace.repository_remote` — Git remote name used to derive repository identity; defaults to `origin` (`DEFAULT_AGENT_TRACE_REPOSITORY_REMOTE` in `cli/src/services/config/resolver.rs`) when no config file sets it. -- `agent_trace.auto_sync` — boolean for the post-commit Agent Trace synchronization trigger; config-file only, with no flag or environment layer, and defaults to `true` (set `false` to opt out). +- `agent_trace.auto_sync` — boolean for the post-commit Agent Trace synchronization trigger; config-file only, with no flag or environment layer. Omitted values resolve to `false`; a newly created repo-local config from `sce setup` explicitly writes `true`, and `false` remains the opt-out. Resolved observability values that currently have no CLI flag layer follow the same lower-precedence chain without a flag step: @@ -95,7 +95,7 @@ When a default-discovered global or repo-local config file exists but fails JSON - `agent_trace` must be an object when present and currently allows `repository_id`, `repository_remote`, and `auto_sync`. - `agent_trace.repository_id` must be a non-empty string when present. - `agent_trace.repository_remote` must be a non-empty string when present; omitted values resolve to `origin`. -- `agent_trace.auto_sync` must be a boolean when present; omitted values resolve to `true`. +- `agent_trace.auto_sync` must be a boolean when present; omitted values resolve to `false`. A newly created repo-local config from `sce setup` contains an explicit `true` value. - `integrations` must be an object when present and currently allows `target` and `optional_workflows`; either key alone yields a parsed `IntegrationsConfig` with the other defaulting to empty. - `integrations.target` must be an array of unique canonical target IDs when present. diff --git a/context/context-map.md b/context/context-map.md index 78328619a..80dbb8fcd 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -17,10 +17,10 @@ Feature/domain context: - `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) -- `context/cli/agent-trace-auto-sync.md` (default-enabled post-commit Agent Trace synchronization with explicit-false opt-out plus doctor readiness reporting: the existing `sce sync` command launched once through the current executable after local persistence, detached null-standard-stream behavior, fail-open startup, canonical managed-block proof of hook readiness, stable text/JSON enabled/disabled/not-ready/not-applicable states, no daemon/queue/high-frequency trigger, and retryability through manual sync and control-plane cursor authority) +- `context/cli/agent-trace-auto-sync.md` (setup-enabled post-commit Agent Trace synchronization with explicit-false opt-out plus doctor readiness reporting: a newly created repo-local config supplies the explicit `true` opt-in while omitted runtime values resolve to `false`; the existing `sce sync` command is launched once through the current executable after local persistence, with detached null-standard-stream behavior, fail-open startup, canonical managed-block proof of hook readiness, stable text/JSON enabled/disabled/not-ready/not-applicable states, no daemon/queue/high-frequency trigger, and retryability through manual sync and control-plane cursor authority) - `context/cli/sync-command.md` (the top-level `sce sync` command: repository-scoped Agent Trace storage resolution, WorkOS-authenticated four-stream control-plane synchronization through the sync-owned consumer-typed `services::sync::progress` reporter contract with sync-owned events, its generic/no-op contract and `indicatif` presentation adapter for aligned stderr progress with independent stream completion, explicit successful finalization, JSON stdout silence, and rejection of the removed `sce trace` command group) - `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`, default-enabled `agent_trace.auto_sync` boolean resolution with explicit-false opt-out 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/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/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) @@ -99,6 +99,7 @@ Supporting repo docs: Recent decision records: +- `context/decisions/2026-09-07-split-auto-sync-defaults.md` (newly bootstrapped repo-local config explicitly opts into Agent Trace auto-sync while omitted runtime values remain disabled) - `context/decisions/2026-09-01-remove-top-level-config-timeout.md` (removes the unused top-level config timeout key, environment override, and config-command flags without introducing a replacement global timeout; nested retry and unrelated runtime timeout paths remain active) - `context/decisions/2026-08-23-codex-canonical-worktree-path-resolution.md` (accepts upstream-compatible Codex apply_patch parent/absolute paths only when canonical resolution remains inside the Git worktree, validates nearest existing prefixes for missing targets, and rejects symlink escapes) - `context/decisions/2026-08-23-codex-nondestructive-hook-ownership.md` (uses one shared structural ownership predicate and merge service for setup/doctor: Codex SCE handlers require the generated helper path plus the `sce hooks codex` contract, while unrelated hook configuration survives) diff --git a/context/decisions/2026-09-07-split-auto-sync-defaults.md b/context/decisions/2026-09-07-split-auto-sync-defaults.md new file mode 100644 index 000000000..a92e4dd5e --- /dev/null +++ b/context/decisions/2026-09-07-split-auto-sync-defaults.md @@ -0,0 +1,71 @@ +# Decision: Split setup and runtime auto-sync defaults + +Date: 2026-09-07 +Status: Accepted +Plan: `context/plans/update-auto-sync-default-behavior.md` +Task: `T01` + +## Context + +The repo-local config created by `sce setup` and the runtime resolver previously +shared an implicit `agent_trace.auto_sync` default. The desired rollout needs +newly bootstrapped repositories to opt into post-commit synchronization while +repositories and config layers that omit the key remain conservative. The +existing boolean schema, explicit values, and global-before-local merge are +already established and must remain compatible. + +## Decision + +`sce setup` writes an explicit `agent_trace.auto_sync: true` in a newly created +repo-local `.sce/config.json`, while the runtime resolver resolves an omitted +`agent_trace.auto_sync` value to `false` with default provenance. + +## Rationale + +The generated setup file provides an intentional, visible opt-in for new +repositories without changing the behavior of existing repositories that have +no such setting. Explicit configuration remains the sole higher-precedence +input, so global/local precedence and opt-out behavior remain stable. + +## Alternatives considered + +- **Keep one implicit `true` default everywhere** — Existing repositories would + continue opting into automatic synchronization without an explicit config + declaration. +- **Use `false` for setup bootstrap and runtime fallback** — New repositories + would not receive the requested setup opt-in. + +## Compatibility and risks + +- Existing config files are left untouched and explicit `true`/`false` values + retain their current meaning; only omitted runtime values change to `false`. +- The setup payload now contains an additional schema-supported field, and its + explicit value is covered by setup bootstrap tests. + +## Guardrails + +- Do not change the schema shape, config precedence, post-commit launcher, or + synchronization protocol. +- Only a newly created repo-local setup config receives the explicit `true`; + existing files are never rewritten by bootstrap. + +## Consequences + +- New repositories created through setup are explicitly opted into automatic + post-commit synchronization. +- Omitted values in global/local config layers resolve to a disabled runtime + gate, making the setup-generated declaration the visible opt-in boundary. + +## Follow-up + +- Update current durable setup and Agent Trace configuration context to state + the split defaults. + +## References + +- Plan: [`update-auto-sync-default-behavior`](../plans/update-auto-sync-default-behavior.md) +- Task: `T01` +- Current-state context: [`CLI config precedence contract`](../cli/config-precedence-contract.md) +- Current-state context: [`Automatic Agent Trace synchronization`](../cli/agent-trace-auto-sync.md) +- Evidence: [`setup bootstrap implementation`](../../cli/src/services/setup/mod.rs) +- Evidence: [`runtime resolver implementation`](../../cli/src/services/config/resolver.rs) diff --git a/context/glossary.md b/context/glossary.md index e23eb5e0b..3e5b89099 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -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`) before any setup writes begin; enforces that all `sce setup` modes (config-only, hooks-only, combined, and interactive) require the current directory to be inside a git repository, failing with actionable guidance to run `git init` and rerun `sce setup` when the precondition is not met. -- `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 declaration plus explicit `agent_trace.auto_sync: true` bootstrap opt-in, `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 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. @@ -247,4 +247,4 @@ - `parts table (Agent Trace DB)`: Agent Trace DB table created by migration `009_create_parts.sql`; stores append-only message parts with columns `type` (typed by Rust as `text`/`reasoning`/`patch`/`question` and stored as unconstrained `TEXT NOT NULL`), `text`, `message_id`, `session_id`, `generated_at_unix_ms`, `created_at`, `updated_at`. Uses only the internal `id` for row identity (no upsert/dedup). Multiple parts can exist for the same `(session_id, message_id)`. A compound index on `(session_id, message_id, generated_at_unix_ms, id)` enables ordered joins. No foreign keys to `messages` or any other table, so parts may be inserted before their parent message exists. - `AgentTraceExportReader`: Read-only incremental export reader in `cli/src/services/agent_trace_export/mod.rs` over one `RepositoryAgentTraceDb`, exposing `read_messages_after`/`read_parts_after`/`read_diff_traces_after`/`read_agent_traces_after`, each `(cursor: i64, limit: usize) -> Result>` over `WHERE id > cursor ORDER BY id ASC LIMIT {limit}`. Holds no local cursor, performs no mutation, makes no network calls, and returns owned camelCase `serde::Serialize` export-row DTOs matching the shipped control-plane ingestion contract. See `context/sce/agent-trace-export-readers.md`. - `context synchronization lifecycle`: Durable task-level state for synchronization after successful `/next-task` execution. The task record is `pending`, `synced`, or `blocked`; blocked records carry a blocker, required action, and retry condition. Missing lifecycle state on a completed task is unresolved debt, not evidence of synchronization. `/validate` does not persist a plan-level synchronization lifecycle. See `context/sce/shared-context-code-workflow.md`. -- `agent_trace.auto_sync`: Config-file-only boolean resolved by the shared config layers for the post-commit Agent Trace synchronization boundary; omitted values are `true` and explicit `false` opts out, and `sce config show` reports its winning source. After validation and repository-DB persistence, enabled post-commit runs launch the existing sync-owned `sync --format json` command once through the current executable with detached null-standard-stream child semantics and repository-root working directory; the hook never waits, has no daemon or high-frequency trigger, and treats launcher failures as fail-open. See [Agent Trace hook routing](sce/agent-trace-hooks-command-routing.md). +- `agent_trace.auto_sync`: Config-file-only boolean resolved by the shared config layers for the post-commit Agent Trace synchronization boundary; omitted values are `false`, while setup writes an explicit `true` into a newly created repo-local config, and explicit `false` opts out. `sce config show` reports the winning source. After validation and repository-DB persistence, enabled post-commit runs launch the existing sync-owned `sync --format json` command once through the current executable with detached null-standard-stream child semantics and repository-root working directory; the hook never waits, has no daemon or high-frequency trigger, and treats launcher failures as fail-open. See [Agent Trace hook routing](sce/agent-trace-hooks-command-routing.md). diff --git a/context/overview.md b/context/overview.md index 8153fd8b5..37e8bccef 100644 --- a/context/overview.md +++ b/context/overview.md @@ -12,7 +12,7 @@ The generated `/next-task` workflow persists task-level context-synchronization - **Stderr diagnostics:** stable `SCE-ERR-{PARSE,VALIDATION,RUNTIME,DEPENDENCY}` codes with class-default `Try:` remediation (see `context/sce/cli-error-code-taxonomy.md`). - **Stdout/stderr:** command payloads on stdout only; redacted diagnostics and text-mode `sce sync` progress on stderr, while JSON sync remains silent (see `context/sce/cli-stdout-stderr-contract.md`). - **Observability:** config-resolved logging with tracing, explicit config-file/default `log_to_file` control, error-specific stderr suppression when file logging is enabled, and optional dated/session-partitioned `log_dir` / `SCE_LOG_DIR` files with retention (see `context/sce/cli-observability-contract.md`). -- **Config precedence:** `flags > env > config file > defaults` (see `context/cli/config-precedence-contract.md`); invalid default-discovered config remains degradable for ordinary startup, setup, and Agent Trace storage, while explicit `--config` and `SCE_CONFIG_FILE` failures remain fatal. Setup preserves invalid repo-local files and may omit target/optional-workflow persistence for that run. The config-file-only `agent_trace.auto_sync` setting defaults to `true` and is resolved with source metadata for the post-commit trigger and doctor readiness boundaries. Its asynchronous post-commit behavior and doctor capability reporting are documented in `context/cli/agent-trace-auto-sync.md`. +- **Config precedence:** `flags > env > config file > defaults` (see `context/cli/config-precedence-contract.md`); invalid default-discovered config remains degradable for ordinary startup, setup, and Agent Trace storage, while explicit `--config` and `SCE_CONFIG_FILE` failures remain fatal. Setup preserves invalid repo-local files and may omit target/optional-workflow persistence for that run. The config-file-only `agent_trace.auto_sync` setting resolves omitted values to `false`, while setup explicitly writes `true` into a newly created repo-local config; source metadata feeds the post-commit trigger and doctor readiness boundaries. Its asynchronous post-commit behavior and doctor capability reporting are documented in `context/cli/agent-trace-auto-sync.md`. - **Attribution hooks:** enabled by default, gated by staged-diff AI-overlap preflight; `SCE_ATTRIBUTION_HOOKS_DISABLED` opt-out (see `context/sce/agent-trace-commit-msg-coauthor-policy.md`). - **Install channels:** repo-flake Nix, Cargo, npm, and source-built Flatpak (`dev.crocoder.sce`); Homebrew deferred (see `context/sce/cli-first-install-channels-contract.md`). diff --git a/context/patterns.md b/context/patterns.md index ff5cc5d03..591661368 100644 --- a/context/patterns.md +++ b/context/patterns.md @@ -127,8 +127,8 @@ - For observability log-directory configuration, resolve `log_dir` through `SCE_LOG_DIR` > config-file `log_dir` > `default_paths::observability_log_dir()` (`/sce/logs`; Linux `${XDG_STATE_HOME:-~/.local/state}/sce/logs`); select log files per emission from the machine-local date and optional logger session context, append rendered records to the selected file, run retention only after successfully creating a selected file, and keep session IDs out of rendered log schemas unless a caller explicitly passes them as normal fields. - Keep `log_file_retention_limit` flat and config-file/default only: validate it as an integer with minimum `1`, merge global before local, default it to `10`, expose resolved source metadata without adding an environment variable or CLI flag, and pass the resolved value unchanged to primary and v2 creation-triggered logger cleanup. -- For runtime CLI configuration, keep precedence deterministic and explicit (`flags > env > config file > defaults`) and expose inspect/validate command entrypoints with stable text/JSON outputs. Config-file-only Agent Trace runtime switches such as `agent_trace.auto_sync` should document their default and explicit opt-out, resolve global before local, and expose winning source metadata without adding an environment variable or CLI flag unless the contract explicitly requires one. -- For default-enabled automatic Agent Trace synchronization, keep the post-commit launcher as a one-shot reuse of `sce sync`: start the current executable after local Agent Trace persistence, use the repository root and null child streams, do not wait, and fail open on launcher errors. Keep manual sync and the control-plane cursor authority as the retry path; do not add daemons, watchers, polling, queues, or high-frequency hook triggers. +- For runtime CLI configuration, keep precedence deterministic and explicit (`flags > env > config file > defaults`) and expose inspect/validate command entrypoints with stable text/JSON outputs. Config-file-only Agent Trace runtime switches such as `agent_trace.auto_sync` should document the resolver fallback and explicit opt-out, distinguish setup-written bootstrap values, resolve global before local, and expose winning source metadata without adding an environment variable or CLI flag unless the contract explicitly requires one. +- For setup-enabled automatic Agent Trace synchronization, keep the post-commit launcher as a one-shot reuse of `sce sync`: start the current executable after local Agent Trace persistence when the resolved setting is true, use the repository root and null child streams, do not wait, and fail open on launcher errors. Keep manual sync and the control-plane cursor authority as the retry path; do not add daemons, watchers, polling, queues, or high-frequency hook triggers. - For commands that support text/JSON dual output, centralize `--format ` parsing in one shared contract and pass command-specific `--help` guidance into invalid-value errors instead of duplicating parser logic per command. - For setup-style command contracts, keep interactive mode as the zero-flag default and enforce mutually-exclusive explicit target flags for non-interactive automation. - For setup config safety, resolve invalid default-discovered config through the shared degraded layer behavior, preserve the invalid repo-local file byte-for-byte, and allow Git/remote preflight, context bootstrap, lifecycle providers, hooks, and target assets to continue; preserve create-if-missing behavior for absent config, skip target/optional-workflow persistence when the local file is invalid, and keep explicit `--config` / `SCE_CONFIG_FILE` failures fatal. diff --git a/context/plans/update-auto-sync-default-behavior.md b/context/plans/update-auto-sync-default-behavior.md new file mode 100644 index 000000000..42b663fb4 --- /dev/null +++ b/context/plans/update-auto-sync-default-behavior.md @@ -0,0 +1,136 @@ +# Plan: update-auto-sync-default-behavior + +## Change summary + +Separate the two `agent_trace.auto_sync` defaults that currently share one +configuration concept. When `sce setup` creates a missing repo-local +`.sce/config.json`, the generated file will explicitly contain +`"auto_sync": true`, opting the new repository into post-commit synchronization. +The runtime config resolver will remain conservative: an omitted value resolves +to `false`, while explicit config values continue to control behavior. + +The existing schema and post-commit trigger remain unchanged apart from these +default boundaries. Focused setup and resolver tests will make the distinction +regression-safe, and durable context will be corrected where it currently treats +the setup payload and resolver fallback as the same default. + +## Acceptance criteria + +How this plan is proven complete. Each criterion is observable and names the +check that proves it. `/validate` runs these checks; no task in the stack +performs final validation. + +- [x] AC1: A newly generated repo-local `.sce/config.json` explicitly contains `"agent_trace": { "auto_sync": true }` alongside its schema declaration. + - Validate: setup bootstrap tests assert the generated payload/file contains the explicit `agent_trace.auto_sync` value. +- [x] AC2: When `agent_trace.auto_sync` is absent from all config layers, the resolver returns `false` with default provenance; explicit `true` and `false` values and existing global/local precedence remain unchanged. + - Validate: focused config resolver tests cover the missing-value fallback, explicit values, and local-over-global resolution. +- [x] AC3: Durable SCE configuration and setup documentation distinguishes setup's explicit `true` bootstrap value from the resolver's `false` fallback without changing the documented post-commit opt-out semantics. + - Validate: manual review of the affected durable context files against the implemented setup payload and resolver branch. + +### Full validation + +Repository-wide checks `/validate` runs after the last task, regardless of +which criterion they map to. + +- `nix flake check` +- `nix run .#pkl-check-generated` + +### Context sync + +- `context/overview.md` +- `context/architecture.md` +- `context/patterns.md` +- `context/glossary.md` +- `context/context-map.md` +- `context/cli/config-precedence-contract.md` +- `context/cli/agent-trace-auto-sync.md` +- `context/sce/setup-repo-local-config-bootstrap.md` +- `context/sce/agent-trace-hooks-command-routing.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:** repo-local setup config bootstrap payload and tests; config resolver omitted-value fallback and tests; the durable context files listed under Context sync. +- **Out of scope:** changes to the JSON schema's accepted shape, explicit config precedence, post-commit launcher behavior, synchronization protocol, generated target trees, and unrelated setup persistence. +- **Constraints:** preserve existing files when `.sce/config.json` already exists; preserve explicit `agent_trace.auto_sync` values and global-before-local merge behavior; use repository test and validation commands through Nix; do not edit generated artifacts. +- **Non-goal:** making every resolver default or every existing repository opt into auto-sync; only a newly created setup config receives the explicit `true` value. + +## Assumptions + +- The existing optional `agent_trace.auto_sync` schema field and config inspection surfaces already support the required boolean; this change only separates setup serialization from missing-value resolution. +- The current completed `automatic-agent-trace-sync` plan remains historical context and is not amended; this request is tracked as a new plan as requested. + +## Task stack + +- [x] T01: `Separate setup bootstrap and resolver auto_sync defaults` (status:complete) + - Task ID: T01 + - Scope: In — `cli/src/services/setup/mod.rs` bootstrap serialization/tests and `cli/src/services/config/resolver.rs` missing-value fallback/tests. Out — schema changes, hook/launcher behavior, and durable context edits. + - Dependencies: none + - Done when: newly created setup config payloads explicitly serialize `agent_trace.auto_sync` as `true`; missing resolver values remain `false` with default provenance; explicit values and global/local precedence still pass their focused tests. + - Verify: `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup`; `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::`. + - Completed: 2026-09-07 + - Files changed: + - `cli/src/services/setup/mod.rs` + - `cli/src/services/config/resolver.rs` + - Result: Setup bootstrap payloads now explicitly write `agent_trace.auto_sync: true`; omitted runtime values resolve to `false` with default provenance; explicit values and local-over-global precedence remain covered by focused tests. + - Verify: + - `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` — passed (69 tests). + - `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` — passed (59 tests). + - Context impact: `application_behavior` — setup bootstrap serialization and runtime config resolution changed; durable context remains pending for T02. + - Context synchronization: synced + +- [x] T02: `Document the intentionally split auto_sync defaults` (status:complete) + - Task ID: T02 + - Scope: In — the durable context files listed under Context sync, updating setup-generation and resolver-fallback statements to match T01. Out — application code, tests, generated outputs, and historical plan/decision records. + - Dependencies: T01 + - Done when: current context consistently says setup writes explicit `true` for a newly generated config, resolver fallback is `false` when missing, and explicit opt-out/trigger behavior is unchanged. + - Verify: manual review of the affected context files against `cli/src/services/setup/mod.rs` and `cli/src/services/config/resolver.rs`. + - Completed: 2026-09-07 + - Files changed: + - `context/architecture.md` + - `context/context-map.md` + - Result: Durable context now distinguishes the explicit `true` setup bootstrap opt-in from the resolver's `false` omitted-value fallback while preserving explicit configuration, precedence, trigger, and fail-open behavior. + - Verify: + - Manual review of the affected context files against `cli/src/services/setup/mod.rs` and `cli/src/services/config/resolver.rs` — passed; all listed setup/config/auto-sync statements align with the implementation, and stale current-state default wording was corrected. + - Context impact: documentation — current setup, config, and Agent Trace auto-sync context is synchronized with T01; the mandatory context synchronization pass remains required. + - Context synchronization: synced + +## Open questions + +None. The requested setup value, resolver fallback, separation boundary, and test coverage are explicit; remaining choices follow existing config and setup conventions. + +## Validation Report + +**Status:** validated +**Date:** 2026-09-07 + +### Commands run + +- `nix flake check` -> exit 0 (flake evaluation and checks passed) +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral Pkl generation passed with 141 files) +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` -> exit 0 (69 setup tests passed) +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` -> exit 0 (59 config tests passed) + +### Success-criteria verification + +- [x] AC1: A newly generated repo-local `.sce/config.json` explicitly contains `"agent_trace": { "auto_sync": true }` alongside its schema declaration. -> setup test suite passed, including `repo_local_config_bootstrap_payload_uses_versioned_schema_url` and generated-payload/file bootstrap coverage. +- [x] AC2: When `agent_trace.auto_sync` is absent from all config layers, the resolver returns `false` with default provenance; explicit `true` and `false` values and existing global/local precedence remain unchanged. -> config resolver suite passed, including missing default, explicit true/false, and local-over-global tests. +- [x] AC3: Durable SCE configuration and setup documentation distinguishes setup's explicit `true` bootstrap value from the resolver's `false` fallback without changing the documented post-commit opt-out semantics. -> manually reviewed `context/overview.md`, `context/architecture.md`, `context/patterns.md`, `context/glossary.md`, `context/context-map.md`, `context/cli/config-precedence-contract.md`, `context/cli/agent-trace-auto-sync.md`, `context/sce/setup-repo-local-config-bootstrap.md`, and `context/sce/agent-trace-hooks-command-routing.md` against `cli/src/services/setup/mod.rs` and `cli/src/services/config/resolver.rs`; statements align. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/sce/agent-trace-hooks-command-routing.md b/context/sce/agent-trace-hooks-command-routing.md index 10984ad6e..41103000c 100644 --- a/context/sce/agent-trace-hooks-command-routing.md +++ b/context/sce/agent-trace-hooks-command-routing.md @@ -65,7 +65,7 @@ - Post-commit Agent Trace success requires both schema validation and Agent Trace DB `agent_traces` persistence to succeed. - Current command-surface success output is: `post-commit hook processed intersection: commit=, intersection_files=`. - After Agent Trace validation and `agent_traces` persistence succeed, post-commit runs exactly one passive WAL checkpoint through `RepositoryAgentTraceDb::passive_checkpoint()` (see [shared-turso-db.md](shared-turso-db.md)) before resolving auto-sync. This is routine maintenance, not a durability boundary: a checkpoint failure is logged as a warning via `Logger::warn` with event `sce.agent_trace_db.passive_checkpoint_failed` and does not fail the hook or affect already-persisted Agent Trace data. `diff-trace` and `conversation-trace` do not checkpoint per write; only this one post-commit call site does. -- After Agent Trace validation and `agent_traces` persistence succeed, post-commit resolves the config-file-only `agent_trace.auto_sync` gate. When it is `true`, the hook invokes the sync-owned one-shot launcher exactly once with the repository root; the launcher starts the current `sce` executable as detached `sync --format json` work and is not awaited. Explicit `false` configuration does not launch; omitted configuration launches, and validation or persistence failure reaches the existing error path before the gate. Launcher/current-executable/spawn failures are fail-open and do not change the successful post-commit result. No `pre-commit`, `diff-trace`, or `conversation-trace` path invokes automatic synchronization. +- After Agent Trace validation and `agent_traces` persistence succeed, post-commit resolves the config-file-only `agent_trace.auto_sync` gate. When it is `true`, the hook invokes the sync-owned one-shot launcher exactly once with the repository root; the launcher starts the current `sce` executable as detached `sync --format json` work and is not awaited. Explicit `false` configuration and omitted configuration do not launch; a newly created setup config launches because it contains an explicit `true`. Validation or persistence failure reaches the existing error path before the gate. Launcher/current-executable/spawn failures are fail-open and do not change the successful post-commit result. No `pre-commit`, `diff-trace`, or `conversation-trace` path invokes automatic synchronization. - `post-rewrite` is a deterministic no-op entrypoint. - `diff-trace` reads STDIN JSON and classifies the payload: - **Claude structured payloads** (detected by presence of top-level `hook_event_name`): the STDIN JSON is validated through `derive_claude_structured_patch`. Supported `PostToolUse` `Write` create and `Edit` structured-patch events produce a `DiffTracePayload` with `payload_type="structured"` and the raw event JSON stored as the `diff` column without conversion to unified-diff text. Model attribution is resolved with `direct > exact transcript > exact Claude state > NULL`: top-level `model`, `model_id`, or `modelId`, or nested `model.id`, `model.model`, or `model.name`, wins when present. Otherwise, when the event provides both `transcript_path` and `tool_use_id`, Rust scans that Claude JSONL transcript for the assistant-message envelope whose `tool_use.id` matches, skipping malformed unrelated records. Either source is normalized once with the `claude/` prefix. For these raw structured Claude payloads only, if both event-local sources are unavailable, persistence performs one exact `(cc_, agent_id)` lookup in the local `claude_model_state` register after opening the repository DB; normalized payloads with `tool_name="claude"` do not qualify, and absent state remains nullable. Ephemeral `agent_id` is trimmed for exact lookup, with missing/null main-session context mapped to `""`; subagents never inherit main-session state. Missing/unreadable transcripts, unmatched tool calls, missing models, or absent lookup fields leave the parser's `model_id` nullable without rejecting the hook. No polling, waiting, or stored-raw-event reparsing participates. Unsupported Claude events (non-`PostToolUse`, unsupported tools, invalid payloads) produce a deterministic `NoOp` success result. diff --git a/context/sce/doctor-human-text-contract.md b/context/sce/doctor-human-text-contract.md index c47874ad5..61b24bd4c 100644 --- a/context/sce/doctor-human-text-contract.md +++ b/context/sce/doctor-human-text-contract.md @@ -57,13 +57,14 @@ JSON exposes the same fact as `post_commit_auto_sync` with stable `state`, remediation, and overall readiness semantics remain the source of blocking diagnostics. -The resolved `enabled` value defaults to `true` when `agent_trace.auto_sync` is -omitted and is `false` only for the explicit config opt-out. `source` reports -`default` or `config_file` for resolved values, or `unresolved` when config -resolution fails; `config_source` identifies the discovered global or local -config layer when applicable and is otherwise `null`. Doctor only reports -this fact: it never launches `sce sync` or a background process. The post-commit -runtime still launches one detached `sync --format json` child only after +The resolved `enabled` value defaults to `false` when `agent_trace.auto_sync` is +omitted; a newly created setup config contains an explicit `true`, and an +explicit `false` remains disabled. `source` reports `default` or `config_file` +for resolved values, or `unresolved` when config resolution fails; +`config_source` identifies the discovered global or local config layer when +applicable and is otherwise `null`. Doctor only reports this fact: it never +launches `sce sync` or a background process. The post-commit runtime still +launches one detached `sync --format json` child only after successful Agent Trace persistence when enabled, and launcher failures remain fail-open. diff --git a/context/sce/setup-repo-local-config-bootstrap.md b/context/sce/setup-repo-local-config-bootstrap.md index 116db2769..ee0325c34 100644 --- a/context/sce/setup-repo-local-config-bootstrap.md +++ b/context/sce/setup-repo-local-config-bootstrap.md @@ -7,7 +7,7 @@ Task `setup-repo-gate-and-local-config-bootstrap` T02, `turso-local-db-sync` T04 ## Behavior - Any successful `sce setup` run in a git-backed repository creates `.sce/config.json` when the file is absent. -- The bootstrap writes the canonical schema-only JSON payload: `{"$schema": "https://sce.crocoder.dev/v/config.json"}` (where `` is the CLI release version, with a trailing newline). +- The bootstrap writes the canonical JSON payload with the versioned schema declaration and explicit Agent Trace opt-in: `{"$schema": "https://sce.crocoder.dev/v/config.json", "agent_trace": {"auto_sync": true}}` (where `` is the CLI release version, with a trailing newline). - If `.sce/config.json` already exists, the bootstrap step returns `Ok(())` immediately and leaves the file untouched — no merge, no reformat, no overwrite. - The parent `.sce/` directory is created via `fs::create_dir_all` if missing. - The setup flow also bootstraps the canonical local DB through `LocalDbLifecycle::setup` and the Agent Trace DB through `AgentTraceDbLifecycle::setup`; both use the shared `TursoDb` adapter. @@ -62,6 +62,6 @@ The same write also records the run's resolved optional-workflow selection under - Default-discovered invalid repo-local config is degradable and never rewritten; explicit `--config` / `SCE_CONFIG_FILE` selections remain fatal, while absent local config remains create-if-missing. - Context baseline bootstrap is independent of config/DB/hooks install and runs before those steps on normal setup paths. - Local bootstrap (repo config + local DB init) is independent of config install and hook install; it runs before both after context baseline bootstrap. -- The bootstrap payload matches the `$schema` declaration accepted by startup config loading and the Pkl-authored JSON Schema embedded from Cargo `OUT_DIR`. +- The bootstrap payload matches the `$schema` declaration accepted by startup config loading and the Pkl-authored JSON Schema embedded from Cargo `OUT_DIR`; its explicit `agent_trace.auto_sync: true` is distinct from the runtime resolver's `false` fallback for omitted values. See also [the degraded discovered-config boundary decision](../decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md) and the superseded [fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md). From 55208366494631876173700fb1df8623819093e4 Mon Sep 17 00:00:00 2001 From: Ivan Ivic Date: Wed, 9 Sep 2026 14:31:24 +0200 Subject: [PATCH 3/3] setup: Add interactive behavior confirmations and persistence Add independent Yes-default prompts for Agent Trace auto-sync and commit attribution, persisting selections while preserving non-interactive config behavior and existing runtime gates. Document the configuration contract and validation coverage. Co-authored-by: SCE --- README.md | 18 ++ cli/src/services/setup/command.rs | 28 +- cli/src/services/setup/mod.rs | 121 ++++++++- context/architecture.md | 1 + context/cli/agent-trace-auto-sync.md | 18 +- context/cli/cli-command-surface.md | 8 +- context/cli/config-precedence-contract.md | 10 +- context/context-map.md | 2 + context/glossary.md | 4 +- context/overview.md | 1 + context/patterns.md | 1 + .../interactive-setup-behavior-prompts.md | 257 ++++++++++++++++++ .../agent-trace-commit-msg-coauthor-policy.md | 4 + .../sce/agent-trace-hooks-command-routing.md | 4 + .../sce/setup-repo-local-config-bootstrap.md | 16 +- 15 files changed, 464 insertions(+), 29 deletions(-) create mode 100644 context/plans/interactive-setup-behavior-prompts.md diff --git a/README.md b/README.md index 73731433c..e7562da96 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,24 @@ sce doctor # verify the install is healthy `sce setup` writes OpenCode, Claude Code, and/or Pi config into your repo, installs the required git hooks, and initializes the per-repo Agent Trace database. Use `sce setup --pi` for Pi only, or `sce setup --all` for OpenCode + Claude Code + Pi. `sce doctor` is read-only by default; `sce doctor --fix` will repair the issues it knows how to repair (missing or stale hooks, missing canonical DB parent directories) and report the rest for manual follow-up. +With plain `sce setup`, after choosing targets and optional workflows, answer two independent confirmations: + +```text +Enable automatic Agent Trace synchronization? [Y/n] +Enable SCE commit attribution trailers? [Y/n] +``` + +Both default to Yes, so pressing Enter enables that choice; answering `n` disables only the corresponding behavior. A successful target setup persists the answers in `.sce/config.json`: + +```json +{ + "agent_trace": { "auto_sync": true }, + "policies": { "attribution_hooks": { "enabled": true } } +} +``` + +Non-interactive setup asks no questions. It explicitly enables both behaviors only when creating a missing repo-local config; an existing config's behavior values and omissions are not changed merely by a non-interactive run. Runtime precedence and hook behavior remain unchanged: `agent_trace.auto_sync` is config-file-only with omitted fallback `false`, while attribution can still be disabled by `SCE_ATTRIBUTION_HOOKS_DISABLED`, `SCE_DISABLED`, or its config value. + ## Bash policy **Stop agents from running commands your repo does not allow.** diff --git a/cli/src/services/setup/command.rs b/cli/src/services/setup/command.rs index 668c23236..cebbce8a3 100644 --- a/cli/src/services/setup/command.rs +++ b/cli/src/services/setup/command.rs @@ -44,7 +44,14 @@ impl SetupCommand { setup::SetupDispatch::Proceed { mode: resolved_mode, optional_workflows, - } => Some((resolved_mode, optional_workflows)), + agent_trace_auto_sync, + attribution_hooks_enabled, + } => Some(( + resolved_mode, + optional_workflows, + agent_trace_auto_sync, + attribution_hooks_enabled, + )), setup::SetupDispatch::Cancelled => { return Ok(setup::setup_cancelled_text()); } @@ -84,16 +91,27 @@ impl SetupCommand { } // Handle config target installation (OpenCode/Claude assets). - if let Some((resolved_mode, prompted_optional_workflows)) = setup_dispatch { + if let Some(( + resolved_mode, + prompted_optional_workflows, + agent_trace_auto_sync, + attribution_hooks_enabled, + )) = setup_dispatch + { // A prompted selection is authoritative for the run; without one the // `--workflow` selection (or, absent that, the persisted one) applies. let optional_workflows = prompted_optional_workflows .as_deref() .or(self.request.optional_workflows.as_deref()); - let setup_message = - setup::run_setup_for_mode(&repository_root, resolved_mode, optional_workflows) - .map_err(CliError::runtime)?; + let setup_message = setup::run_setup_for_mode( + &repository_root, + resolved_mode, + optional_workflows, + agent_trace_auto_sync, + attribution_hooks_enabled, + ) + .map_err(CliError::runtime)?; sections.push(setup_message); } diff --git a/cli/src/services/setup/mod.rs b/cli/src/services/setup/mod.rs index f21bd79a3..ede945935 100644 --- a/cli/src/services/setup/mod.rs +++ b/cli/src/services/setup/mod.rs @@ -60,7 +60,7 @@ pub(crate) fn is_missing_git_remote_error(error: &anyhow::Error) -> bool { /// Agent Trace post-commit synchronization. fn repo_local_config_bootstrap_payload() -> String { format!( - "{{\n \"$schema\": \"{}\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }}\n}}\n", + "{{\n \"$schema\": \"{}\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }},\n \"policies\": {{\n \"attribution_hooks\": {{\n \"enabled\": true\n }}\n }}\n}}\n", crate::services::agent_trace::sce_config_schema_url() ) } @@ -230,6 +230,12 @@ pub enum SetupDispatch { /// The optional workflows this run installs. `None` means no selection /// was resolved here, so the persisted selection is reused downstream. optional_workflows: Option>, + /// The interactive selection for automatic Agent Trace synchronization. + /// `None` means setup did not prompt for this value. + agent_trace_auto_sync: Option, + /// The interactive selection for SCE commit-attribution trailers. + /// `None` means setup did not prompt for this value. + attribution_hooks_enabled: Option, }, Cancelled, } @@ -401,6 +407,8 @@ pub fn run_setup_for_mode( repository_root: &Path, mode: SetupMode, optional_workflows: Option<&[String]>, + agent_trace_auto_sync: Option, + attribution_hooks_enabled: Option, ) -> Result { let target = match mode { SetupMode::Interactive => { @@ -426,13 +434,19 @@ pub fn run_setup_for_mode( })?; // Persist selected integration targets and optional workflows in repo-local config. - persist_integration_targets(repository_root, target, &selected_optional_workflows) - .with_context(|| { - format!( - "Setup assets were installed for {} but failed to update repo-local config", - setup_target_label(target) - ) - })?; + persist_integration_targets( + repository_root, + target, + &selected_optional_workflows, + agent_trace_auto_sync, + attribution_hooks_enabled, + ) + .with_context(|| { + format!( + "Setup assets were installed for {} but failed to update repo-local config", + setup_target_label(target) + ) + })?; Ok(format_setup_install_success_message(&outcome)) } @@ -489,8 +503,8 @@ pub fn ensure_git_remote(repository_root: &Path, remote_name: &str) -> Result<() /// Bootstraps the repo-local `.sce/config.json` file if it does not already exist. /// /// Creates the `.sce/` parent directory as needed, then writes the canonical -/// schema and Agent Trace bootstrap JSON payload. If the file already exists, it -/// is left untouched. +/// schema and explicit Agent Trace/attribution bootstrap JSON payload. If the +/// file already exists, it is left untouched. pub fn bootstrap_repo_local_config(repository_root: &Path) -> Result<()> { let repo_paths = RepoPaths::new(repository_root); let config_file = repo_paths.sce_config_file(); @@ -826,6 +840,8 @@ pub fn persist_integration_targets( repository_root: &Path, target: SetupTarget, selected_optional_workflows: &[String], + agent_trace_auto_sync: Option, + attribution_hooks_enabled: Option, ) -> Result<()> { let repo_paths = RepoPaths::new(repository_root); let config_file = repo_paths.sce_config_file(); @@ -895,6 +911,37 @@ pub fn persist_integration_targets( }), ); + if let Some(value) = agent_trace_auto_sync { + let agent_trace = config_obj.entry("agent_trace").or_insert_with(|| json!({})); + let agent_trace_obj = agent_trace.as_object_mut().with_context(|| { + format!( + "Config file '{}' must contain an object at 'agent_trace'.", + config_file.display() + ) + })?; + agent_trace_obj.insert("auto_sync".to_string(), json!(value)); + } + + if let Some(value) = attribution_hooks_enabled { + let policies = config_obj.entry("policies").or_insert_with(|| json!({})); + let policies_obj = policies.as_object_mut().with_context(|| { + format!( + "Config file '{}' must contain an object at 'policies'.", + config_file.display() + ) + })?; + let attribution_hooks = policies_obj + .entry("attribution_hooks") + .or_insert_with(|| json!({})); + let attribution_hooks_obj = attribution_hooks.as_object_mut().with_context(|| { + format!( + "Config file '{}' must contain an object at 'policies.attribution_hooks'.", + config_file.display() + ) + })?; + attribution_hooks_obj.insert("enabled".to_string(), json!(value)); + } + let updated = serde_json::to_string_pretty(&config).with_context(|| { format!( "Failed to serialize updated config for '{}'", @@ -1611,6 +1658,14 @@ pub trait SetupTargetPrompter { /// The optional workflows to install, pre-checked from `defaults`. /// `None` means the operator cancelled the prompt. fn prompt_optional_workflows(&self, defaults: &[String]) -> Result>>; + + /// Whether automatic Agent Trace synchronization should be enabled. + /// `None` means the operator cancelled the prompt. + fn prompt_agent_trace_auto_sync(&self) -> Result>; + + /// Whether SCE commit-attribution trailers should be enabled. + /// `None` means the operator cancelled the prompt. + fn prompt_attribution_hooks_enabled(&self) -> Result>; } #[derive(Clone, Copy, Debug, Default)] @@ -1624,6 +1679,14 @@ impl SetupTargetPrompter for InquireSetupTargetPrompter { fn prompt_optional_workflows(&self, defaults: &[String]) -> Result>> { prompt::prompt_optional_workflows(defaults) } + + fn prompt_agent_trace_auto_sync(&self) -> Result> { + prompt::prompt_agent_trace_auto_sync() + } + + fn prompt_attribution_hooks_enabled(&self) -> Result> { + prompt::prompt_attribution_hooks_enabled() + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -1660,7 +1723,7 @@ fn setup_prompt_title_with_color_policy(color_enabled: bool) -> String { mod prompt { use anyhow::{bail, Result}; - use inquire::{InquireError, MultiSelect, Select}; + use inquire::{Confirm, InquireError, MultiSelect, Select}; use crate::services::style::{ prompt_label, prompt_label_with_color_policy, prompt_value_with_color_policy, @@ -1672,6 +1735,8 @@ mod prompt { SetupDispatch::Proceed { mode: SetupMode::NonInteractive(target), optional_workflows: None, + agent_trace_auto_sync: None, + attribution_hooks_enabled: None, } } @@ -1731,6 +1796,25 @@ mod prompt { } } + pub(super) fn prompt_agent_trace_auto_sync() -> Result> { + prompt_confirmation("Enable automatic Agent Trace synchronization?") + } + + pub(super) fn prompt_attribution_hooks_enabled() -> Result> { + prompt_confirmation("Enable SCE commit attribution trailers?") + } + + fn prompt_confirmation(label: &str) -> Result> { + match Confirm::new(label).with_default(true).prompt() { + Ok(value) => Ok(Some(value)), + Err(InquireError::OperationCanceled | InquireError::OperationInterrupted) => Ok(None), + Err(InquireError::NotTTY) => bail!( + "Interactive setup requires a TTY. Re-run with '--non-interactive' and one of '--opencode', '--claude', '--pi', '--codex', or '--all'." + ), + Err(error) => Err(error.into()), + } + } + /// One selectable row per optional workflow, in catalog order. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct OptionalWorkflowRow { @@ -1864,14 +1948,27 @@ where return Ok(SetupDispatch::Cancelled); }; + let Some(agent_trace_auto_sync) = prompter.prompt_agent_trace_auto_sync()? else { + return Ok(SetupDispatch::Cancelled); + }; + + let Some(attribution_hooks_enabled) = prompter.prompt_attribution_hooks_enabled()? + else { + return Ok(SetupDispatch::Cancelled); + }; + Ok(SetupDispatch::Proceed { mode, optional_workflows: Some(optional_workflows), + agent_trace_auto_sync: Some(agent_trace_auto_sync), + attribution_hooks_enabled: Some(attribution_hooks_enabled), }) } SetupMode::NonInteractive(target) => Ok(SetupDispatch::Proceed { mode: SetupMode::NonInteractive(target), optional_workflows: None, + agent_trace_auto_sync: None, + attribution_hooks_enabled: None, }), } } @@ -1950,7 +2047,7 @@ mod tests { assert_eq!( payload, format!( - "{{\n \"$schema\": \"https://sce.crocoder.dev/v{}/config.json\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }}\n}}\n", + "{{\n \"$schema\": \"https://sce.crocoder.dev/v{}/config.json\",\n \"agent_trace\": {{\n \"auto_sync\": true\n }},\n \"policies\": {{\n \"attribution_hooks\": {{\n \"enabled\": true\n }}\n }}\n}}\n", env!("CARGO_PKG_VERSION") ) ); diff --git a/context/architecture.md b/context/architecture.md index db5d2a103..bae84530a 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -117,6 +117,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `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 (omitted values fall back to `false`; setup bootstrap supplies an explicit `true`), database-retry config resolution and `DATABASE_RETRY_CONFIG` `OnceLock` initialization, default-discovered invalid-file degradation consumed by startup, setup, and 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. +- Setup keeps behavior selection at the interactive dispatch boundary: after target and optional-workflow selection, the `inquire` prompter independently returns the auto-sync and attribution booleans, while non-interactive dispatch returns no behavior selections. The setup persistence seam writes those values only when supplied, preserving existing config omissions and leaving runtime resolver precedence and hook execution semantics in their existing service owners. - `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. diff --git a/context/cli/agent-trace-auto-sync.md b/context/cli/agent-trace-auto-sync.md index d7651765b..d7378b0b4 100644 --- a/context/cli/agent-trace-auto-sync.md +++ b/context/cli/agent-trace-auto-sync.md @@ -11,9 +11,21 @@ explicitly; a config layer that omits the setting remains disabled at runtime. `agent_trace.auto_sync` is a config-file-only boolean resolved through the normal global-then-local config merge. The runtime fallback is `false`, while `sce setup` -writes an explicit `true` when it creates a missing repo-local `.sce/config.json`. -`sce config show` reports the resolved value and its source. Set it explicitly to -`false` to opt out. There is no environment variable or CLI flag for this setting. +writes an explicit `true` when it creates a missing repo-local `.sce/config.json` +and persists the independent interactive answer when a target is selected. A +non-interactive run does not add the key to an existing config. `sce config show` +reports the resolved value and its source. Set it explicitly to `false` to opt out. +There is no environment variable or CLI flag for this setting. + +For an interactive target setup, the selected value is persisted as part of the repo-local config merge: + +```json +{ + "agent_trace": { "auto_sync": true } +} +``` + +Non-interactive setup does not change this key in an existing config. The separate attribution choice is persisted under `policies.attribution_hooks.enabled`; it does not introduce an environment or CLI override for auto-sync. ## Trigger boundary diff --git a/context/cli/cli-command-surface.md b/context/cli/cli-command-surface.md index a842eae3c..5348fe7bb 100644 --- a/context/cli/cli-command-surface.md +++ b/context/cli/cli-command-surface.md @@ -23,6 +23,12 @@ Operator onboarding currently comes from `sce --help`, command-local `--help` ou - Command-local help is available for implemented commands including bare `sce auth`, `sce auth --help`, `sce auth login --help`, `sce setup --help`, `sce doctor --help`, and `sce completion --help`; when stdout color is enabled those help payloads now reuse the shared heading/command/placeholder styling pass while non-TTY and `NO_COLOR` flows stay plain text. Human-readable stderr diagnostics and interactive setup prompt text now follow the same shared styling policy on their respective terminal streams. - Current repository verification guidance for this CLI slice prefers the root Nix entrypoints: `nix flake check` for routine validation, `nix build .#sce` / `nix run .#sce -- --help` for native packaged installability, and targeted `nix develop -c sh -c 'cd cli && '` only when a narrower Rust-only check is explicitly needed. +## Setup behavior selections + +Plain `sce setup` asks, after target and optional-workflow selection, `Enable automatic Agent Trace synchronization? [Y/n]` and `Enable SCE commit attribution trailers? [Y/n]`. The confirmations are independent and default to Yes, so Enter persists `true`; an explicit `n` changes only its corresponding value. Successful target setup records the answers at `agent_trace.auto_sync` and `policies.attribution_hooks.enabled` in repo-local `.sce/config.json` while retaining the existing JSON merge and newline behavior. + +Non-interactive target setup never prompts. A missing config receives explicit `true` values for both behaviors; an existing config's explicit values and omissions remain unchanged by the non-interactive behavior-selection path. The auto-sync resolver remains config-file-only with omitted fallback `false`, and attribution retains its `SCE_ATTRIBUTION_HOOKS_DISABLED` precedence, `SCE_DISABLED` kill switch, and staged-diff AI-overlap gate. See [setup local bootstrap](../sce/setup-repo-local-config-bootstrap.md), [config precedence](config-precedence-contract.md), and [hook routing](../sce/agent-trace-hooks-command-routing.md). + ## Nix installability surface - Root `flake.nix` exposes `packages.sce` and `packages.default = packages.sce` for the **native** development package; the static-musl release binary is the distinct `packages.sce-release` (`apps.sce-release`) output. @@ -58,7 +64,7 @@ Deferred or gated command surfaces currently avoid claiming unimplemented behavi `completion` exposes deterministic shell completion generation via `sce completion --shell `. `setup` defaults to an `inquire` interactive target selection (OpenCode, Claude, Pi, Codex, All) and accepts mutually-exclusive non-interactive target flags (`--opencode`, `--claude`, `--pi`, `--codex`, `--all`); the former `--both` flag was removed in favor of `--all` (opencode+claude+pi+codex); the interactive prompt title and target labels reuse shared prompt styling helpers when stdout color is enabled. Every setup mode first validates the Git repository and effective `agent_trace.repository_remote` (default `origin`) before prompts or writes. If that configured remote is missing, the typed `NotGitRemote { remote_name }` diagnostic identifies the effective name in its explanation and `git remote add` remediation without exposing the URL. `setup` also accepts `--bootstrap-context` as a standalone context-only mode that ensures the durable-context baseline without prompts or integration installs; every normal successful setup path ensures that baseline only after both preflights. `setup` accepts a repeatable `--workflow ` flag selecting which optional workflows to install (currently only `brownfield`). Passing it makes the listed slugs the exact selection for that run; omitting it reuses the persisted `integrations.optional_workflows`, so a repeat run preserves an earlier opt-in. Unknown slugs fail request resolution with a validation error naming the embedded catalog's available slugs and write no files, and `--workflow` is rejected alongside `--bootstrap-context` or on a hooks-only run because neither installs target assets. The resolved selection filters the installed assets and is persisted; see [config precedence contract](config-precedence-contract.md) and [setup local bootstrap](../sce/setup-repo-local-config-bootstrap.md). -An interactive `setup` run instead resolves the selection through an `inquire` multi-select shown after the target prompt, titled `Select optional workflows` with one `{title} — {description}` row per optional workflow using the shared prompt styling. Rows are unchecked when nothing is persisted and pre-checked from `integrations.optional_workflows` otherwise (a supplied `--workflow` list seeds them instead); the answered prompt is the run's exact selection. Cancelling either prompt yields the existing `Setup cancelled. No files were changed.` outcome, a non-TTY run keeps the existing actionable guidance, and the prompt is skipped when the catalog has no optional workflow. +An interactive `setup` run instead resolves the selection through an `inquire` multi-select shown after the target prompt, titled `Select optional workflows` with one `{title} — {description}` row per optional workflow using the shared prompt styling. Rows are unchecked when nothing is persisted and pre-checked from `integrations.optional_workflows` otherwise (a supplied `--workflow` list seeds them instead); the answered prompt is the run's exact selection. It then asks `Enable automatic Agent Trace synchronization? [Y/n]` and `Enable SCE commit attribution trailers? [Y/n]` as independent confirmations, both defaulting to Yes; the two answers are carried independently through setup dispatch. Cancelling any interactive prompt yields the existing `Setup cancelled. No files were changed.` outcome, a non-TTY run keeps the existing actionable guidance, and the optional-workflow prompt is skipped when the catalog has no optional workflow. `auth` now emits auth-local guidance for bare `sce auth` and `sce auth --help`, listing `login`, `logout`, and `whoami` plus copy-ready next steps. `sce auth login` uses the existing token-validation path whenever stored credentials are present: valid credentials are preserved, expired credentials are refreshed, and a failed renewal falls back to device authorization. First login without stored credentials still starts device authorization, and renewal reports remain labeled as `login` in text and JSON output. Authenticated `sce auth whoami` retrieves the authoritative profile from the Control Plane `GET /me` endpoint and renders flat text labels for `Email`, `First Name`, `Last Name`, `Role`, `Permissions`, and `Organization Name`; missing first/last names render empty and missing role, permissions, or workspace values render `none`. `setup`, `doctor`, `hooks`, `policy`, `sync`, `version`, and `completion` all support command-local `--help`/`-h` usage output via top-level parser routing in `cli/src/app.rs`. `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. diff --git a/context/cli/config-precedence-contract.md b/context/cli/config-precedence-contract.md index 1109c5b6d..6d328dcf3 100644 --- a/context/cli/config-precedence-contract.md +++ b/context/cli/config-precedence-contract.md @@ -4,7 +4,7 @@ This contract documents the implemented `sce config` command behavior, runtime resolver, renderer, and canonical Pkl-authored `sce/config.json` schema. The schema is emitted to payload-relative `config/schema/sce-config.schema.json` under Cargo `OUT_DIR` or packaging fallbacks and embedded by `cli/src/services/config/schema.rs` as `SCE_CONFIG_SCHEMA_JSON`; no generated schema is committed. -The current implementation resolves flat logging keys and Agent Trace runtime keys with deterministic precedence and source metadata, exposes resolved-value inspection through `sce config show`, and keeps `sce config validate` focused on validation status plus errors/warnings. File logging is explicitly controlled by the config-file/default `log_to_file` boolean, which defaults to `true`; `log_to_file` and `log_dir` resolve independently, with omitted `log_dir` falling back to the default location. Threshold, format, directory, and `log_file_retention_limit` values are consumed by runtime logging; the concrete logger uses the retention value for primary and v2 creation-triggered cleanup. The config-file-only `agent_trace.auto_sync` value is consumed by the post-commit trigger boundary and by doctor readiness reporting: omitted values resolve to `false`, while `sce setup` writes an explicit `true` for a newly created repo-local config and explicit `false` remains the opt-out. +The current implementation resolves flat logging keys and Agent Trace runtime keys with deterministic precedence and source metadata, exposes resolved-value inspection through `sce config show`, and keeps `sce config validate` focused on validation status plus errors/warnings. File logging is explicitly controlled by the config-file/default `log_to_file` boolean, which defaults to `true`; `log_to_file` and `log_dir` resolve independently, with omitted `log_dir` falling back to the default location. Threshold, format, directory, and `log_file_retention_limit` values are consumed by runtime logging; the concrete logger uses the retention value for primary and v2 creation-triggered cleanup. The config-file-only `agent_trace.auto_sync` value is consumed by the post-commit trigger boundary and by doctor readiness reporting: omitted values resolve to `false`, while `sce setup` writes explicit behavior values for interactive selections and explicit `true` bootstrap defaults for a newly created repo-local config; non-interactive setup does not add omitted behavior keys to an existing config and explicit `false` remains the opt-out. ## Command surface @@ -29,7 +29,7 @@ Agent Trace repository identity keys are also config-file only with per-key `glo - `agent_trace.repository_id` — optional explicit repository identity; resolves as an optional value with no default. - `agent_trace.repository_remote` — Git remote name used to derive repository identity; defaults to `origin` (`DEFAULT_AGENT_TRACE_REPOSITORY_REMOTE` in `cli/src/services/config/resolver.rs`) when no config file sets it. -- `agent_trace.auto_sync` — boolean for the post-commit Agent Trace synchronization trigger; config-file only, with no flag or environment layer. Omitted values resolve to `false`; a newly created repo-local config from `sce setup` explicitly writes `true`, and `false` remains the opt-out. +- `agent_trace.auto_sync` — boolean for the post-commit Agent Trace synchronization trigger; config-file only, with no flag or environment layer. Omitted values resolve to `false`; a newly created repo-local config from `sce setup` explicitly writes `true`, an interactive setup answer can write either value, and `false` remains the opt-out. Resolved observability values that currently have no CLI flag layer follow the same lower-precedence chain without a flag step: @@ -149,6 +149,12 @@ When a default-discovered global or repo-local config file exists but fails JSON - Auth login runtime guidance refers to the resolved source chain generically (`WORKOS_CLIENT_ID`, config file, or baked default for `workos_client_id`) instead of env-only wording. - `control_plane_base_url` resolves through the same shared auth-adjacent key path but has no dedicated auth failure guidance of its own; it is consumed by the Agent Trace control-plane client (`sce sync`). +## Setup-written behavior values + +Interactive setup is the only selection path for these two behavior values. After target selection, it persists the independent confirmation results at `agent_trace.auto_sync` and `policies.attribution_hooks.enabled`; Enter accepts each Yes default, and `n` changes only that key. A non-interactive target run supplies no values, so it preserves both keys when present and preserves their omission when absent. Creating a missing repo-local config is the exception: setup bootstraps both values as explicit `true`. + +These setup writes do not add a resolver layer. `agent_trace.auto_sync` remains config-file-only with global-before-local merge and omitted fallback `false`; attribution remains enabled by default, with `SCE_ATTRIBUTION_HOOKS_DISABLED` overriding `policies.attribution_hooks.enabled`. `SCE_DISABLED`, staged-diff AI-overlap evidence, and all other hook semantics remain owned by the hook runtime. + ## Related files - `config/pkl/base/sce-config-schema.pkl` diff --git a/context/context-map.md b/context/context-map.md index 80dbb8fcd..a80ad9eaa 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -86,6 +86,8 @@ Feature/domain context: - `context/sce/config-schema-publication.md` (current embedded-only publication contract for the generated SCE config JSON Schema and the canonical SCE web application URL used in config declarations) - `context/sce/flatpak-distribution-patterns.md` (source-built Flatpak conventions, including Nix-side ephemeral fallback preparation for the Pkl-free build sandbox, manifest generation, local/release builds, release assets, and host-git bridge) +- Setup behavior selection contract: [local bootstrap](sce/setup-repo-local-config-bootstrap.md) is canonical for the independent interactive confirmations, explicit nested config values, non-interactive existing-config safety, and unchanged runtime gates; see also [config precedence](cli/config-precedence-contract.md), [automatic sync](cli/agent-trace-auto-sync.md), [hook routing](sce/agent-trace-hooks-command-routing.md), and [commit attribution](sce/agent-trace-commit-msg-coauthor-policy.md). + Working areas: - `context/plans/` (active plan execution artifacts, not durable history) diff --git a/context/glossary.md b/context/glossary.md index 3e5b89099..734635b80 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -103,7 +103,7 @@ - `setup target flags`: Mutually-exclusive `sce setup` target selectors (`--opencode`, `--claude`, `--pi`, `--codex`, `--all`) that force non-interactive mode for automation; `--all` expands to opencode+claude+pi+codex and replaced the removed `--both` flag. - `setup mode contract`: `cli/src/services/setup/mod.rs` model where `SetupMode::Interactive` is the default and `SetupMode::NonInteractive(SetupTarget)` is selected only when exactly one target flag is provided. - `setup interactive target prompt`: `inquire::Select` flow in `cli/src/services/setup/mod.rs` (`InquireSetupTargetPrompter`) that presents OpenCode, Claude, Pi, Codex, and All (OpenCode + Claude + Pi + Codex) when `sce setup` runs without target flags. -- `setup dispatch outcome`: Execution model in `cli/src/services/setup/mod.rs` (`SetupDispatch`) where setup either proceeds with a selected/non-interactive target or exits as cancelled without file changes. +- `setup dispatch outcome`: Execution model in `cli/src/services/setup/mod.rs` (`SetupDispatch`) where setup either proceeds with a selected/non-interactive target or exits as cancelled without file changes. Interactive dispatch also carries independent `agent_trace.auto_sync` and `policies.attribution_hooks.enabled` booleans from Yes-default confirmations; non-interactive dispatch carries neither, allowing persistence to preserve existing keys and omissions. - `setup embedded asset manifest`: Compile-time generated file index emitted by `cli/build.rs` into `OUT_DIR/setup_embedded_assets.rs`, embedding bytes from Pkl-generated `OUT_DIR/pkl-generated/config/.{opencode,claude,pi}/**` plus staged `OUT_DIR/static/hooks/**` as deterministic normalized relative-path entries consumed by `cli/src/services/setup/mod.rs`; `OPENCODE_EMBEDDED_ASSETS`, `CLAUDE_EMBEDDED_ASSETS`, and `PI_EMBEDDED_ASSETS` all back live setup targets. The manifest also carries `CODEX_EMBEDDED_ASSETS`, embedding Codex's two Pkl-generated output roots (`config/.agents/**`, `config/.codex/**`) merged by `cli/build.rs` into a build-time-only `OUT_DIR/pkl-generated/config/codex-target/` staging tree so its relative-path entries keep their `.agents/`/`.codex/` prefixes; `SetupTarget::Codex` now backs it as a fourth live setup target via `sce setup --codex`/`--all`, installing directly at the repository root (via `InstallTargetPaths::codex_target_dir()`) since its asset paths already carry their own output-root prefix, unlike the other three targets' single-subdirectory destinations. - `setup required-hook embedded assets`: Setup-service accessors in `cli/src/services/setup/mod.rs` (`iter_required_hook_assets`, `get_required_hook_asset`) that expose canonical embedded templates for `pre-commit`, `commit-msg`, and `post-commit` without runtime config reads. - `SCE managed block`: The CLI-presence check plus `sce hooks ` invocation in each canonical hook template (`cli/assets/hooks/{pre-commit,commit-msg,post-commit}`), delimited by `# >>> sce managed block (do not edit) >>>` / `# <<< sce managed block <<<` comment markers so the same block content can be embedded inside a foreign hook without disturbing content around it (see `context/sce/setup-githooks-hook-asset-packaging.md`). The block propagates an available `sce` command's exit status by capturing `$?` and calling `exit` explicitly rather than by `exec`, so it terminates the script deterministically even when appended after other content. A pure merge module computes hook install bytes against this marker pair (see `setup hook-merge seam`); both `sce setup --hooks` install (see `setup required-hook install orchestration`) and `sce doctor` hook inspection decide currency against this marker pair rather than whole-file byte comparison, so a hook a repository has extended around the block still reports current. @@ -247,4 +247,4 @@ - `parts table (Agent Trace DB)`: Agent Trace DB table created by migration `009_create_parts.sql`; stores append-only message parts with columns `type` (typed by Rust as `text`/`reasoning`/`patch`/`question` and stored as unconstrained `TEXT NOT NULL`), `text`, `message_id`, `session_id`, `generated_at_unix_ms`, `created_at`, `updated_at`. Uses only the internal `id` for row identity (no upsert/dedup). Multiple parts can exist for the same `(session_id, message_id)`. A compound index on `(session_id, message_id, generated_at_unix_ms, id)` enables ordered joins. No foreign keys to `messages` or any other table, so parts may be inserted before their parent message exists. - `AgentTraceExportReader`: Read-only incremental export reader in `cli/src/services/agent_trace_export/mod.rs` over one `RepositoryAgentTraceDb`, exposing `read_messages_after`/`read_parts_after`/`read_diff_traces_after`/`read_agent_traces_after`, each `(cursor: i64, limit: usize) -> Result>` over `WHERE id > cursor ORDER BY id ASC LIMIT {limit}`. Holds no local cursor, performs no mutation, makes no network calls, and returns owned camelCase `serde::Serialize` export-row DTOs matching the shipped control-plane ingestion contract. See `context/sce/agent-trace-export-readers.md`. - `context synchronization lifecycle`: Durable task-level state for synchronization after successful `/next-task` execution. The task record is `pending`, `synced`, or `blocked`; blocked records carry a blocker, required action, and retry condition. Missing lifecycle state on a completed task is unresolved debt, not evidence of synchronization. `/validate` does not persist a plan-level synchronization lifecycle. See `context/sce/shared-context-code-workflow.md`. -- `agent_trace.auto_sync`: Config-file-only boolean resolved by the shared config layers for the post-commit Agent Trace synchronization boundary; omitted values are `false`, while setup writes an explicit `true` into a newly created repo-local config, and explicit `false` opts out. `sce config show` reports the winning source. After validation and repository-DB persistence, enabled post-commit runs launch the existing sync-owned `sync --format json` command once through the current executable with detached null-standard-stream child semantics and repository-root working directory; the hook never waits, has no daemon or high-frequency trigger, and treats launcher failures as fail-open. See [Agent Trace hook routing](sce/agent-trace-hooks-command-routing.md). +- `agent_trace.auto_sync`: Config-file-only boolean resolved by the shared config layers for the post-commit Agent Trace synchronization boundary; omitted values are `false`, a newly created setup config contains explicit `true`, and an interactive setup answer can persist either value. Non-interactive setup does not add the key to an existing config, and explicit `false` opts out. `sce config show` reports the winning source. After validation and repository-DB persistence, enabled post-commit runs launch the existing sync-owned `sync --format json` command once through the current executable with detached null-standard-stream child semantics and repository-root working directory; the hook never waits, has no daemon or high-frequency trigger, and treats launcher failures as fail-open. See [Agent Trace hook routing](sce/agent-trace-hooks-command-routing.md). diff --git a/context/overview.md b/context/overview.md index 37e8bccef..b49e06052 100644 --- a/context/overview.md +++ b/context/overview.md @@ -14,6 +14,7 @@ The generated `/next-task` workflow persists task-level context-synchronization - **Observability:** config-resolved logging with tracing, explicit config-file/default `log_to_file` control, error-specific stderr suppression when file logging is enabled, and optional dated/session-partitioned `log_dir` / `SCE_LOG_DIR` files with retention (see `context/sce/cli-observability-contract.md`). - **Config precedence:** `flags > env > config file > defaults` (see `context/cli/config-precedence-contract.md`); invalid default-discovered config remains degradable for ordinary startup, setup, and Agent Trace storage, while explicit `--config` and `SCE_CONFIG_FILE` failures remain fatal. Setup preserves invalid repo-local files and may omit target/optional-workflow persistence for that run. The config-file-only `agent_trace.auto_sync` setting resolves omitted values to `false`, while setup explicitly writes `true` into a newly created repo-local config; source metadata feeds the post-commit trigger and doctor readiness boundaries. Its asynchronous post-commit behavior and doctor capability reporting are documented in `context/cli/agent-trace-auto-sync.md`. - **Attribution hooks:** enabled by default, gated by staged-diff AI-overlap preflight; `SCE_ATTRIBUTION_HOOKS_DISABLED` opt-out (see `context/sce/agent-trace-commit-msg-coauthor-policy.md`). +- **Setup behavior choices:** interactive setup asks independent Yes-default confirmations for post-commit Agent Trace auto-sync and commit-attribution trailers, persists the answers under `agent_trace.auto_sync` and `policies.attribution_hooks.enabled`, and leaves existing behavior keys/omissions untouched during non-interactive setup. The runtime resolver and hook contracts remain authoritative (see `context/sce/setup-repo-local-config-bootstrap.md`). - **Install channels:** repo-flake Nix, Cargo, npm, and source-built Flatpak (`dev.crocoder.sce`); Homebrew deferred (see `context/sce/cli-first-install-channels-contract.md`). The CLI crate currently depends on `anyhow`, `chrono`, `clap`, `clap_complete`, `dirs`, `hmac`, `indicatif`, `inquire`, `jsonschema`, `keyring-core`, `murmur3`, `owo-colors`, `rand`, `reqwest`, `serde`, `serde_json`, `sha2`, `tokio`, `tracing`, `turso`, and `uuid`, with target-specific keyring backend dependencies for Linux/FreeBSD, macOS, and Windows. No CLI dev-dependencies are currently declared. diff --git a/context/patterns.md b/context/patterns.md index 591661368..38e00d6df 100644 --- a/context/patterns.md +++ b/context/patterns.md @@ -138,6 +138,7 @@ - For interactive setup flows, isolate prompt handling behind a service-layer prompter seam so selection mapping and cancellation behavior can be tested without a live TTY. - When setup or path-catalog modules grow dense, extract focused internal support seams (for example install-flow, prompt-flow, or root-resolution helpers) before adding new behavior so orchestration files stay navigable without changing command contracts. - Treat setup prompt cancellation/interrupt as a non-destructive exit path with explicit user messaging (no file mutations and no partial side effects). +- For setup-controlled runtime behaviors, ask independent Yes-default confirmations only in interactive target setup, persist each answer under its existing config key, and pass no new selection from non-interactive setup. A missing config receives explicit `true` bootstrap values; an existing config retains behavior values and omissions, while runtime precedence and hook gates remain unchanged. - For repository setup-asset build prep, declare canonical generator inputs in `config/pkl/generator-inputs.txt` and route input discovery, two-pass Pkl evaluation, determinism comparison, payload/input inventory creation, in-flight input checks, atomic publication, and private staging cleanup through `scripts/produce-cli-generated-input.sh`. The Cargo wrapper, generated-output check, package-fallback helper, and Nix `cliGeneratedInput` derivation must consume that producer rather than implement those mechanics independently. Keep each consumer's domain checks separate: the generated-output check owns metadata/contract/negative/path assertions; packaging owns static hook/schema/migration staging and the combined Pkl-plus-static checksum inventory; Nix owns declarative producer/input source selection and pre-Cargo handoff wiring. Route build, run, targeted-test, Clippy, and local-install Cargo workflows through `scripts/run-cli-cargo.sh`, which passes the producer handoff through `SCE_CLI_GENERATED_INPUT_DIR` and owns cleanup around Cargo. Keep `cli/build.rs` free of Pkl subprocesses and source-tree generated mirrors. - For CLI database migration prep, keep SQL files under immediate `cli/migrations//` directories named `NNN_description.sql`; `cli/build.rs` stages those files under `OUT_DIR/static/migrations`, sorts by the numeric prefix before `_`, and writes deterministic `OUT_DIR/generated_migrations.rs` constants with `include_str!` references for service `DbSpec` consumers. - For setup install execution, write each selected embedded asset into its own staging file next to its final destination, then swap the staged content into place by renaming it directly over the destination — never unlink the destination first, since `fs::rename` already replaces an existing file atomically; never remove or recreate the integration target directory as a whole. On swap failure, clean the failing asset's staging path and return deterministic recovery guidance naming that asset's destination (recover from version control); the pre-existing destination content, if any, is untouched. No backup artifacts are created. After the install loop, prune stale SCE-owned paths by diffing the full embedded catalog for the target against the assets actually installed, deleting each catalog path not installed, then removing any parent directory left empty by that deletion (a directory still holding a user file fails to remove and survives). diff --git a/context/plans/interactive-setup-behavior-prompts.md b/context/plans/interactive-setup-behavior-prompts.md new file mode 100644 index 000000000..12985134d --- /dev/null +++ b/context/plans/interactive-setup-behavior-prompts.md @@ -0,0 +1,257 @@ +# Plan: interactive-setup-behavior-prompts + +## Change summary + +Extend the existing interactive `sce setup` prompt/dispatch seam with two +independent confirmation questions for the user-facing Agent Trace behaviors: +automatic post-commit synchronization and SCE commit-attribution trailers. +Both confirmations use the existing `inquire` prompt boundary, display the +requested `[Y/n]` defaults, and treat Enter as `true`; explicit `n` produces +`false`. + +Carry the selected values through setup and persist them as explicit nested +values in repo-local `.sce/config.json`, while retaining the current JSON merge, +formatting, and create-if-missing behavior. Non-interactive setup remains +prompt-free and deterministic: a newly created config explicitly enables both +behaviors, while an existing config is not rewritten merely because setup is +non-interactive. The existing config-only auto-sync resolver, attribution +environment/config precedence, `SCE_DISABLED` handling, post-commit launcher, +and canonical trailer semantics remain unchanged; setup only supplies the +selected persisted configuration. + +## Acceptance criteria + +How this plan is proven complete. Each criterion is observable and names the +check that proves it. `/validate` runs these checks; no task in the stack +performs final validation. + +- [x] AC1: Interactive setup asks two separate questions with the exact labels + `Enable automatic Agent Trace synchronization? [Y/n]` and + `Enable SCE commit attribution trailers? [Y/n]`; Enter selects `true` for + each independently, and `n` selects `false` for only the corresponding + behavior. + - Validate: setup prompter/dispatch tests using the test prompter seam cover + both default-Yes answers and each independent explicit-No combination; + prompt construction inspection confirms the exact labels and default. +- [x] AC2: A completed setup persists the selected values at + `agent_trace.auto_sync` and `policies.attribution_hooks.enabled`, preserving + unrelated config keys and the existing pretty-JSON/newline merge behavior. + - Validate: setup persistence tests assert the nested JSON values for true and + false selections and assert existing keys/formatting and both unrelated + behavior values are preserved according to the existing-config policy. +- [x] AC3: Non-interactive setup never waits for prompts; a newly created + repo-local config explicitly contains both behavior values as `true`, while + an existing config's explicitly configured values and omitted keys remain + untouched. The persisted auto-sync value controls the existing post-commit + launch gate, and the persisted attribution value controls the existing + canonical trailer gate without changing either hook's other semantics. + - Validate: setup tests cover newly created and existing-config + non-interactive runs; focused hook tests cover auto-sync enabled/disabled and + attribution enabled/disabled paths using the existing injected seams. +- [x] AC4: Resolver behavior remains unchanged: `agent_trace.auto_sync` is + config-file-only with its existing omitted-value fallback and global-before- + local merge, while attribution enablement retains its existing + `SCE_ATTRIBUTION_HOOKS_DISABLED` over config precedence, default, and + `SCE_DISABLED` interaction. + - Validate: focused `config::` tests cover omitted, explicit true/false, + global/local, environment-over-config, and `SCE_DISABLED`-compatible hook + gate behavior for both properties. +- [x] AC5: User-facing setup documentation and durable SCE context explain the + two interactive choices, explicit persisted config shape, Enter-as-Yes + behavior, safe existing-config handling, and unchanged runtime precedence and + hook semantics; no generated artifact is edited manually. + - Validate: manual review of the updated README/context against the setup, + resolver, and hook implementations; `nix flake check` confirms generated + surfaces remain valid. + +### Full validation + +Repository-wide checks `/validate` runs after the last task, regardless of +which criterion they map to. + +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` +- `nix flake check` + +### Context sync + +- `README.md` +- `context/overview.md` +- `context/architecture.md` +- `context/patterns.md` +- `context/glossary.md` +- `context/context-map.md` +- `context/cli/cli-command-surface.md` +- `context/cli/config-precedence-contract.md` +- `context/cli/agent-trace-auto-sync.md` +- `context/sce/setup-repo-local-config-bootstrap.md` +- `context/sce/agent-trace-hooks-command-routing.md` +- `context/sce/agent-trace-commit-msg-coauthor-policy.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:** the existing setup prompt/profiler seam and interactive dispatch; + setup behavior-selection data flow; repo-local config persistence and the + create-if-missing bootstrap payload; focused setup, resolver, and existing + hook-gate regression tests; user-facing setup documentation and the listed + durable context. +- **Out of scope:** changing the JSON schema shape or generated artifacts, + adding CLI flags or environment variables, changing config precedence or + fallback values, changing post-commit synchronization/trailer/hook execution + semantics, changing the sync protocol/database, or redesigning setup target + and optional-workflow selection. +- **Constraints:** preserve exact nested keys, existing pretty JSON formatting + and trailing newline, existing unrelated config keys, target/optional-workflow + merge behavior, cancellation/non-TTY behavior, and the current Nix-based + validation commands. Use the existing `inquire` dependency and service-layer + prompter seam; do not edit generated outputs manually. +- **Non-goal:** do not make setup a new runtime policy resolver or add a second + control path for automatic sync or attribution. + +## Assumptions + +- An interactive answer is an intentional operator selection, including Enter + accepting the required Yes default; it may replace an existing explicit + behavior value. Non-interactive setup has no such new selection and therefore + preserves an existing config file's explicit values and omissions, following + the current create-if-missing/additive setup convention. +- The existing setup flow continues to run repository preflights and resolve + prompts before side effects; the two behavior confirmations follow the target + and optional-workflow selection and share its existing cancellation handling. +- Existing config files that are invalid remain byte-preserved under the current + degraded setup behavior, so behavior persistence is skipped for that run + rather than attempting to repair or rewrite the file. +- The existing hook tests and injected post-commit/commit-msg seams are the + appropriate proof that persisted values control runtime behavior; no new + integration harness or hook protocol is needed. + +## Task stack + +- [x] T01: `Add independent interactive behavior confirmations to setup dispatch` (status:complete) + - Task ID: T01 + - Scope: In — extend the service-layer setup prompter seam and `SetupDispatch` + with the two boolean selections, implement the two exact `inquire` confirm + prompts with default `true`, preserve target/optional-workflow ordering and + cancellation/non-TTY behavior, and add fake-prompter dispatch tests for + default-Yes, each explicit-No, and independence. Out — config writes, + resolver changes, hook runtime changes, and documentation. + - Dependencies: none + - Done when: interactive dispatch asks both prompts exactly once after the + existing setup selections, carries both independent booleans, Enter/default + maps to true, explicit false maps only its own field, and cancellation still + returns the existing non-destructive outcome. + - Verify: targeted setup tests through `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup`. + - Completed: 2026-09-09 + - Files changed: + - `cli/src/services/setup/command.rs` + - `cli/src/services/setup/mod.rs` + - Result: Added independent interactive confirmations for automatic Agent Trace synchronization and SCE commit-attribution trailers, carried both selections through `SetupDispatch`, preserved cancellation and non-interactive behavior, and added dispatch coverage for defaults, independent explicit-No selections, and cancellation. + - Verify: `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` — passed (74 tests). + - Context impact: local — setup prompt dispatch and its focused tests changed; no durable context files or runtime resolver semantics were changed. + - Context synchronization: synced + +- [x] T02: `Persist setup behavior selections without changing runtime semantics` (status:complete) + - Task ID: T02 + - Scope: In — add the setup behavior-selection config merge using the existing + repo-local JSON persistence conventions; include explicit `true` defaults for + both properties in a newly created config; thread interactive selections into + the successful setup write; leave existing non-interactive config values and + omissions untouched; add setup persistence/bootstrap tests and resolver/hook + regressions proving the selected values reach the existing auto-sync and + attribution gates. Out — schema shape changes, new precedence layers, + changes to launcher/trailer algorithms, and generated artifacts. + - Dependencies: T01 + - Done when: the exact nested config shape is written for selected values, + unrelated keys and existing merge formatting survive, new configs contain + both true values, existing configs are not silently changed by non-interactive + setup, invalid discovered configs remain byte-preserved, and existing resolver + precedence plus post-commit/commit-msg gate semantics pass focused tests. + - Verify: `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup`; `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::`. + - Completed: 2026-09-09 + - Files changed: + - `cli/src/services/setup/command.rs` + - `cli/src/services/setup/mod.rs` + - Result: Threaded interactive behavior selections through setup persistence, added explicit true bootstrap values for both Agent Trace synchronization and attribution hooks, preserved existing non-interactive omissions and invalid-config byte preservation, and added persistence/bootstrap regression coverage. + - Verify: `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` — passed (78 tests); `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` — passed (58 tests). + - Context impact: local — setup persistence/bootstrap and focused regression tests changed; runtime resolver and hook semantics were preserved and no durable context files were changed. + - Context synchronization: synced + +- [x] T03: `Document interactive setup behavior choices and persistence` (status:complete) + - Task ID: T03 + - Scope: In — update the root quick-start/setup documentation and the listed + current-state context files to describe the two prompts, defaults, explicit + nested config values, existing-config safety rule, and unchanged resolver and + hook contracts. Out — application code, tests, generated target/schema + outputs, and historical completed plan/decision records. + - Dependencies: T02 + - Done when: setup documentation accurately shows the two independent + confirmations and config shape, durable context consistently distinguishes + interactive selection from non-interactive bootstrap and preserves the + existing precedence/runtime semantics, and no generated artifact is manually + modified. + - Verify: manual review against the implemented setup/config/hooks code and + the plan's full validation commands. + - Completed: 2026-09-09 + - Files changed: + - `README.md` + - `context/overview.md` + - `context/architecture.md` + - `context/patterns.md` + - `context/glossary.md` + - `context/context-map.md` + - `context/cli/cli-command-surface.md` + - `context/cli/config-precedence-contract.md` + - `context/cli/agent-trace-auto-sync.md` + - `context/sce/setup-repo-local-config-bootstrap.md` + - `context/sce/agent-trace-hooks-command-routing.md` + - `context/sce/agent-trace-commit-msg-coauthor-policy.md` + - Result: Documented the independent interactive setup confirmations, explicit nested persistence shape, Enter-as-Yes defaults, safe non-interactive existing-config behavior, and unchanged resolver and hook contracts across the quick-start and durable context surfaces. + - Verify: manual review against setup/config/hooks implementation — passed; `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` — passed (78 tests); `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` — passed (58 tests); `nix flake check` — passed (all checks passed). + - Context impact: root — user-facing setup behavior and persistence contracts are cross-cutting durable knowledge; updated the listed root, CLI, and SCE context surfaces. + - Context synchronization: synced + +## Open questions + +None. The request fixes the prompt text, default behavior, persisted schema +shape, runtime precedence/non-goals, existing-config safety requirement, test +coverage, and required validation; the remaining choice that Enter is an +intentional interactive answer follows the existing prompt conventions. + +## Validation Report + +**Status:** validated +**Date:** 2026-09-09 + +### Commands run + +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup` -> exit 0 (78 setup tests passed) +- `nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml config::` -> exit 0 (58 config tests passed) +- `nix flake check` -> exit 0 (all checks passed) +- `git diff --check` -> exit 0 (no whitespace errors) + +### Success-criteria verification + +- [x] AC1: Interactive setup asks two exact, independent Yes-default confirmation questions and carries default/explicit-No selections independently -> setup tests passed for both defaults, each independent No combination, both No, and cancellation; prompt construction inspection confirmed the exact labels and `with_default(true)`. +- [x] AC2: Setup persists the selected nested behavior values while preserving unrelated keys and formatting -> setup persistence and bootstrap tests passed, including true/false selections, preserved config content, omissions, and invalid-config byte preservation. +- [x] AC3: Non-interactive setup is prompt-free, bootstraps both values only for a new config, and existing gates retain their semantics -> setup tests passed for new/existing configs; full flake checks passed the injected auto-sync and attribution hook gate regressions. +- [x] AC4: Resolver precedence, omitted fallback, environment overrides, and `SCE_DISABLED` behavior remain unchanged -> config tests passed for omitted/explicit values, global/local precedence, attribution environment precedence, and disabled-hook interactions. +- [x] AC5: Documentation and durable context describe the setup choices, persistence shape, safety behavior, and unchanged runtime contracts -> manual review matched the setup/config/hooks implementation; `nix flake check` passed generated-surface validation. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/sce/agent-trace-commit-msg-coauthor-policy.md b/context/sce/agent-trace-commit-msg-coauthor-policy.md index 076676a2b..0ecc3135d 100644 --- a/context/sce/agent-trace-commit-msg-coauthor-policy.md +++ b/context/sce/agent-trace-commit-msg-coauthor-policy.md @@ -48,3 +48,7 @@ ## Verification evidence - `nix flake check` + +## Setup configuration + +Interactive setup can persist the attribution gate at `policies.attribution_hooks.enabled` through the existing repo-local JSON merge. The confirmation defaults to enabled and is independent from `agent_trace.auto_sync`; non-interactive setup preserves an existing value or omission, while a newly created config explicitly stores `enabled: true`. Runtime precedence and trailer semantics remain unchanged: `SCE_ATTRIBUTION_HOOKS_DISABLED` overrides config, `SCE_DISABLED` still disables the hook, and staged-diff AI-overlap evidence is still required. diff --git a/context/sce/agent-trace-hooks-command-routing.md b/context/sce/agent-trace-hooks-command-routing.md index 41103000c..a26373514 100644 --- a/context/sce/agent-trace-hooks-command-routing.md +++ b/context/sce/agent-trace-hooks-command-routing.md @@ -16,6 +16,10 @@ - `sce hooks codex` - `sce hooks claude-model-state` +## Setup-controlled gates + +Interactive `sce setup` persists the independent confirmations at `agent_trace.auto_sync` and `policies.attribution_hooks.enabled`, using Yes defaults and treating Enter as `true`. Non-interactive setup supplies no behavior selections; creation of a missing config bootstraps both values as `true`, while an existing config's values and omissions remain unchanged. These are inputs to the existing gates below, not a second runtime policy path. + ## Parser and dispatch behavior - `cli/src/app.rs` routes `hooks` through dedicated hook-subcommand parsing. diff --git a/context/sce/setup-repo-local-config-bootstrap.md b/context/sce/setup-repo-local-config-bootstrap.md index ee0325c34..4a5d5b93c 100644 --- a/context/sce/setup-repo-local-config-bootstrap.md +++ b/context/sce/setup-repo-local-config-bootstrap.md @@ -7,8 +7,9 @@ Task `setup-repo-gate-and-local-config-bootstrap` T02, `turso-local-db-sync` T04 ## Behavior - Any successful `sce setup` run in a git-backed repository creates `.sce/config.json` when the file is absent. -- The bootstrap writes the canonical JSON payload with the versioned schema declaration and explicit Agent Trace opt-in: `{"$schema": "https://sce.crocoder.dev/v/config.json", "agent_trace": {"auto_sync": true}}` (where `` is the CLI release version, with a trailing newline). -- If `.sce/config.json` already exists, the bootstrap step returns `Ok(())` immediately and leaves the file untouched — no merge, no reformat, no overwrite. +- Interactive setup asks two independent post-selection confirmations: `Enable automatic Agent Trace synchronization? [Y/n]` and `Enable SCE commit attribution trailers? [Y/n]`. Both default to Yes, and the prompt seam carries each answer separately through setup dispatch so declining one does not change the other. +- The bootstrap writes the canonical JSON payload with the versioned schema declaration and explicit opt-ins for both setup-controlled behaviors: `{"$schema": "https://sce.crocoder.dev/v/config.json", "agent_trace": {"auto_sync": true}, "policies": {"attribution_hooks": {"enabled": true}}}` (where `` is the CLI release version, with a trailing newline). +- If `.sce/config.json` already exists, the bootstrap step returns `Ok(())` immediately and leaves the file untouched — no merge, no reformat, no overwrite. A later interactive target-install write may merge the two answered behavior values, while non-interactive setup leaves existing behavior keys and omissions unchanged. - The parent `.sce/` directory is created via `fs::create_dir_all` if missing. - The setup flow also bootstraps the canonical local DB through `LocalDbLifecycle::setup` and the Agent Trace DB through `AgentTraceDbLifecycle::setup`; both use the shared `TursoDb` adapter. - After both repository preflights (`ensure_git_repository` and the effective named-remote URL check), setup consumes the shared resolver's degraded result for an invalid default-discovered repo-local `.sce/config.json`; the file remains untouched and the run continues through prompts, context baseline bootstrap, lifecycle providers, hooks, and target assets. An absent config continues through the normal bootstrap path, while explicit config selections remain fatal. @@ -47,9 +48,16 @@ The same write also records the run's resolved optional-workflow selection under - The persisted set is repository-wide, not per target: a `--all` run records one selection covering `.opencode/`, `.claude/`, and `.pi/`. - Unknown slugs are rejected during request resolution, before any file or config write. +## Interactive behavior persistence + +- The two interactive confirmations are persisted with the successful target-install write at `agent_trace.auto_sync` and `policies.attribution_hooks.enabled`. Enter accepts each Yes default; an explicit No changes only its corresponding value. +- Non-interactive setup supplies no behavior selections. A newly created config therefore retains both explicit `true` bootstrap values, while an existing config's explicit values and omitted keys remain untouched. +- The write preserves unrelated top-level and nested behavior keys, existing integration merge behavior, pretty JSON formatting, and the trailing newline. Invalid default-discovered repo-local config remains byte-preserved and skips this persistence, as it does for integration selections. +- These setup values only configure the existing runtime gates: auto-sync remains a config-file-only post-commit launch choice, and attribution retains its environment/config precedence, `SCE_DISABLED` handling, overlap evidence requirement, and canonical trailer behavior. + ## Implementation -- `cli/src/services/setup/mod.rs` exports `bootstrap_repo_local_config(repository_root: &Path) -> Result<()>`, `bootstrap_context_baseline(repository_root: &Path) -> Result`, and `persist_integration_targets(repository_root: &Path, target: SetupTarget, selected_optional_workflows: &[String]) -> Result<()>`, which writes both `integrations.target` and `integrations.optional_workflows`. `run_setup_for_mode` resolves the selection (the selection handed to it, else the persisted value read through the exported `persisted_optional_workflows`, which parses the repo-local file via `parse_file_config`) before installing and persisting it. `cli/src/services/setup/command.rs` resolves the repository root before any prompt so it can seed the interactive prompt from that persisted value, and passes the prompted selection — when the run was interactive — to `run_setup_for_mode` ahead of the request's `--workflow` list. +- `cli/src/services/setup/mod.rs` exports `bootstrap_repo_local_config(repository_root: &Path) -> Result<()>`, `bootstrap_context_baseline(repository_root: &Path) -> Result`, and `persist_integration_targets(...) -> Result<()>`, which writes integration selections and, when supplied by interactive setup, the two behavior values. `run_setup_for_mode` resolves the selection (the selection handed to it, else the persisted value read through the exported `persisted_optional_workflows`, which parses the repo-local file via `parse_file_config`) before installing and persisting it. `cli/src/services/setup/command.rs` resolves the repository root before any prompt so it can seed the interactive prompt from that persisted value, and passes the prompted selection and behavior values — when the run was interactive — to `run_setup_for_mode` ahead of the request's `--workflow` list. - `cli/src/services/local_db/lifecycle.rs` implements `LocalDbLifecycle::setup()` for local DB initialization. - `cli/src/services/agent_trace_db/lifecycle.rs` implements `AgentTraceDbLifecycle::setup()` for Agent Trace DB initialization. - Repo-local config bootstrap uses `RepoPaths::sce_config_file()` and `RepoPaths::sce_dir()`; context baseline bootstrap uses the shared context accessors including `RepoPaths::context_tmp_gitignore_file()`. @@ -62,6 +70,6 @@ The same write also records the run's resolved optional-workflow selection under - Default-discovered invalid repo-local config is degradable and never rewritten; explicit `--config` / `SCE_CONFIG_FILE` selections remain fatal, while absent local config remains create-if-missing. - Context baseline bootstrap is independent of config/DB/hooks install and runs before those steps on normal setup paths. - Local bootstrap (repo config + local DB init) is independent of config install and hook install; it runs before both after context baseline bootstrap. -- The bootstrap payload matches the `$schema` declaration accepted by startup config loading and the Pkl-authored JSON Schema embedded from Cargo `OUT_DIR`; its explicit `agent_trace.auto_sync: true` is distinct from the runtime resolver's `false` fallback for omitted values. +- The bootstrap payload matches the `$schema` declaration accepted by startup config loading and the Pkl-authored JSON Schema embedded from Cargo `OUT_DIR`; its explicit `agent_trace.auto_sync: true` and `policies.attribution_hooks.enabled: true` values are distinct from the runtime resolver fallbacks for omitted values. See also [the degraded discovered-config boundary decision](../decisions/2026-09-04-setup-storage-degrade-invalid-discovered-config.md) and the superseded [fail-closed boundary decision](../decisions/2026-08-26-setup-storage-fail-closed-on-invalid-config.md).