From 4f708e50e41241fb75554a3bb560c8ec61afc267 Mon Sep 17 00:00:00 2001 From: David Abram Date: Tue, 11 Aug 2026 13:23:30 +0200 Subject: [PATCH 1/2] config: Update baked WorkOS client ID The previous placeholder was not a usable WorkOS client identifier. Use the deployed client ID as the fallback when environment and config-file values are absent, and expose that value in the configuration contract and glossary. Co-authored-by: SCE --- cli/src/services/config/resolver.rs | 2 +- context/cli/config-precedence-contract.md | 2 +- context/glossary.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/services/config/resolver.rs b/cli/src/services/config/resolver.rs index 05c07b57..5ffc5298 100644 --- a/cli/src/services/config/resolver.rs +++ b/cli/src/services/config/resolver.rs @@ -26,7 +26,7 @@ const DEFAULT_TIMEOUT_MS: u64 = 30000; pub(crate) const DEFAULT_AGENT_TRACE_REPOSITORY_REMOTE: &str = "origin"; pub(crate) const PRECEDENCE_DESCRIPTION: &str = "flags > env > config file > defaults"; const WORKOS_CLIENT_ID_ENV: &str = "WORKOS_CLIENT_ID"; -const WORKOS_CLIENT_ID_BAKED_DEFAULT: &str = "client_sce_default"; +const WORKOS_CLIENT_ID_BAKED_DEFAULT: &str = "client_01KZE4DDA8HM1JHZGF2QCF49RP"; pub(crate) const WORKOS_CLIENT_ID_KEY: AuthConfigKeySpec = AuthConfigKeySpec { config_key: "workos_client_id", diff --git a/context/cli/config-precedence-contract.md b/context/cli/config-precedence-contract.md index a8136adf..b1ce7c6b 100644 --- a/context/cli/config-precedence-contract.md +++ b/context/cli/config-precedence-contract.md @@ -46,7 +46,7 @@ Supported auth-adjacent runtime keys can participate in one shared key-declared - `workos_client_id`, which resolves as: 1. environment value (`WORKOS_CLIENT_ID`) 2. config file value (`workos_client_id`) - 3. baked default (`client_sce_default`) + 3. baked default (`client_01KZE4DDA8HM1JHZGF2QCF49RP`) - `control_plane_base_url`, the base URL of the control-plane Agent Trace ingestion API, which resolves as: 1. environment value (`SCE_CONTROL_PLANE_BASE_URL`) 2. config file value (`control_plane_base_url`) diff --git a/context/glossary.md b/context/glossary.md index ad0ca0b4..a98d3a05 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -156,7 +156,7 @@ - `Rust bash-policy evaluator seam`: CLI-agnostic evaluator in `cli/src/services/bash_policy.rs` for the active Claude/OpenCode Rust-hook migration. It reuses the embedded preset catalog exposed by `cli/src/services/config/policy.rs`, parses shell segments, unwraps supported env/shell/nix wrappers, applies longest-prefix/custom-over-preset precedence, and formats the canonical SCE denial message. The evaluator is exposed through the hidden `sce policy bash` command adapter for hook callers. - `sce policy command adapter`: Hidden/internal `sce policy bash` command in `cli/src/services/bash_policy.rs` that exposes the Rust bash-policy evaluator to hook callers. It reads JSON from STDIN, resolves bash-policy config from the project root (git root with current-directory fallback), evaluates the command against active policies, and emits hook-safe output: Claude Code deny JSON (`hookSpecificOutput` with `permissionDecision: "deny"`) or empty string for allowed commands in `--output claude-hook` mode (default), and structured `{"status","decision","command","normalized_argv","reason","policy_id"}` JSON in `--output json` mode. Input modes are `--input claude-pre-tool-use` (default, parses Claude `PreToolUse` event JSON with `tool_name`/`tool_input.command`) and `--input normalized` (parses `{"command":...}` for OpenCode delegation). The command uses explicit `--input`/`--output` flags rather than auto-detection; Claude Code hooks invoke `sce policy bash` with defaults, while OpenCode plugin delegation passes `--input normalized --output json`. Invalid invocation/input returns deterministic validation diagnostics without executing target commands. - `bash policy redundancy warning`: Non-fatal config validation output emitted when `forbid-git-all` and `forbid-git-commit` are enabled together; the config remains valid, but `sce config show|validate` reports the overlap deterministically as a warning instead of an error. -- `auth config baked default`: Optional key-declared fallback in `cli/src/services/config/mod.rs` (with schema/parsing in `schema.rs`) used only after env and config-file inputs are absent; the first implemented case is `workos_client_id`, which currently falls back to `client_sce_default`. +- `auth config baked default`: Optional key-declared fallback in `cli/src/services/config/mod.rs` (with schema/parsing in `schema.rs`) used only after env and config-file inputs are absent; the first implemented case is `workos_client_id`, which currently falls back to `client_01KZE4DDA8HM1JHZGF2QCF49RP`. - `setup install engine`: Installer in `cli/src/services/setup/mod.rs` (`install_embedded_setup_assets`) that writes embedded setup assets into per-target staging directories and swaps them into repository-root `.opencode/`/`.claude/` destinations, using a unified remove-and-replace policy that removes existing targets before swapping staged content. - `setup remove-and-replace`: Replacement choreography in `cli/src/services/setup/mod.rs` where existing install targets are removed before staged content is promoted; on swap failure, the engine cleans temporary staging paths and returns deterministic recovery guidance (recover from version control). No backup artifacts are created. - `hooks command routing contract`: Current hook command parser/dispatcher plus runtime wiring in `cli/src/services/hooks/mod.rs` (`HookSubcommand`, `run_hooks_subcommand`) supports `pre-commit`, `commit-msg `, `post-commit`, `post-rewrite `, `diff-trace`, and `conversation-trace` with deterministic invocation validation/usage errors; `session-model` is no longer supported. `commit-msg` is the only active attribution path behind the attribution hooks gate and staged-diff AI-overlap preflight; `pre-commit`/`post-rewrite` are no-ops; `post-commit` persists intersections and built Agent Trace payloads; `diff-trace` persists DB-only AgentTraceDb rows using tool-prefixed `session_id` values plus direct `model_id` and `tool_version` values (session-model fallback was removed in the `remove-session-models-direct-claude-model-id` plan). From 53c6cd0b7a2818c8e52ed0bce11cca831f5c2b06 Mon Sep 17 00:00:00 2001 From: David Abram Date: Tue, 11 Aug 2026 13:56:41 +0200 Subject: [PATCH 2/2] repo: Remove brownfield workflow and separate sync URL ownership The brownfield workflow is no longer part of the configured surface, so remove its Claude, OpenCode, and Pi command and skill assets and clear its optional registration. Agent Trace sync now uses the dedicated control-plane baked default https://sce.crocoderlab.dev, while the config schema and SCE web URLs remain on https://sce.crocoder.dev. Existing environment and config overrides, ingestion routes, and authentication behavior remain unchanged. Plan: separate-config-schema-and-control-plane-urls Task: T01 Co-authored-by: SCE --- .claude/commands/brownfield.md | 9 - .claude/skills/sce-brownfield/SKILL.md | 334 ------------------ .../sce-brownfield/references/output.md | 134 ------- .opencode/command/brownfield.md | 12 - .opencode/skills/sce-brownfield/SKILL.md | 334 ------------------ .../sce-brownfield/references/output.md | 134 ------- .pi/prompts/brownfield.md | 8 - .pi/skills/sce-brownfield/SKILL.md | 333 ----------------- .../sce-brownfield/references/output.md | 134 ------- .sce/config.json | 4 +- cli/src/services/config/resolver.rs | 2 +- context/architecture.md | 4 +- context/cli/agent-trace-sync-command.md | 4 +- context/cli/config-precedence-contract.md | 4 +- context/cli/trace-command.md | 2 +- context/context-map.md | 1 + ...separate-control-plane-and-sce-web-urls.md | 50 +++ context/glossary.md | 4 +- context/overview.md | 4 +- ...te-config-schema-and-control-plane-urls.md | 93 +++++ 20 files changed, 158 insertions(+), 1446 deletions(-) delete mode 100644 .claude/commands/brownfield.md delete mode 100644 .claude/skills/sce-brownfield/SKILL.md delete mode 100644 .claude/skills/sce-brownfield/references/output.md delete mode 100644 .opencode/command/brownfield.md delete mode 100644 .opencode/skills/sce-brownfield/SKILL.md delete mode 100644 .opencode/skills/sce-brownfield/references/output.md delete mode 100644 .pi/prompts/brownfield.md delete mode 100644 .pi/skills/sce-brownfield/SKILL.md delete mode 100644 .pi/skills/sce-brownfield/references/output.md create mode 100644 context/decisions/2026-08-11-separate-control-plane-and-sce-web-urls.md create mode 100644 context/plans/separate-config-schema-and-control-plane-urls.md diff --git a/.claude/commands/brownfield.md b/.claude/commands/brownfield.md deleted file mode 100644 index 68dcef63..00000000 --- a/.claude/commands/brownfield.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -description: "Reconstruct durable context from an existing repository's own evidence" -argument-hint: "[rebuild] [path ...]" -allowed-tools: Read, Glob, Grep, Edit, Write, Question, Skill, Bash ---- - -Invoke the `sce-brownfield` skill exactly once with `$ARGUMENTS`. -The skill owns the complete workflow, including all waits and same-session resume -behavior. Do not invoke any phase skill or sequence workflow steps in this command. diff --git a/.claude/skills/sce-brownfield/SKILL.md b/.claude/skills/sce-brownfield/SKILL.md deleted file mode 100644 index d82518da..00000000 --- a/.claude/skills/sce-brownfield/SKILL.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -name: sce-brownfield -description: > - Reconstruct durable context from an existing repository's own evidence -compatibility: claude ---- - -# SCE Brownfield - -## Purpose - -Own this workflow from input parsing through its terminal user-visible response. -Execute the phases below directly and in order. Phase statuses are internal state, -not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or -workflow command. Follow the canonical workflow's steps, gates, -and stops exactly as written: never invent, skip, reorder, or merge a step. - -## User-visible output - -Use `references/output.md` for every gate and terminal response. Render no raw -internal state. The reference contains only human-visible Markdown layouts. -User-visible output is limited to those layouts: never invent a layout, and never -wrap one in an added preamble, commentary, summary, or extra section. - -## Composite control flow - -Keep phase results as internal state and continue immediately whenever the -canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. - -This workflow reconstructs durable `context/` memory for a repository that has -none, or that has gaps. It is a cold-start and gap-fill tool, not a recurring -context-maintenance or drift-repair command. Ongoing maintenance stays owned by -the task and plan synchronization phases. - -## Input - -`$ARGUMENTS` is `[rebuild] [path ...]`. Parse it into two parts before any -investigation: - -- An optional leading literal token `rebuild`. Its presence, and only its - presence, grants rewrite authority over existing context files. -- Zero or more remaining tokens, each an additional local documentation path. - A path may be a file or a directory, inside or outside the repository. - -Parsing rules: - -- Empty `$ARGUMENTS` is valid and selects additive mode with no extra paths. -- `rebuild` is recognized only as the first token. In any later position it is a - path. -- Never infer `rebuild` from conversation content, repository state, or the - apparent staleness of existing context. Only the literal leading token sets it. -- A remaining token that does not resolve to an existing readable local path is - invalid input. - -When input is invalid, render the **Invalid usage** layout from -`references/output.md` naming the offending token, and stop. Do not guess the -token's meaning, and do not investigate or write anything. - -## Workflow - -### 1. Confirm the context root - -Before any investigation, evidence gathering, or write, check whether `context/` -exists. - -When `context/` does not exist, there is no durable memory to fill. Render the -**Missing context bootstrap gate** layout from `references/output.md` with -`sce setup --bootstrap-context` as the required action, and stop. Read no -repository evidence and write no file. - -Bootstrapping is the user's action, not this workflow's. This workflow never -creates the `context/` root, and never creates a file outside it. - -Wait for the user. When they report the command ran, run this step again and -continue in this session. Do not restart argument parsing. - -When `context/` exists, record which of the baseline files and directories are -present, then continue to the next step. - -### 2. Gather evidence - -Evidence is strictly local. Gather it in the priority order below. When two -sources disagree, the earlier source in this order wins, and the disagreement is -recorded for step 5. - -#### 2.1 Current code first - -Read the repository's current source before anything that describes it. -Establish: - -- Entry points, executables, and published interfaces. -- Module and package boundaries, and the dependency direction between them. -- Data shapes, persistence, and external integrations. -- Error handling, configuration surfaces, and observable behavior. - -Current code is the highest-authority evidence class. Nothing later in this order -overrides it. - -#### 2.2 Tests, schemas, migrations, build and runtime configuration - -Read, when present: - -- Test suites, for intended behavior, invariants, and edge cases the code alone - does not state. -- Schemas and migrations, for the data model and its history. -- Build, packaging, and dependency manifests, for the artifact set and toolchain. -- Runtime and deployment configuration, for environments, services, and - operational behavior. - -This class is executable truth. Treat it as second only to current code. - -#### 2.3 Discover documentation explicitly - -Do not assume a README is the only documentation. Sweep the repository for it: - -- Repository-root Markdown and plain-text documents. -- `docs/`, `doc/`, `documentation/`, `wiki/`, `adr/`, `decisions/`, `rfcs/`, - `design/`, and `notes/` directories, at any depth. -- Per-package or per-module `README`, `CONTRIBUTING`, `ARCHITECTURE`, and - `CHANGELOG` files. -- Comment blocks that document a whole module rather than a single line. -- Issue, pull-request, and agent-instruction files committed to the repository, - such as `AGENTS.md` or equivalent. - -Record what the sweep found and what it did not. Documentation describes intent -and may be stale; it never outranks code. - -#### 2.4 Read argument-supplied paths - -Read every path supplied as an argument, as an additional documentation source -at the same authority as step 2.3. A supplied directory is read recursively for -documentation files. - -A supplied path that exists but yields nothing usable is recorded as a gap, not -a failure. - -#### 2.5 Read at least three months of Git history - -Read no less than three months of history measured back from the current date. - -Use it for: - -- When and why current structure arrived. -- Migrations, renames, deletions, and reversals. -- Recurring risk areas and repeatedly repaired code. - -Read older history only when recent evidence points at a still-relevant -architectural decision, migration, rename, deletion, or recurring risk. Follow -that thread as far back as it stays relevant, and no further. - -History explains change. It is the weakest evidence class for current truth: a -commit message describes an intention at a moment, not the state of the code now. - -#### 2.6 No network access - -This workflow performs no network access. Do not fetch a URL, search the web, -query a package registry, call a remote API, or read any external documentation -source. Evidence is the local repository plus argument-supplied local paths, and -nothing else. - -When local evidence is insufficient, that is a gap or a clarification question. -It is never a reason to go looking outside the repository. - -### 3. Score every important fact - -An important fact is any statement that would be written into `context/` as -durable truth. Maintain an internal ledger with one entry per important fact: -the statement, its supporting evidence, its status, and a confidence score from -`1` to `100`. - -Assign the score from the evidence, not from how plausible the statement sounds: - -- `90`–`100` **Verified** — directly observable in current code or executable - configuration. -- `70`–`89` **Strongly supported** — consistent across two independent evidence - classes, with no contradiction. -- `50`–`69` **Inferred** — supported by one evidence class and contradicted by - none, but not directly observable. -- `1`–`49` **Clarification required** — guessed, supported only by documentation - or history that the code does not confirm, or genuinely ambiguous. - -A fact whose evidence conflicted and was resolved under step 5 is scored after -resolution and carries the status **Contradiction resolved**. - -The ledger is internal state and chat evidence only. No score, and nothing -derived from one, is ever written under `context/`. - -### 4. Block on low-confidence facts - -Any important fact scoring below `50` blocks. It is not written as truth, and -it is not silently downgraded, hedged, or omitted to avoid asking. - -Collect every blocking fact, group the questions by area so the user answers a -coherent set rather than a list of fragments, and render the **Clarification -gate** layout from `references/output.md`. Each question must offer at least two -concrete options drawn from the evidence, plus an explicit freeform answer. - -Stop and wait. Write no context file while waiting. - -When the user answers, apply the answers to the ledger, rescore the affected -facts, and continue in this session at step 5. Do not restart investigation, and -do not re-ask an answered question. - -When a question goes unanswered, its fact is not written. Record it under -**Gaps** in the report. - -### 5. Resolve and disclose contradictions - -A contradiction is material when the conflicting statements would produce -different context. Ignore cosmetic disagreements such as wording or formatting. - -Resolve each material contradiction by evidence priority: current code, then -executable configuration, then documentation, then history. - -Classify each one: - -- **Stale documentation** — documentation describes behavior the code no longer - has. -- **Superseded decision** — history records a decision that later work reversed. -- **Divergent implementations** — two parts of the current code disagree with - each other. -- **Unexplained history** — history and code disagree with no evidence of which - is current. - -Every material contradiction is disclosed in the report with its classification -and the interpretation written to context, whatever its confidence. Never resolve -one silently. A contradiction that cannot be resolved by evidence priority is a -blocking clarification under step 4. - -### 6. Infer the context structure - -Derive the `context/{domain}/` structure from the repository's own boundaries — -its modules, services, deployable units, and data ownership — not from a generic -template. - -- One topic per file. A domain that owns several distinct concerns gets several - focused files rather than one large one. -- Name domains in the repository's own terminology. -- Prefer extending the existing structure over inventing a parallel one. When - `context/` already carries domains, new files join them. -- Plan the root files against what the evidence actually established: - `overview.md`, `architecture.md`, `glossary.md`, `patterns.md`, and - `context-map.md`. - -Record the planned file set before writing it. - -### 7. Write context - -Writes are additive by default. In additive mode: - -- Write only missing files and missing domains. -- Never overwrite, truncate, move, rename, or delete an existing context file. -- When a planned file already exists, leave it untouched and record it as - skipped, even when the existing content looks thinner than what was found. - -When the literal `rebuild` token was supplied, rewrite authority extends to -existing context files. Even then: - -- Rewrite only files this reconstruction has evidence for. -- Never delete a context file. Rewriting is the whole of the granted authority. -- Never touch `context/plans/`, `context/handovers/`, `context/decisions/`, or - `context/tmp/`. Those are owned by other workflows. -- Never modify a context file with uncommitted changes. - -Every write obeys these content rules: - -- Describe current state and resulting behavior, not the investigation that found - it and not a change narrative. -- Never write a confidence score, a commit hash, a timestamp, or a date under - `context/`. -- Never attribute a statement to this workflow, this session, or its evidence - gathering. -- One topic per file, at most 250 lines, relative Markdown links between context - files. -- A Mermaid diagram where structure, boundaries, or flows are complex enough that - prose alone would not carry them; concrete code examples only where they - clarify non-trivial behavior. -- Add a glossary entry for domain language the repository uses and the glossary - does not define. - -Link every created or updated domain file from `context/context-map.md`. When -the map already exists, edit it additively: add the missing entries and leave the -existing ones as they are. - -Never write outside `context/`. Do not modify application code, tests, plans, or -configuration. - -### 8. Audit the result - -After writing, audit what was written. Every item below is blocking: a failure -stops the workflow rather than being noted in passing. - -- No confidence score, commit hash, timestamp, or date appears under `context/`. -- Every created or updated domain file is reachable from - `context/context-map.md`. -- Every written file is at or below 250 lines and covers one topic. -- Every link between context files is relative and resolves. -- No fact scoring below `50` was written as truth. -- No existing context file was overwritten unless `rebuild` was supplied, and - none was deleted in any mode. -- Nothing was written outside `context/`. -- Every material contradiction found in step 5 appears in the report. - -When an audit item fails, repair the written files when the repair is -unambiguous, then rerun the item. When it cannot be repaired without inventing a -fact, render the **Blocked** layout from `references/output.md` naming the failed -item, the files preserved, and the retry condition, and stop. - -### 9. Report - -Render the **Completed report** layout from `references/output.md` with the -mode, the written and untouched files, the fact ledger, the contradictions, the -gaps, and the audit outcome. Stop. - -## Rules - -- Reconstruct context at most once per invocation. -- Never create the `context/` root; `sce setup --bootstrap-context` owns that. -- Never write, move, or delete a file outside `context/`. -- Never delete a context file, in either mode. -- Never overwrite an existing context file unless the literal leading `rebuild` - token was supplied. -- Never infer `rebuild` from anything other than that token. -- Never access the network or read a non-local source. -- Never write a fact scoring below `50` as truth. -- Never resolve a material contradiction without disclosing it. -- Never write a confidence score, hash, timestamp, or date under `context/`. -- Never invoke another skill, sibling package, or workflow command. -- Never synchronize context, validate a plan, select or execute a task, or create - a Git commit. -- Never treat this workflow as recurring context maintenance. diff --git a/.claude/skills/sce-brownfield/references/output.md b/.claude/skills/sce-brownfield/references/output.md deleted file mode 100644 index 43f96d74..00000000 --- a/.claude/skills/sce-brownfield/references/output.md +++ /dev/null @@ -1,134 +0,0 @@ -# Brownfield output layouts - -Use only the applicable layout. Values come from internal workflow state. - -## Invalid usage - -```markdown -------------------------------------- - -# Brownfield: invalid arguments - -`/brownfield` takes an optional leading `rebuild` token followed by any number -of local documentation paths: `/brownfield [rebuild] [path ...]`. - -Problem: {unrecognized token, misplaced `rebuild`, or unreadable path} - -Received: `{$ARGUMENTS}` - -Nothing was investigated and nothing was written. -``` - -## Missing context bootstrap gate - -```markdown -------------------------------------- - -# This repository has no durable context. - -Bootstrap it, then continue in this session: - -`sce setup --bootstrap-context` - -Nothing was investigated and nothing was written. -``` - -## Clarification gate - -```markdown -------------------------------------- - -# Clarification needed. - -No context file was written. - -{count} facts could not be established from repository evidence alone. Answer -each group below. Every question accepts one of its options or a freeform -answer. - -## {group-id} · {area} - -{question} - -- A. {option} -- B. {option} -- C. {option, when one applies} -- Or answer freely. - -Evidence found: {what the repository showed} -Why this blocks: {which context statement cannot be written as truth} -``` - -## Blocked - -Present each blocker's problem, impact, and required action. State what was -investigated, that no context file was written, and the concrete condition -under which `/brownfield` should run again. - -## Contradiction disclosure - -Rendered as a section of the completed report, never on its own. - -```markdown -## Contradictions - -- **{subject}** — {classification: stale documentation | superseded decision | - divergent implementations | unexplained history} - - Code says: {what the code shows} - - Other evidence says: {what documentation or history claims} - - Resolved as: {the interpretation written to context, and why} -``` - -## Completed report - -```markdown -------------------------------------- - -# Brownfield reconstruction complete. - -Mode: {additive | rebuild} - -{written-count} context files written, {skipped-count} left untouched. - -## Written - -- `{context path}` — {what durable truth it now records} - -## Left untouched - -- `{context path}` — already present; additive mode does not overwrite it. - -## Fact ledger - -| Fact | Status | Score | Evidence | -| --- | --- | --- | --- | -| {fact} | {Verified \| Strongly supported \| Inferred \| Contradiction resolved} | {1-100} | {file, test, or commit range that supports it} | - -## Contradictions - -{Render the **Contradiction disclosure** layout here, or `None found.`} - -## Gaps - -- {Area the repository's own evidence could not establish, and what would - resolve it, or `None identified.`} - -## Verification - -- {Which quality-audit checks ran and their outcome.} -``` - -# Report rules - -- Every written path must be exact so the reader can open it directly. -- The fact ledger reports the score the workflow assigned; it is chat output - only and is never written into a context file. -- A fact scored below `50` never appears in the ledger as written truth. It is - either resolved through the clarification gate or listed under **Gaps**. -- **Contradictions** is never omitted. State `None found.` when none were - found; silence reads as none were looked for. -- Never claim a file was written unless the write actually completed. -- Never report a quality-audit check as passed unless it ran. -- Report `Mode: rebuild` only when the literal `rebuild` token was supplied. -- Do not recommend a follow-up workflow command. The reconstruction is the - whole deliverable. diff --git a/.opencode/command/brownfield.md b/.opencode/command/brownfield.md deleted file mode 100644 index b1c0a8f7..00000000 --- a/.opencode/command/brownfield.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: "Reconstruct durable context from an existing repository's own evidence" -argument-hint: "[rebuild] [path ...]" -agent: "Shared Context Code" -entry-skill: "sce-brownfield" -skills: - - "sce-brownfield" ---- - -Invoke the `sce-brownfield` skill exactly once with `$ARGUMENTS`. -The skill owns the complete workflow, including all waits and same-session resume -behavior. Do not invoke any phase skill or sequence workflow steps in this command. diff --git a/.opencode/skills/sce-brownfield/SKILL.md b/.opencode/skills/sce-brownfield/SKILL.md deleted file mode 100644 index 7499d16c..00000000 --- a/.opencode/skills/sce-brownfield/SKILL.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -name: sce-brownfield -description: > - Reconstruct durable context from an existing repository's own evidence -compatibility: opencode ---- - -# SCE Brownfield - -## Purpose - -Own this workflow from input parsing through its terminal user-visible response. -Execute the phases below directly and in order. Phase statuses are internal state, -not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or -workflow command. Follow the canonical workflow's steps, gates, -and stops exactly as written: never invent, skip, reorder, or merge a step. - -## User-visible output - -Use `references/output.md` for every gate and terminal response. Render no raw -internal state. The reference contains only human-visible Markdown layouts. -User-visible output is limited to those layouts: never invent a layout, and never -wrap one in an added preamble, commentary, summary, or extra section. - -## Composite control flow - -Keep phase results as internal state and continue immediately whenever the -canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. - -This workflow reconstructs durable `context/` memory for a repository that has -none, or that has gaps. It is a cold-start and gap-fill tool, not a recurring -context-maintenance or drift-repair command. Ongoing maintenance stays owned by -the task and plan synchronization phases. - -## Input - -`$ARGUMENTS` is `[rebuild] [path ...]`. Parse it into two parts before any -investigation: - -- An optional leading literal token `rebuild`. Its presence, and only its - presence, grants rewrite authority over existing context files. -- Zero or more remaining tokens, each an additional local documentation path. - A path may be a file or a directory, inside or outside the repository. - -Parsing rules: - -- Empty `$ARGUMENTS` is valid and selects additive mode with no extra paths. -- `rebuild` is recognized only as the first token. In any later position it is a - path. -- Never infer `rebuild` from conversation content, repository state, or the - apparent staleness of existing context. Only the literal leading token sets it. -- A remaining token that does not resolve to an existing readable local path is - invalid input. - -When input is invalid, render the **Invalid usage** layout from -`references/output.md` naming the offending token, and stop. Do not guess the -token's meaning, and do not investigate or write anything. - -## Workflow - -### 1. Confirm the context root - -Before any investigation, evidence gathering, or write, check whether `context/` -exists. - -When `context/` does not exist, there is no durable memory to fill. Render the -**Missing context bootstrap gate** layout from `references/output.md` with -`sce setup --bootstrap-context` as the required action, and stop. Read no -repository evidence and write no file. - -Bootstrapping is the user's action, not this workflow's. This workflow never -creates the `context/` root, and never creates a file outside it. - -Wait for the user. When they report the command ran, run this step again and -continue in this session. Do not restart argument parsing. - -When `context/` exists, record which of the baseline files and directories are -present, then continue to the next step. - -### 2. Gather evidence - -Evidence is strictly local. Gather it in the priority order below. When two -sources disagree, the earlier source in this order wins, and the disagreement is -recorded for step 5. - -#### 2.1 Current code first - -Read the repository's current source before anything that describes it. -Establish: - -- Entry points, executables, and published interfaces. -- Module and package boundaries, and the dependency direction between them. -- Data shapes, persistence, and external integrations. -- Error handling, configuration surfaces, and observable behavior. - -Current code is the highest-authority evidence class. Nothing later in this order -overrides it. - -#### 2.2 Tests, schemas, migrations, build and runtime configuration - -Read, when present: - -- Test suites, for intended behavior, invariants, and edge cases the code alone - does not state. -- Schemas and migrations, for the data model and its history. -- Build, packaging, and dependency manifests, for the artifact set and toolchain. -- Runtime and deployment configuration, for environments, services, and - operational behavior. - -This class is executable truth. Treat it as second only to current code. - -#### 2.3 Discover documentation explicitly - -Do not assume a README is the only documentation. Sweep the repository for it: - -- Repository-root Markdown and plain-text documents. -- `docs/`, `doc/`, `documentation/`, `wiki/`, `adr/`, `decisions/`, `rfcs/`, - `design/`, and `notes/` directories, at any depth. -- Per-package or per-module `README`, `CONTRIBUTING`, `ARCHITECTURE`, and - `CHANGELOG` files. -- Comment blocks that document a whole module rather than a single line. -- Issue, pull-request, and agent-instruction files committed to the repository, - such as `AGENTS.md` or equivalent. - -Record what the sweep found and what it did not. Documentation describes intent -and may be stale; it never outranks code. - -#### 2.4 Read argument-supplied paths - -Read every path supplied as an argument, as an additional documentation source -at the same authority as step 2.3. A supplied directory is read recursively for -documentation files. - -A supplied path that exists but yields nothing usable is recorded as a gap, not -a failure. - -#### 2.5 Read at least three months of Git history - -Read no less than three months of history measured back from the current date. - -Use it for: - -- When and why current structure arrived. -- Migrations, renames, deletions, and reversals. -- Recurring risk areas and repeatedly repaired code. - -Read older history only when recent evidence points at a still-relevant -architectural decision, migration, rename, deletion, or recurring risk. Follow -that thread as far back as it stays relevant, and no further. - -History explains change. It is the weakest evidence class for current truth: a -commit message describes an intention at a moment, not the state of the code now. - -#### 2.6 No network access - -This workflow performs no network access. Do not fetch a URL, search the web, -query a package registry, call a remote API, or read any external documentation -source. Evidence is the local repository plus argument-supplied local paths, and -nothing else. - -When local evidence is insufficient, that is a gap or a clarification question. -It is never a reason to go looking outside the repository. - -### 3. Score every important fact - -An important fact is any statement that would be written into `context/` as -durable truth. Maintain an internal ledger with one entry per important fact: -the statement, its supporting evidence, its status, and a confidence score from -`1` to `100`. - -Assign the score from the evidence, not from how plausible the statement sounds: - -- `90`–`100` **Verified** — directly observable in current code or executable - configuration. -- `70`–`89` **Strongly supported** — consistent across two independent evidence - classes, with no contradiction. -- `50`–`69` **Inferred** — supported by one evidence class and contradicted by - none, but not directly observable. -- `1`–`49` **Clarification required** — guessed, supported only by documentation - or history that the code does not confirm, or genuinely ambiguous. - -A fact whose evidence conflicted and was resolved under step 5 is scored after -resolution and carries the status **Contradiction resolved**. - -The ledger is internal state and chat evidence only. No score, and nothing -derived from one, is ever written under `context/`. - -### 4. Block on low-confidence facts - -Any important fact scoring below `50` blocks. It is not written as truth, and -it is not silently downgraded, hedged, or omitted to avoid asking. - -Collect every blocking fact, group the questions by area so the user answers a -coherent set rather than a list of fragments, and render the **Clarification -gate** layout from `references/output.md`. Each question must offer at least two -concrete options drawn from the evidence, plus an explicit freeform answer. - -Stop and wait. Write no context file while waiting. - -When the user answers, apply the answers to the ledger, rescore the affected -facts, and continue in this session at step 5. Do not restart investigation, and -do not re-ask an answered question. - -When a question goes unanswered, its fact is not written. Record it under -**Gaps** in the report. - -### 5. Resolve and disclose contradictions - -A contradiction is material when the conflicting statements would produce -different context. Ignore cosmetic disagreements such as wording or formatting. - -Resolve each material contradiction by evidence priority: current code, then -executable configuration, then documentation, then history. - -Classify each one: - -- **Stale documentation** — documentation describes behavior the code no longer - has. -- **Superseded decision** — history records a decision that later work reversed. -- **Divergent implementations** — two parts of the current code disagree with - each other. -- **Unexplained history** — history and code disagree with no evidence of which - is current. - -Every material contradiction is disclosed in the report with its classification -and the interpretation written to context, whatever its confidence. Never resolve -one silently. A contradiction that cannot be resolved by evidence priority is a -blocking clarification under step 4. - -### 6. Infer the context structure - -Derive the `context/{domain}/` structure from the repository's own boundaries — -its modules, services, deployable units, and data ownership — not from a generic -template. - -- One topic per file. A domain that owns several distinct concerns gets several - focused files rather than one large one. -- Name domains in the repository's own terminology. -- Prefer extending the existing structure over inventing a parallel one. When - `context/` already carries domains, new files join them. -- Plan the root files against what the evidence actually established: - `overview.md`, `architecture.md`, `glossary.md`, `patterns.md`, and - `context-map.md`. - -Record the planned file set before writing it. - -### 7. Write context - -Writes are additive by default. In additive mode: - -- Write only missing files and missing domains. -- Never overwrite, truncate, move, rename, or delete an existing context file. -- When a planned file already exists, leave it untouched and record it as - skipped, even when the existing content looks thinner than what was found. - -When the literal `rebuild` token was supplied, rewrite authority extends to -existing context files. Even then: - -- Rewrite only files this reconstruction has evidence for. -- Never delete a context file. Rewriting is the whole of the granted authority. -- Never touch `context/plans/`, `context/handovers/`, `context/decisions/`, or - `context/tmp/`. Those are owned by other workflows. -- Never modify a context file with uncommitted changes. - -Every write obeys these content rules: - -- Describe current state and resulting behavior, not the investigation that found - it and not a change narrative. -- Never write a confidence score, a commit hash, a timestamp, or a date under - `context/`. -- Never attribute a statement to this workflow, this session, or its evidence - gathering. -- One topic per file, at most 250 lines, relative Markdown links between context - files. -- A Mermaid diagram where structure, boundaries, or flows are complex enough that - prose alone would not carry them; concrete code examples only where they - clarify non-trivial behavior. -- Add a glossary entry for domain language the repository uses and the glossary - does not define. - -Link every created or updated domain file from `context/context-map.md`. When -the map already exists, edit it additively: add the missing entries and leave the -existing ones as they are. - -Never write outside `context/`. Do not modify application code, tests, plans, or -configuration. - -### 8. Audit the result - -After writing, audit what was written. Every item below is blocking: a failure -stops the workflow rather than being noted in passing. - -- No confidence score, commit hash, timestamp, or date appears under `context/`. -- Every created or updated domain file is reachable from - `context/context-map.md`. -- Every written file is at or below 250 lines and covers one topic. -- Every link between context files is relative and resolves. -- No fact scoring below `50` was written as truth. -- No existing context file was overwritten unless `rebuild` was supplied, and - none was deleted in any mode. -- Nothing was written outside `context/`. -- Every material contradiction found in step 5 appears in the report. - -When an audit item fails, repair the written files when the repair is -unambiguous, then rerun the item. When it cannot be repaired without inventing a -fact, render the **Blocked** layout from `references/output.md` naming the failed -item, the files preserved, and the retry condition, and stop. - -### 9. Report - -Render the **Completed report** layout from `references/output.md` with the -mode, the written and untouched files, the fact ledger, the contradictions, the -gaps, and the audit outcome. Stop. - -## Rules - -- Reconstruct context at most once per invocation. -- Never create the `context/` root; `sce setup --bootstrap-context` owns that. -- Never write, move, or delete a file outside `context/`. -- Never delete a context file, in either mode. -- Never overwrite an existing context file unless the literal leading `rebuild` - token was supplied. -- Never infer `rebuild` from anything other than that token. -- Never access the network or read a non-local source. -- Never write a fact scoring below `50` as truth. -- Never resolve a material contradiction without disclosing it. -- Never write a confidence score, hash, timestamp, or date under `context/`. -- Never invoke another skill, sibling package, or workflow command. -- Never synchronize context, validate a plan, select or execute a task, or create - a Git commit. -- Never treat this workflow as recurring context maintenance. diff --git a/.opencode/skills/sce-brownfield/references/output.md b/.opencode/skills/sce-brownfield/references/output.md deleted file mode 100644 index 43f96d74..00000000 --- a/.opencode/skills/sce-brownfield/references/output.md +++ /dev/null @@ -1,134 +0,0 @@ -# Brownfield output layouts - -Use only the applicable layout. Values come from internal workflow state. - -## Invalid usage - -```markdown -------------------------------------- - -# Brownfield: invalid arguments - -`/brownfield` takes an optional leading `rebuild` token followed by any number -of local documentation paths: `/brownfield [rebuild] [path ...]`. - -Problem: {unrecognized token, misplaced `rebuild`, or unreadable path} - -Received: `{$ARGUMENTS}` - -Nothing was investigated and nothing was written. -``` - -## Missing context bootstrap gate - -```markdown -------------------------------------- - -# This repository has no durable context. - -Bootstrap it, then continue in this session: - -`sce setup --bootstrap-context` - -Nothing was investigated and nothing was written. -``` - -## Clarification gate - -```markdown -------------------------------------- - -# Clarification needed. - -No context file was written. - -{count} facts could not be established from repository evidence alone. Answer -each group below. Every question accepts one of its options or a freeform -answer. - -## {group-id} · {area} - -{question} - -- A. {option} -- B. {option} -- C. {option, when one applies} -- Or answer freely. - -Evidence found: {what the repository showed} -Why this blocks: {which context statement cannot be written as truth} -``` - -## Blocked - -Present each blocker's problem, impact, and required action. State what was -investigated, that no context file was written, and the concrete condition -under which `/brownfield` should run again. - -## Contradiction disclosure - -Rendered as a section of the completed report, never on its own. - -```markdown -## Contradictions - -- **{subject}** — {classification: stale documentation | superseded decision | - divergent implementations | unexplained history} - - Code says: {what the code shows} - - Other evidence says: {what documentation or history claims} - - Resolved as: {the interpretation written to context, and why} -``` - -## Completed report - -```markdown -------------------------------------- - -# Brownfield reconstruction complete. - -Mode: {additive | rebuild} - -{written-count} context files written, {skipped-count} left untouched. - -## Written - -- `{context path}` — {what durable truth it now records} - -## Left untouched - -- `{context path}` — already present; additive mode does not overwrite it. - -## Fact ledger - -| Fact | Status | Score | Evidence | -| --- | --- | --- | --- | -| {fact} | {Verified \| Strongly supported \| Inferred \| Contradiction resolved} | {1-100} | {file, test, or commit range that supports it} | - -## Contradictions - -{Render the **Contradiction disclosure** layout here, or `None found.`} - -## Gaps - -- {Area the repository's own evidence could not establish, and what would - resolve it, or `None identified.`} - -## Verification - -- {Which quality-audit checks ran and their outcome.} -``` - -# Report rules - -- Every written path must be exact so the reader can open it directly. -- The fact ledger reports the score the workflow assigned; it is chat output - only and is never written into a context file. -- A fact scored below `50` never appears in the ledger as written truth. It is - either resolved through the clarification gate or listed under **Gaps**. -- **Contradictions** is never omitted. State `None found.` when none were - found; silence reads as none were looked for. -- Never claim a file was written unless the write actually completed. -- Never report a quality-audit check as passed unless it ran. -- Report `Mode: rebuild` only when the literal `rebuild` token was supplied. -- Do not recommend a follow-up workflow command. The reconstruction is the - whole deliverable. diff --git a/.pi/prompts/brownfield.md b/.pi/prompts/brownfield.md deleted file mode 100644 index 8a6d77b5..00000000 --- a/.pi/prompts/brownfield.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -description: "Reconstruct durable context from an existing repository's own evidence" -argument-hint: "[rebuild] [path ...]" ---- - -Invoke the `sce-brownfield` skill exactly once with `$ARGUMENTS`. -The skill owns the complete workflow, including all waits and same-session resume -behavior. Do not invoke any phase skill or sequence workflow steps in this command. diff --git a/.pi/skills/sce-brownfield/SKILL.md b/.pi/skills/sce-brownfield/SKILL.md deleted file mode 100644 index 38f88e72..00000000 --- a/.pi/skills/sce-brownfield/SKILL.md +++ /dev/null @@ -1,333 +0,0 @@ ---- -name: sce-brownfield -description: > - Reconstruct durable context from an existing repository's own evidence ---- - -# SCE Brownfield - -## Purpose - -Own this workflow from input parsing through its terminal user-visible response. -Execute the phases below directly and in order. Phase statuses are internal state, -not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or -workflow command. Follow the canonical workflow's steps, gates, -and stops exactly as written: never invent, skip, reorder, or merge a step. - -## User-visible output - -Use `references/output.md` for every gate and terminal response. Render no raw -internal state. The reference contains only human-visible Markdown layouts. -User-visible output is limited to those layouts: never invent a layout, and never -wrap one in an added preamble, commentary, summary, or extra section. - -## Composite control flow - -Keep phase results as internal state and continue immediately whenever the -canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. - -This workflow reconstructs durable `context/` memory for a repository that has -none, or that has gaps. It is a cold-start and gap-fill tool, not a recurring -context-maintenance or drift-repair command. Ongoing maintenance stays owned by -the task and plan synchronization phases. - -## Input - -`$ARGUMENTS` is `[rebuild] [path ...]`. Parse it into two parts before any -investigation: - -- An optional leading literal token `rebuild`. Its presence, and only its - presence, grants rewrite authority over existing context files. -- Zero or more remaining tokens, each an additional local documentation path. - A path may be a file or a directory, inside or outside the repository. - -Parsing rules: - -- Empty `$ARGUMENTS` is valid and selects additive mode with no extra paths. -- `rebuild` is recognized only as the first token. In any later position it is a - path. -- Never infer `rebuild` from conversation content, repository state, or the - apparent staleness of existing context. Only the literal leading token sets it. -- A remaining token that does not resolve to an existing readable local path is - invalid input. - -When input is invalid, render the **Invalid usage** layout from -`references/output.md` naming the offending token, and stop. Do not guess the -token's meaning, and do not investigate or write anything. - -## Workflow - -### 1. Confirm the context root - -Before any investigation, evidence gathering, or write, check whether `context/` -exists. - -When `context/` does not exist, there is no durable memory to fill. Render the -**Missing context bootstrap gate** layout from `references/output.md` with -`sce setup --bootstrap-context` as the required action, and stop. Read no -repository evidence and write no file. - -Bootstrapping is the user's action, not this workflow's. This workflow never -creates the `context/` root, and never creates a file outside it. - -Wait for the user. When they report the command ran, run this step again and -continue in this session. Do not restart argument parsing. - -When `context/` exists, record which of the baseline files and directories are -present, then continue to the next step. - -### 2. Gather evidence - -Evidence is strictly local. Gather it in the priority order below. When two -sources disagree, the earlier source in this order wins, and the disagreement is -recorded for step 5. - -#### 2.1 Current code first - -Read the repository's current source before anything that describes it. -Establish: - -- Entry points, executables, and published interfaces. -- Module and package boundaries, and the dependency direction between them. -- Data shapes, persistence, and external integrations. -- Error handling, configuration surfaces, and observable behavior. - -Current code is the highest-authority evidence class. Nothing later in this order -overrides it. - -#### 2.2 Tests, schemas, migrations, build and runtime configuration - -Read, when present: - -- Test suites, for intended behavior, invariants, and edge cases the code alone - does not state. -- Schemas and migrations, for the data model and its history. -- Build, packaging, and dependency manifests, for the artifact set and toolchain. -- Runtime and deployment configuration, for environments, services, and - operational behavior. - -This class is executable truth. Treat it as second only to current code. - -#### 2.3 Discover documentation explicitly - -Do not assume a README is the only documentation. Sweep the repository for it: - -- Repository-root Markdown and plain-text documents. -- `docs/`, `doc/`, `documentation/`, `wiki/`, `adr/`, `decisions/`, `rfcs/`, - `design/`, and `notes/` directories, at any depth. -- Per-package or per-module `README`, `CONTRIBUTING`, `ARCHITECTURE`, and - `CHANGELOG` files. -- Comment blocks that document a whole module rather than a single line. -- Issue, pull-request, and agent-instruction files committed to the repository, - such as `AGENTS.md` or equivalent. - -Record what the sweep found and what it did not. Documentation describes intent -and may be stale; it never outranks code. - -#### 2.4 Read argument-supplied paths - -Read every path supplied as an argument, as an additional documentation source -at the same authority as step 2.3. A supplied directory is read recursively for -documentation files. - -A supplied path that exists but yields nothing usable is recorded as a gap, not -a failure. - -#### 2.5 Read at least three months of Git history - -Read no less than three months of history measured back from the current date. - -Use it for: - -- When and why current structure arrived. -- Migrations, renames, deletions, and reversals. -- Recurring risk areas and repeatedly repaired code. - -Read older history only when recent evidence points at a still-relevant -architectural decision, migration, rename, deletion, or recurring risk. Follow -that thread as far back as it stays relevant, and no further. - -History explains change. It is the weakest evidence class for current truth: a -commit message describes an intention at a moment, not the state of the code now. - -#### 2.6 No network access - -This workflow performs no network access. Do not fetch a URL, search the web, -query a package registry, call a remote API, or read any external documentation -source. Evidence is the local repository plus argument-supplied local paths, and -nothing else. - -When local evidence is insufficient, that is a gap or a clarification question. -It is never a reason to go looking outside the repository. - -### 3. Score every important fact - -An important fact is any statement that would be written into `context/` as -durable truth. Maintain an internal ledger with one entry per important fact: -the statement, its supporting evidence, its status, and a confidence score from -`1` to `100`. - -Assign the score from the evidence, not from how plausible the statement sounds: - -- `90`–`100` **Verified** — directly observable in current code or executable - configuration. -- `70`–`89` **Strongly supported** — consistent across two independent evidence - classes, with no contradiction. -- `50`–`69` **Inferred** — supported by one evidence class and contradicted by - none, but not directly observable. -- `1`–`49` **Clarification required** — guessed, supported only by documentation - or history that the code does not confirm, or genuinely ambiguous. - -A fact whose evidence conflicted and was resolved under step 5 is scored after -resolution and carries the status **Contradiction resolved**. - -The ledger is internal state and chat evidence only. No score, and nothing -derived from one, is ever written under `context/`. - -### 4. Block on low-confidence facts - -Any important fact scoring below `50` blocks. It is not written as truth, and -it is not silently downgraded, hedged, or omitted to avoid asking. - -Collect every blocking fact, group the questions by area so the user answers a -coherent set rather than a list of fragments, and render the **Clarification -gate** layout from `references/output.md`. Each question must offer at least two -concrete options drawn from the evidence, plus an explicit freeform answer. - -Stop and wait. Write no context file while waiting. - -When the user answers, apply the answers to the ledger, rescore the affected -facts, and continue in this session at step 5. Do not restart investigation, and -do not re-ask an answered question. - -When a question goes unanswered, its fact is not written. Record it under -**Gaps** in the report. - -### 5. Resolve and disclose contradictions - -A contradiction is material when the conflicting statements would produce -different context. Ignore cosmetic disagreements such as wording or formatting. - -Resolve each material contradiction by evidence priority: current code, then -executable configuration, then documentation, then history. - -Classify each one: - -- **Stale documentation** — documentation describes behavior the code no longer - has. -- **Superseded decision** — history records a decision that later work reversed. -- **Divergent implementations** — two parts of the current code disagree with - each other. -- **Unexplained history** — history and code disagree with no evidence of which - is current. - -Every material contradiction is disclosed in the report with its classification -and the interpretation written to context, whatever its confidence. Never resolve -one silently. A contradiction that cannot be resolved by evidence priority is a -blocking clarification under step 4. - -### 6. Infer the context structure - -Derive the `context/{domain}/` structure from the repository's own boundaries — -its modules, services, deployable units, and data ownership — not from a generic -template. - -- One topic per file. A domain that owns several distinct concerns gets several - focused files rather than one large one. -- Name domains in the repository's own terminology. -- Prefer extending the existing structure over inventing a parallel one. When - `context/` already carries domains, new files join them. -- Plan the root files against what the evidence actually established: - `overview.md`, `architecture.md`, `glossary.md`, `patterns.md`, and - `context-map.md`. - -Record the planned file set before writing it. - -### 7. Write context - -Writes are additive by default. In additive mode: - -- Write only missing files and missing domains. -- Never overwrite, truncate, move, rename, or delete an existing context file. -- When a planned file already exists, leave it untouched and record it as - skipped, even when the existing content looks thinner than what was found. - -When the literal `rebuild` token was supplied, rewrite authority extends to -existing context files. Even then: - -- Rewrite only files this reconstruction has evidence for. -- Never delete a context file. Rewriting is the whole of the granted authority. -- Never touch `context/plans/`, `context/handovers/`, `context/decisions/`, or - `context/tmp/`. Those are owned by other workflows. -- Never modify a context file with uncommitted changes. - -Every write obeys these content rules: - -- Describe current state and resulting behavior, not the investigation that found - it and not a change narrative. -- Never write a confidence score, a commit hash, a timestamp, or a date under - `context/`. -- Never attribute a statement to this workflow, this session, or its evidence - gathering. -- One topic per file, at most 250 lines, relative Markdown links between context - files. -- A Mermaid diagram where structure, boundaries, or flows are complex enough that - prose alone would not carry them; concrete code examples only where they - clarify non-trivial behavior. -- Add a glossary entry for domain language the repository uses and the glossary - does not define. - -Link every created or updated domain file from `context/context-map.md`. When -the map already exists, edit it additively: add the missing entries and leave the -existing ones as they are. - -Never write outside `context/`. Do not modify application code, tests, plans, or -configuration. - -### 8. Audit the result - -After writing, audit what was written. Every item below is blocking: a failure -stops the workflow rather than being noted in passing. - -- No confidence score, commit hash, timestamp, or date appears under `context/`. -- Every created or updated domain file is reachable from - `context/context-map.md`. -- Every written file is at or below 250 lines and covers one topic. -- Every link between context files is relative and resolves. -- No fact scoring below `50` was written as truth. -- No existing context file was overwritten unless `rebuild` was supplied, and - none was deleted in any mode. -- Nothing was written outside `context/`. -- Every material contradiction found in step 5 appears in the report. - -When an audit item fails, repair the written files when the repair is -unambiguous, then rerun the item. When it cannot be repaired without inventing a -fact, render the **Blocked** layout from `references/output.md` naming the failed -item, the files preserved, and the retry condition, and stop. - -### 9. Report - -Render the **Completed report** layout from `references/output.md` with the -mode, the written and untouched files, the fact ledger, the contradictions, the -gaps, and the audit outcome. Stop. - -## Rules - -- Reconstruct context at most once per invocation. -- Never create the `context/` root; `sce setup --bootstrap-context` owns that. -- Never write, move, or delete a file outside `context/`. -- Never delete a context file, in either mode. -- Never overwrite an existing context file unless the literal leading `rebuild` - token was supplied. -- Never infer `rebuild` from anything other than that token. -- Never access the network or read a non-local source. -- Never write a fact scoring below `50` as truth. -- Never resolve a material contradiction without disclosing it. -- Never write a confidence score, hash, timestamp, or date under `context/`. -- Never invoke another skill, sibling package, or workflow command. -- Never synchronize context, validate a plan, select or execute a task, or create - a Git commit. -- Never treat this workflow as recurring context maintenance. diff --git a/.pi/skills/sce-brownfield/references/output.md b/.pi/skills/sce-brownfield/references/output.md deleted file mode 100644 index 43f96d74..00000000 --- a/.pi/skills/sce-brownfield/references/output.md +++ /dev/null @@ -1,134 +0,0 @@ -# Brownfield output layouts - -Use only the applicable layout. Values come from internal workflow state. - -## Invalid usage - -```markdown -------------------------------------- - -# Brownfield: invalid arguments - -`/brownfield` takes an optional leading `rebuild` token followed by any number -of local documentation paths: `/brownfield [rebuild] [path ...]`. - -Problem: {unrecognized token, misplaced `rebuild`, or unreadable path} - -Received: `{$ARGUMENTS}` - -Nothing was investigated and nothing was written. -``` - -## Missing context bootstrap gate - -```markdown -------------------------------------- - -# This repository has no durable context. - -Bootstrap it, then continue in this session: - -`sce setup --bootstrap-context` - -Nothing was investigated and nothing was written. -``` - -## Clarification gate - -```markdown -------------------------------------- - -# Clarification needed. - -No context file was written. - -{count} facts could not be established from repository evidence alone. Answer -each group below. Every question accepts one of its options or a freeform -answer. - -## {group-id} · {area} - -{question} - -- A. {option} -- B. {option} -- C. {option, when one applies} -- Or answer freely. - -Evidence found: {what the repository showed} -Why this blocks: {which context statement cannot be written as truth} -``` - -## Blocked - -Present each blocker's problem, impact, and required action. State what was -investigated, that no context file was written, and the concrete condition -under which `/brownfield` should run again. - -## Contradiction disclosure - -Rendered as a section of the completed report, never on its own. - -```markdown -## Contradictions - -- **{subject}** — {classification: stale documentation | superseded decision | - divergent implementations | unexplained history} - - Code says: {what the code shows} - - Other evidence says: {what documentation or history claims} - - Resolved as: {the interpretation written to context, and why} -``` - -## Completed report - -```markdown -------------------------------------- - -# Brownfield reconstruction complete. - -Mode: {additive | rebuild} - -{written-count} context files written, {skipped-count} left untouched. - -## Written - -- `{context path}` — {what durable truth it now records} - -## Left untouched - -- `{context path}` — already present; additive mode does not overwrite it. - -## Fact ledger - -| Fact | Status | Score | Evidence | -| --- | --- | --- | --- | -| {fact} | {Verified \| Strongly supported \| Inferred \| Contradiction resolved} | {1-100} | {file, test, or commit range that supports it} | - -## Contradictions - -{Render the **Contradiction disclosure** layout here, or `None found.`} - -## Gaps - -- {Area the repository's own evidence could not establish, and what would - resolve it, or `None identified.`} - -## Verification - -- {Which quality-audit checks ran and their outcome.} -``` - -# Report rules - -- Every written path must be exact so the reader can open it directly. -- The fact ledger reports the score the workflow assigned; it is chat output - only and is never written into a context file. -- A fact scored below `50` never appears in the ledger as written truth. It is - either resolved through the clarification gate or listed under **Gaps**. -- **Contradictions** is never omitted. State `None found.` when none were - found; silence reads as none were looked for. -- Never claim a file was written unless the write actually completed. -- Never report a quality-audit check as passed unless it ran. -- Report `Mode: rebuild` only when the literal `rebuild` token was supplied. -- Do not recommend a follow-up workflow command. The reconstruction is the - whole deliverable. diff --git a/.sce/config.json b/.sce/config.json index 77242a90..a2bdc0f7 100644 --- a/.sce/config.json +++ b/.sce/config.json @@ -1,9 +1,7 @@ { "$schema": "https://sce.crocoder.dev/config.json", "integrations": { - "optional_workflows": [ - "brownfield" - ], + "optional_workflows": [], "target": [ "claude", "opencode", diff --git a/cli/src/services/config/resolver.rs b/cli/src/services/config/resolver.rs index 5ffc5298..f786979f 100644 --- a/cli/src/services/config/resolver.rs +++ b/cli/src/services/config/resolver.rs @@ -35,7 +35,7 @@ pub(crate) const WORKOS_CLIENT_ID_KEY: AuthConfigKeySpec = AuthConfigKeySpec { }; const CONTROL_PLANE_BASE_URL_ENV: &str = "SCE_CONTROL_PLANE_BASE_URL"; -const CONTROL_PLANE_BASE_URL_BAKED_DEFAULT: &str = "https://sce.crocoder.dev"; +const CONTROL_PLANE_BASE_URL_BAKED_DEFAULT: &str = "https://sce.crocoderlab.dev"; pub(crate) const CONTROL_PLANE_BASE_URL_KEY: AuthConfigKeySpec = AuthConfigKeySpec { config_key: "control_plane_base_url", diff --git a/context/architecture.md b/context/architecture.md index b4e0ec82..25f98eea 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -111,7 +111,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `cli/src/services/observability.rs` no longer owns duplicate log enums or parsing helpers; it consumes the canonical primitive seam from `cli/src/services/config/mod.rs` and stays focused on logger and telemetry runtime behavior. - `cli/src/cli_schema.rs` is now the canonical owner for top-level command metadata for the real clap-backed command set (`auth`, `config`, `setup`, `doctor`, `hooks`, `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. +- `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 trace 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 resolution, database-retry config resolution and `DATABASE_RETRY_CONFIG` `OnceLock` initialization, default-discovered invalid-file degradation, 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. @@ -132,7 +132,7 @@ The repository includes a new placeholder Rust binary crate at `cli/`. - `cli/src/services/hooks/mod.rs` defines the current local hook runtime parsing/dispatch (`HookSubcommand`, `run_hooks_subcommand`) 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); `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 extracts direct model metadata from top-level or nested `model` fields and normalizes it with the `claude/` prefix when present. `session-model` is no longer a supported hook route. - Generated Claude settings no longer register `SessionStart` for Agent Trace model attribution, and `sce hooks session-model` is no longer a supported hook command. The `session_models` table/API and diff-trace fallback lookup were removed in T02 of the `remove-session-models-direct-claude-model-id` plan; `diff-trace` now uses only direct payload `model_id` and `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. -- `sce trace sync [--format text|json]` is implemented: `cli/src/services/trace/sync.rs` resolves repository-scoped Agent Trace storage the same way `sce trace status` does, authenticates against the control plane with stored WorkOS credentials, calls the ingestion `/state` endpoint once, then synchronizes the `messages`/`parts`/`diff_traces`/`agent_traces` capture streams via `AgentTraceExportReader` and a shared per-stream reconciliation engine, and `cli/src/services/trace/render_sync.rs` renders the converged `AgentTraceSyncReport` as concise per-stream text or `camelCase` JSON (see `context/cli/trace-command.md`). Local DB bootstrap and setup-time repository-scoped Agent Trace DB initialization otherwise still flow through lifecycle providers aggregated by setup, while repository-scoped DB health/repair flows through the doctor surface. There is no checkout-scoped DB discovery or `sce trace --legacy` surface (removed by the `retire-legacy-agent-trace-db` plan). +- `sce trace sync [--format text|json]` is implemented: `cli/src/services/trace/sync.rs` resolves repository-scoped Agent Trace storage the same way `sce trace status` does, authenticates against the control plane with stored WorkOS credentials, uses the config-resolved `control_plane_base_url` with baked default `https://sce.crocoderlab.dev`, calls the ingestion `/state` endpoint once, then synchronizes the `messages`/`parts`/`diff_traces`/`agent_traces` capture streams via `AgentTraceExportReader` and a shared per-stream reconciliation engine, and `cli/src/services/trace/render_sync.rs` renders the converged `AgentTraceSyncReport` as concise per-stream text or `camelCase` JSON (see `context/cli/trace-command.md`). Local DB bootstrap and setup-time repository-scoped Agent Trace DB initialization otherwise still flow through lifecycle providers aggregated by setup, while repository-scoped DB health/repair flows through the doctor surface. There is no checkout-scoped DB discovery or `sce trace --legacy` surface (removed by the `retire-legacy-agent-trace-db` plan). - `cli/src/services/patch.rs` defines the standalone patch domain model (`ParsedPatch`, `PatchFileChange`, `FileChangeKind`, `PatchHunk`, `TouchedLine`, `TouchedLineKind`) for in-memory parsed unified-diff representation, capturing only touched lines (added/removed) plus minimal per-file/per-hunk metadata while excluding non-hunk headers and unchanged context lines. All types are `serde`-serializable/deserializable with `snake_case` JSON field naming. The module also provides `parse_patch`, a public parser function that converts raw unified-diff text (both `Index:` SVN-style and `diff --git` git-style formats) into `ParsedPatch` structs, with `ParseError` for actionable malformed-input diagnostics. Storage-agnostic JSON load helpers (`load_patch_from_json` for string input, `load_patch_from_json_bytes` for byte input) reconstruct `ParsedPatch` from serialized JSON content with `PatchLoadError` for actionable deserialization diagnostics. Its patch-set operations now include deterministic ordered combination plus target-shaped intersection that prefers exact touched-line matches and falls back to historical `kind`+`content` matching when incremental diffs and canonical post-commit diffs have drifted line numbers; `parse_patch`, `combine_patches`, and `intersect_patches` are consumed by the active post-commit hook runtime. - `cli/src/services/structured_patch.rs` defines the synchronous structured editor-hook derivation seam. It derives Claude `PostToolUse` `Write` structured-update hunks, `Write` `tool_input.content` create fallback, and `Edit` structured-patch payloads into canonical `ParsedPatch` values plus Claude session/tool metadata, returning deterministic skip reasons for unsupported events/tools/payload shapes. The module is pure and side-effect-free. It is wired into `sce hooks diff-trace` for Claude payload classification at intake (T04) and into `RepositoryAgentTraceDb::recent_diff_trace_patches` for post-commit structured payload parsing dispatch at read time (T05). - `cli/src/services/` contains module boundaries for command_registry, lifecycle, auth_command, config, setup, doctor, hooks, checkout identity, bash_policy, version, completion, help, patch, SCE web URL helpers, shared database infrastructure, local DB adapters, encrypted auth DB adapters, and Agent Trace DB adapters with explicit trait seams for future implementations. `cli/src/services/checkout/` owns checkout ID file infrastructure (the per-checkout Agent Trace DB opener/path helper was removed by the `retire-legacy-agent-trace-db` plan); active setup/hooks use `agent_trace_storage` to establish checkout identity as diagnostics and initialize/open the repository-scoped DB, while `sce doctor` surfaces checkout identity facts plus credential-safe repository Agent Trace DB metadata. There is no checkout-scoped discovery or `sce trace --legacy` surface; any pre-migration `agent-trace-*.db` files on disk are never touched and no longer inspectable via the CLI. `cli/src/services/bash_policy.rs` owns both the CLI-agnostic evaluator logic and the hidden `sce policy bash` command adapter used by OpenCode, Claude, and Pi callers. `cli/src/services/command_registry.rs` defines the static `RuntimeCommand` enum, deterministic `CommandRegistry` name catalog, and `build_default_registry()` function for command dispatch metadata. Service-owned command modules own the runtime command payload structs for help/help-text, version, completion, auth, config, setup, doctor, hooks, and policy. diff --git a/context/cli/agent-trace-sync-command.md b/context/cli/agent-trace-sync-command.md index 1eed8e97..3ae5da93 100644 --- a/context/cli/agent-trace-sync-command.md +++ b/context/cli/agent-trace-sync-command.md @@ -19,12 +19,12 @@ flowchart LR A[hooks / plugins] --> B[repository Agent Trace DB] B --> C[AgentTraceExportReader] C --> D[sce trace sync] - D -- "HTTPS + WorkOS Bearer" --> E[control plane] + D -- "HTTPS + WorkOS Bearer" --> E[control plane
sce.crocoderlab.dev by default] ``` - **hooks/plugins** write local capture rows (`messages`, `parts`, `diff_traces`, `agent_traces`) into the current repository's `RepositoryAgentTraceDb` during normal Git/editor activity — this is unchanged by sync. - **`AgentTraceExportReader`** (PR #198) is the read-only local export boundary sync uses to read rows after a cursor; sync never queries the repository DB directly. -- **`sce trace sync`** resolves repository storage through the same `agent_trace_storage` path `sce trace status` uses (not the hook-runtime resolver), builds an `AuthenticatedControlPlaneClient` from stored WorkOS credentials and the resolved `control_plane_base_url`, and drives one authoritative `/state` call plus one bounded per-stream reconciliation loop per stream. +- **`sce trace sync`** resolves repository storage through the same `agent_trace_storage` path `sce trace status` uses (not the hook-runtime resolver), builds an `AuthenticatedControlPlaneClient` from stored WorkOS credentials and the resolved `control_plane_base_url`, and drives one authoritative `/state` call plus one bounded per-stream reconciliation loop per stream. With no environment or config override, that base is `https://sce.crocoderlab.dev`; it is distinct from the `https://sce.crocoder.dev` SCE web and config-schema URL owner. - **Credential runtime boundary:** `AuthenticatedControlPlaneClient` keeps the synchronous `CredentialStore` behind an `Arc` and runs every token-storage `load`/`save` through `tokio::task::spawn_blocking`. The underlying encrypted auth DB and Linux Secret Service/zbus APIs are blocking and may create their own Tokio runtime, so they must never execute directly inside the async control-plane request future. Token refresh and HTTP requests remain asynchronous; only credential persistence crosses the blocking boundary. - **control plane** is the sole source of cursor truth: every invocation starts from `POST /agent-trace/ingestion/state`, uploads via `POST /agent-trace/ingestion/batch`, and advances a stream's cursor only from a validated batch response (`accepted == rows.len()` and `cursor == rows.last().sourceRowId`), never by inferring `cursor + rows.len()`. diff --git a/context/cli/config-precedence-contract.md b/context/cli/config-precedence-contract.md index b1ce7c6b..59ef766e 100644 --- a/context/cli/config-precedence-contract.md +++ b/context/cli/config-precedence-contract.md @@ -50,7 +50,9 @@ Supported auth-adjacent runtime keys can participate in one shared key-declared - `control_plane_base_url`, the base URL of the control-plane Agent Trace ingestion API, which resolves as: 1. environment value (`SCE_CONTROL_PLANE_BASE_URL`) 2. config file value (`control_plane_base_url`) - 3. baked default (`https://sce.crocoder.dev`) + 3. baked default (`https://sce.crocoderlab.dev`) + +The control-plane default is separate from the config schema declaration (`https://sce.crocoder.dev/config.json`) and from the SCE web URL owner used for Agent Trace links. When a supported auth-adjacent key omits a baked default, the same resolver still reports `value: null` / `(unset)` with no resolved source when both env and config inputs are absent. diff --git a/context/cli/trace-command.md b/context/cli/trace-command.md index 75e8f086..6a4ec3da 100644 --- a/context/cli/trace-command.md +++ b/context/cli/trace-command.md @@ -80,7 +80,7 @@ Text rendering shows discovery summary, totals, and a `By database` table with ` ### Sync — `services::trace::sync`, `render_sync` -`run_current_sync(repo_root)` resolves the current repository's Agent Trace storage through the same `agent_trace_storage` path `sce trace status` uses (not the hook-runtime resolver), builds an `AuthenticatedControlPlaneClient` from the resolved `control_plane_base_url`/`workos_client_id` config, calls the control-plane `/agent-trace/ingestion/state` endpoint once, then synchronizes the four independent capture streams (`messages`, `parts`, `diff_traces`, `agent_traces`, in that fixed order) via the local `AgentTraceExportReader` and the shared per-stream reconciliation engine, producing an `AgentTraceSyncReport`. A genuinely ambiguous batch outcome (`5xx`, transport failure, invalid response) reconciles by refetching `/state`; a terminal control-plane failure (missing/invalid credentials, `400`, `403`) fails the stream immediately without an extra network call, so a `403` never mutates local repository metadata or retries. No local sync cursor, cursor file, or database is created — every invocation starts from the authoritative `/state` cursors, so repeated runs are naturally incremental. +`run_current_sync(repo_root)` resolves the current repository's Agent Trace storage through the same `agent_trace_storage` path `sce trace status` uses (not the hook-runtime resolver), builds an `AuthenticatedControlPlaneClient` from the resolved `control_plane_base_url`/`workos_client_id` config, and uses `https://sce.crocoderlab.dev` as the baked control-plane base when no override is configured. This control-plane host is separate from the SCE web/schema URL owned by `SCE_WEB_BASE_URL`. Sync calls the control-plane `/agent-trace/ingestion/state` endpoint once, then synchronizes the four independent capture streams (`messages`, `parts`, `diff_traces`, `agent_traces`, in that fixed order) via the local `AgentTraceExportReader` and the shared per-stream reconciliation engine, producing an `AgentTraceSyncReport`. A genuinely ambiguous batch outcome (`5xx`, transport failure, invalid response) reconciles by refetching `/state`; a terminal control-plane failure (missing/invalid credentials, `400`, `403`) fails the stream immediately without an extra network call, so a `403` never mutates local repository metadata or retries. No local sync cursor, cursor file, or database is created — every invocation starts from the authoritative `/state` cursors, so repeated runs are naturally incremental. `render_sync::render(report, format)` renders the converged `AgentTraceSyncReport`. Text output is a `style::heading("Agent Trace sync complete.")` line, `Repository ID:`/`Source instance ID:` lines, then a padded table with one row per stream (`Stream`, `Uploaded`, `Final cursor`) in the fixed `messages → parts → diff_traces → agent_traces` order — no per-batch or per-row detail is printed. JSON output carries `status`, `command`, `subcommand`, `repositoryId`, `sourceInstanceId`, and `streams.{messages,parts,diffTraces,agentTraces}`, each with `uploaded`/`initialCursor`/`finalCursor`/`batches`; the JSON stream keys are camelCase (`diffTraces`/`agentTraces`) even though the internal `StreamSyncReports` struct fields are `diff_traces`/`agent_traces`. `TraceCommand::execute` dispatches `TraceSubcommandRequest::Sync { format }` to `render_sync::render`, completing the command surface end to end. diff --git a/context/context-map.md b/context/context-map.md index 3f0500cd..9d83b870 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -112,3 +112,4 @@ Recent decision records: - `context/decisions/2026-03-25-first-install-channels.md` (approved first-wave install/distribution scope for `sce`, canonical naming, and Nix-owned build policy) - `context/decisions/2026-07-17-retire-legacy-agent-trace-db.md` (retire the checkout-scoped Agent Trace DB surface; `RepositoryAgentTraceDb` is the sole adapter, no `sce trace --legacy`, no global/checkout fallback path; pre-migration on-disk files are never touched and no longer inspectable via the CLI) - `context/decisions/2026-08-10-agent-trace-source-instance-id.md` (adds `source_instance_id` as a physical-database-lineage identity on `repository_metadata`, independent of and never derived from `repository_id`; concurrency-safe atomic claim; local storage identity only, no remote-ingestion architecture designed) +- `context/decisions/2026-08-11-separate-control-plane-and-sce-web-urls.md` (separates the dedicated control-plane sync default from the SCE web and config-schema URL owner while preserving the existing override seam and ingestion contract) diff --git a/context/decisions/2026-08-11-separate-control-plane-and-sce-web-urls.md b/context/decisions/2026-08-11-separate-control-plane-and-sce-web-urls.md new file mode 100644 index 00000000..f37cd214 --- /dev/null +++ b/context/decisions/2026-08-11-separate-control-plane-and-sce-web-urls.md @@ -0,0 +1,50 @@ +# Decision: Separate control-plane sync and SCE web/schema URLs + +Date: 2026-08-11 +Status: Accepted +Plan: `context/plans/separate-config-schema-and-control-plane-urls.md` +Task: T01 + +## Context + +The CLI has two durable URL responsibilities: `sce trace sync` needs a control-plane ingestion host, while Agent Trace links and the config schema declaration belong to the SCE web application. The existing `control_plane_base_url` default conflated those responsibilities by using the web host. The resolver already provides the compatibility seam for environment and config-file overrides, and the sync client already composes the stable ingestion routes from that resolved base. + +## Decision + +Use `https://sce.crocoderlab.dev` as the baked default for `control_plane_base_url` and `sce trace sync`; keep `https://sce.crocoder.dev` as the owner of SCE web URLs and the config schema declaration. + +## Rationale + +This gives control-plane ingestion an explicit endpoint owner without changing the existing configuration seam, request paths, authentication behavior, Agent Trace web links, or schema publication contract. + +## Alternatives considered + +- **Keep the web host as the control-plane default** — preserves the conflated responsibility and does not route default sync traffic through the dedicated control-plane host. +- **Introduce a second runtime configuration key or URL registry** — broadens the configuration surface beyond the established `control_plane_base_url` seam without being required by the change. + +## Compatibility and risks + +- Existing `SCE_CONTROL_PLANE_BASE_URL` and `control_plane_base_url` overrides continue to take precedence over the new baked default, so deployments with an intentional endpoint remain compatible. +- The default sync destination changes for installations without an override; the dedicated host must serve the existing `/agent-trace/ingestion/state` and `/agent-trace/ingestion/batch` contract. + +## Guardrails + +- Do not change ingestion route paths, authentication, WorkOS endpoints, or the control-plane client protocol. +- Do not change `SCE_WEB_BASE_URL`, Agent Trace conversation/session/trace URL construction, or the `https://sce.crocoder.dev/config.json` schema declaration. +- Do not add another runtime configuration key or a general URL registry. + +## Consequences + +- Default `sce trace sync` requests target the dedicated control-plane host, while schema and web-link consumers remain on the SCE web host. +- Documentation and tests must identify the two URL owners separately, and operators can still override the control-plane host through the existing precedence chain. + +## Follow-up + +None. + +## References + +- Plan: [`separate-config-schema-and-control-plane-urls`](../plans/separate-config-schema-and-control-plane-urls.md) +- Task: `T01` +- Current-state context: [`CLI config precedence contract`](../cli/config-precedence-contract.md), [`sce trace command`](../cli/trace-command.md), [`Agent Trace sync architecture`](../cli/agent-trace-sync-command.md), [`Architecture`](../architecture.md), [`Glossary`](../glossary.md) +- Evidence: [`config resolver`](../../cli/src/services/config/resolver.rs), [`trace sync orchestration`](../../cli/src/services/trace/sync.rs), [`generated schema source`](../../config/pkl/base/sce-config-schema.pkl) diff --git a/context/glossary.md b/context/glossary.md index a98d3a05..60b104c5 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -134,7 +134,7 @@ - `sce shared output-format contract`: Canonical parser contract in `cli/src/services/output_format.rs` (`OutputFormat`) that centralizes supported `--format` values (`text`, `json`) and emits command-specific actionable invalid-value guidance (`Run ' --help' ...`) for commands wired to dual-output rendering. - `sce shell completion contract`: Deterministic CLI completion contract where `sce completion --shell ` emits parser-aligned Bash/Zsh/Fish completion scripts for current top-level commands and supported options/subcommands. - `CLI default path catalog`: Canonical production path-ownership contract in `cli/src/services/default_paths.rs`; it is the shared owner for non-test production CLI path definitions, covering per-user persisted paths plus repo-relative, install, hook, and context-path definitions used by production CLI code; build-time payload paths are `OUT_DIR`-owned and excluded. -- `SCE web URL owner`: Rust service module at `cli/src/services/agent_trace.rs`; owns `SCE_WEB_BASE_URL = "https://sce.crocoder.dev"` and helper functions for SCE-owned URL construction, including Agent Trace conversation URLs, persisted Agent Trace trace URLs, Agent Trace session URLs, and setup-created config schema URLs. +- `SCE web URL owner`: Rust service module at `cli/src/services/agent_trace.rs`; owns `SCE_WEB_BASE_URL = "https://sce.crocoder.dev"` and helper functions for SCE-owned URL construction, including Agent Trace conversation URLs, persisted Agent Trace trace URLs, Agent Trace session URLs, and setup-created config schema URLs. It does not own the `control_plane_base_url` used by `sce trace sync`, whose baked default is `https://sce.crocoderlab.dev`. - `CLI capability traits`: Broad capability seam in `cli/src/services/capabilities.rs` consumed by the borrowed, compile-time-typed `AppContext`. `FsOps`/`StdFsOps` wrap filesystem operations and `GitOps`/`ProcessGitOps` wrap git process execution plus repository-root and hooks-directory resolution; current service internals do not consume them directly yet. - `FsOps`: Filesystem capability trait in `cli/src/services/capabilities.rs` with `read_file`, `write_file`, `metadata`, and `exists`, implemented in production by `StdFsOps`. - `GitOps`: Git capability trait in `cli/src/services/capabilities.rs` with `run_command`, `resolve_repository_root`, `resolve_hooks_directory`, and `is_available`, implemented in production by `ProcessGitOps`. @@ -143,7 +143,7 @@ - `shared runtime/config primitives seam`: Canonical ownership in `cli/src/services/config/types.rs` for the CLI's shared observability/config enums (`LogLevel`, `LogFormat`, `LogFileMode`), request/response primitives (`ConfigSubcommand`, `ConfigRequest`, `ReportFormat`), source metadata types (`ValueSource`, `ConfigPathSource`, `LoadedConfigPath`, `ResolvedValue`, `ResolvedOptionalValue`), resolved runtime config types (`ResolvedAuthRuntimeConfig`, `ResolvedObservabilityRuntimeConfig`, `ResolvedHookRuntimeConfig`), the `NAME` constant, observability env-key constants, and shared bool parsing helpers; re-exported through `cli/src/services/config/mod.rs` via `pub use types::*` so downstream modules continue importing through `services::config` unchanged. - `config schema and file parsing seam`: Canonical ownership in `cli/src/services/config/schema.rs` for the CLI's JSON Schema embedding (`SCE_CONFIG_SCHEMA_JSON`), `OnceLock` validator (`CONFIG_SCHEMA_VALIDATOR`, `config_schema_validator()`), top-level allowed-key validation (`TOP_LEVEL_CONFIG_KEYS`, `validate_object_keys`), serde DTO definitions (`ParsedFileConfigDocument`, `ParsedPoliciesConfigDocument`, `ParsedBashPolicyConfigDocument`, `ParsedAttributionHooksConfigDocument`, `ParsedCustomBashPolicyEntryDocument`, `ParsedCustomBashPolicyMatchDocument`), file config value wrapper (`FileConfigValue`) and aggregate (`FileConfig`), type aliases (`ParsedBashPolicyConfig`, `ParsedFilePolicies`), and config-file load/parse helpers (`validate_config_file`, `parse_file_config`, `deserialize_typed_config`, `map_policies_config`, `map_attribution_hooks_config`, `map_bash_policy_config`); `validate_config_file` is re-exported `pub(crate)` through `mod.rs` for `lifecycle.rs` and `doctor` consumers. Policy parsing helpers (`parse_bash_policy_presets`, `parse_custom_bash_policies`) and `CustomBashPolicyEntry` are imported from `super::policy` rather than the parent module. - `config policy semantic validation seam`: Canonical ownership in `cli/src/services/config/policy.rs` for the CLI's bash-policy and attribution-hooks semantic validation, merge helpers, and policy rendering: built-in/custom bash-policy catalog types and OnceLock (`BuiltinBashPolicyCatalog`, `BuiltinBashPolicyPreset`, `BuiltinBashPolicyMatcher`, `BuiltinBashPolicyRedundancyWarning`, `BUILTIN_BASH_POLICY_CATALOG`, `BASH_POLICY_PRESET_CATALOG_JSON`), policy config types (`BashPolicyConfig`, `CustomBashPolicyEntry`), catalog accessors (`builtin_bash_policy_catalog`, `builtin_bash_policy_preset_ids`, `is_builtin_bash_policy_preset_id`), policy parsing and validation (`parse_bash_policy_presets`, `parse_custom_bash_policies`, `parse_custom_bash_policy_entry`, `parse_custom_bash_policy_match`, `parse_custom_bash_policy_argv_prefix`), policy resolution (`resolve_bash_policy_config`, `build_validation_warnings`), and policy rendering (`format_bash_policies_text`, `format_bash_policies_json`); `mod.rs` imports `BashPolicyConfig`, `build_validation_warnings`, `format_bash_policies_json`, `format_bash_policies_text`, and `resolve_bash_policy_config` from `policy` for resolution and rendering consumers. -- `config runtime resolver seam`: Canonical ownership in `cli/src/services/config/resolver.rs` for config-file discovery, file-layer merging, env/flag/default precedence resolution, shared auth-key resolution (`workos_client_id`), observability runtime resolution, attribution-hooks gate resolution, and default-discovered invalid-file degradation; `cli/src/services/config/mod.rs` delegates `sce config show|validate` runtime resolution to this seam while facade re-exports preserve startup/auth/hooks callers through `services::config`. +- `config runtime resolver seam`: Canonical ownership in `cli/src/services/config/resolver.rs` for config-file discovery, file-layer merging, env/flag/default precedence resolution, shared auth-key resolution (`workos_client_id`, `control_plane_base_url`), observability runtime resolution, attribution-hooks gate resolution, and default-discovered invalid-file degradation; `control_plane_base_url` defaults to `https://sce.crocoderlab.dev` while the schema and SCE web URL remain `https://sce.crocoder.dev`; `cli/src/services/config/mod.rs` delegates `sce config show|validate` runtime resolution to this seam while facade re-exports preserve startup/auth/hooks callers through `services::config`. - `config render seam`: Canonical ownership in `cli/src/services/config/render.rs` for `sce config show` and `sce config validate` text/JSON output construction, including rendering-specific config-path formatting, resolved-value formatting, validation issue/warning rendering, and auth display-value redaction/abbreviation helpers; `cli/src/services/config/mod.rs` delegates rendering to this private submodule after resolver-owned runtime config resolution. - `sce config schema artifact`: Canonical JSON Schema for global and repo-local `sce/config.json` files, authored in `config/pkl/base/sce-config-schema.pkl`, generated to payload-relative `config/schema/sce-config.schema.json` beneath Cargo `OUT_DIR` or packaging fallback roots, and embedded by `cli/src/services/config/schema.rs` for shared `sce config validate` and doctor config validation. The current schema accepts the canonical `$schema` declaration, flat logging keys (`log_level`, `log_format`, `log_dir`, positive-integer `log_file_retention_limit`), and existing auth/config keys. - `log_file_retention_limit`: Flat top-level `sce/config.json` key validated as an integer with minimum `1`. It has config-file > default precedence only, defaults to `10`, merges global before local, appears in `sce config show` with provenance, and is carried by `ResolvedObservabilityRuntimeConfig`; the concrete logger stores the resolved value and uses it for primary and v2 creation-triggered cleanup. diff --git a/context/overview.md b/context/overview.md index c0dbc108..c7ea6871 100644 --- a/context/overview.md +++ b/context/overview.md @@ -27,11 +27,11 @@ The `setup` command includes an `inquire`-backed target-selection flow: default For repository generation consumers, `config/pkl/generator-inputs.txt` declares the canonical Pkl/plugin input set and `scripts/produce-cli-generated-input.sh` owns its discovery, two-pass `config/pkl/generate.pkl` evaluation, determinism comparison, payload/input inventories, in-flight input-mutation rejection, atomic handoff publication, and staging cleanup. `scripts/run-cli-cargo.sh` creates a fresh temporary destination, delegates generation to that producer, invokes the requested Cargo workflow with `SCE_CLI_GENERATED_INPUT_DIR`, and removes the handoff after Cargo success, failure, or handled signals. `config/pkl/check-generated.sh` delegates the same production mechanics while retaining contract and path assertions. `scripts/prepare-cli-generated-assets.sh` moves the producer-validated Pkl payload and checksums into the unchanged package fallback, adds hooks, migrations, and the Agent Trace schema, and appends only those static checksums to the combined inventory. The root flake's pre-Cargo `cliGeneratedInput` derivation invokes the same producer from a declarative source containing the producer plus its declared inputs. `cli/build.rs` rejects missing, incomplete, modified, or stale repository handoffs, copies the validated payload into Cargo `OUT_DIR/pkl-generated`, stages static inputs under `OUT_DIR/static`, and writes setup-asset, optional-workflow-catalog, and migration Rust manifests into `OUT_DIR`; it never invokes Pkl. Published crates carry the ignored packaging-only fallback, and unpacked downstream builds validate and copy it into their own `OUT_DIR` without requiring Pkl or parent repository paths. The setup service also provides repository-root install orchestration: it resolves the repository root, ensures the additive durable-context baseline, then for normal modes derives a repo-root-scoped `AppContext` from the runtime command context, aggregates `ServiceLifecycle::setup` calls across lifecycle providers (config → local_db → auth_db → agent_trace_db → hooks when requested), handles interactive or flag-based target selection for config asset installation, and reports deterministic completion details (selected target(s) and installed file counts). Setup uses a unified remove-and-replace policy for all write flows — it removes existing targets before swapping staged content and returns deterministic recovery guidance (recover from version control) on swap failure, without creating backup artifacts. The setup command gates all modes on an existing git repository before any writes. Internally, `cli/src/services/setup/mod.rs` now separates install-flow logic from interactive prompt logic through focused support seams. The CLI now also applies baseline security hardening for reliability-driven automation: diagnostics/logging paths use deterministic secret redaction, `sce setup --hooks --repo ` canonicalizes and validates repository paths before execution, and setup write flows run explicit directory write-permission probes before staging/swap operations. -The config service now provides deterministic runtime config resolution with explicit precedence (`flags > env > config file > defaults`), strict config-file validation (`$schema`, `log_level`, `log_format`, `log_dir`, `timeout_ms`, `workos_client_id`, and nested `policies.bash`, `policies.attribution_hooks.enabled`, plus `policies.database_retry` with per-DB `connection_open`/`query` retry policy specs), deterministic default discovery/merge of global+local config files (`${config_root}/sce/config.json` then `.sce/config.json` with local override, where `config_root` comes from the shared default-path seam with XDG/`dirs::config_dir()` config-root resolution), defaults for the resolved observability value set (`log_level=error`, `log_format=text`, `log_dir=/sce/logs`), shared auth-key resolution with optional baked defaults starting at `workos_client_id`, first-class bash-policy preset/custom parsing with deterministic conflict and duplicate-prefix validation, custom-policy `satisfied_by` wrapper exemption (a policy does not fire when the matched command was unwrapped from a declared wrapper such as `nix shell nixpkgs#ripgrep`), and a canonical Pkl-authored `sce/config.json` JSON Schema generated beneath Cargo `OUT_DIR` and embedded by `cli/src/services/config/mod.rs` for both `sce config validate` and doctor-time config checks. Runtime startup config loading keeps parity with that schema by accepting its `$schema` declaration in repo-local and global config files, so startup commands such as `sce version` no longer fail before dispatch on that field; the canonical declaration is `"https://sce.crocoder.dev/config.json"`. App-runtime observability now consumes flat logging keys through the shared resolver, so env values still override config-file values while config files provide deterministic fallback for `log_dir`; positive-integer `log_file_retention_limit` uses config-file/default precedence, defaults to `10`, and controls creation-triggered cleanup for primary and v2 log files; `sce config show` reports resolved observability/auth/policy values with provenance, while `sce config validate` is now a trimmed validation surface that reports only pass/fail plus validation errors or warnings in text and JSON modes. The canonical preset catalog and matching contract live in `config/pkl/base/bash-policy-presets.pkl` and `context/sce/bash-tool-policy-enforcement-contract.md`. +The config service now provides deterministic runtime config resolution with explicit precedence (`flags > env > config file > defaults`), strict config-file validation (`$schema`, `log_level`, `log_format`, `log_dir`, `timeout_ms`, `workos_client_id`, and nested `policies.bash`, `policies.attribution_hooks.enabled`, plus `policies.database_retry` with per-DB `connection_open`/`query` retry policy specs), deterministic default discovery/merge of global+local config files (`${config_root}/sce/config.json` then `.sce/config.json` with local override, where `config_root` comes from the shared default-path seam with XDG/`dirs::config_dir()` config-root resolution), defaults for the resolved observability value set (`log_level=error`, `log_format=text`, `log_dir=/sce/logs`), shared auth-key resolution with optional baked defaults starting at `workos_client_id`, first-class bash-policy preset/custom parsing with deterministic conflict and duplicate-prefix validation, custom-policy `satisfied_by` wrapper exemption (a policy does not fire when the matched command was unwrapped from a declared wrapper such as `nix shell nixpkgs#ripgrep`), and a canonical Pkl-authored `sce/config.json` JSON Schema generated beneath Cargo `OUT_DIR` and embedded by `cli/src/services/config/mod.rs` for both `sce config validate` and doctor-time config checks. Runtime startup config loading keeps parity with that schema by accepting its `$schema` declaration in repo-local and global config files, so startup commands such as `sce version` no longer fail before dispatch on that field; the canonical declaration is `"https://sce.crocoder.dev/config.json"`; this schema URL is separate from the `https://sce.crocoderlab.dev` baked default used by `sce trace sync` for control-plane ingestion. App-runtime observability now consumes flat logging keys through the shared resolver, so env values still override config-file values while config files provide deterministic fallback for `log_dir`; positive-integer `log_file_retention_limit` uses config-file/default precedence, defaults to `10`, and controls creation-triggered cleanup for primary and v2 log files; `sce config show` reports resolved observability/auth/policy values with provenance, while `sce config validate` is now a trimmed validation surface that reports only pass/fail plus validation errors or warnings in text and JSON modes. The canonical preset catalog and matching contract live in `config/pkl/base/bash-policy-presets.pkl` and `context/sce/bash-tool-policy-enforcement-contract.md`. Invalid default-discovered config files now also degrade gracefully at startup: `sce` keeps running with degraded observability defaults, logs `sce.config.invalid_config` warnings, and reserves hard failures for explicit `--config` / `SCE_CONFIG_FILE` targets or other truly invalid runtime observability inputs. `cli/src/services/config/mod.rs` is now a module facade that declares focused config submodules (`types`, `schema`, `policy`, `resolver`, private `render`, `command`, and `lifecycle`), re-exporting `pub use types::*` and `pub(crate) use schema::validate_config_file`. Shared config primitive ownership is delegated to `cli/src/services/config/types.rs`; schema loading and file parsing to `cli/src/services/config/schema.rs`; bash-policy semantic validation and policy-specific formatting to `cli/src/services/config/policy.rs`; runtime discovery/precedence to `cli/src/services/config/resolver.rs`; and `sce config show` / `sce config validate` text+JSON output construction to `cli/src/services/config/render.rs`. Downstream modules continue importing through `services::config` unchanged. The CLI now has a generic borrowed `AppContext` dependency view in `cli/src/app.rs`; `AppRuntime` owns concrete production logger/telemetry/fs/git dependencies, and command execution receives context views that borrow those dependencies plus an optional `repo_root: Option`. `AppContext::with_repo_root(...)` / `ContextWithRepoRoot` derives repo-root-scoped views while preserving the borrowed runtime dependencies, and command execution is generic over associated-type narrow accessor traits where practical. The broad capability seam lives in `cli/src/services/capabilities.rs`, where `FsOps`/`StdFsOps` wrap filesystem operations and `GitOps`/`ProcessGitOps` wrap git process execution plus repository-root/hooks-directory resolution. The shared default path service in `cli/src/services/default_paths.rs` is now the canonical owner for production CLI path definitions. It resolves per-user config/state/cache roots through a dedicated internal `roots` seam, exposes the current persisted-artifact inventory (global config and auth tokens), and also defines named DB paths (auth DB, local DB, Agent Trace DB) plus the repo-relative, install, hook, and context-path accessors consumed across current CLI production code. Non-test production modules should consume this shared catalog instead of hardcoding owned path literals. No default cache-backed persisted artifact currently exists, so cache-root resolution remains available without speculative cache-path features and no legacy default-path fallback is supported. -The Rust CLI also centralizes SCE-owned web URI construction in `cli/src/services/agent_trace.rs`, with `SCE_WEB_BASE_URL` as the single Rust owner for `https://sce.crocoder.dev` and helpers consumed by Agent Trace conversation URLs, Agent Trace persisted trace URLs, Agent Trace session URLs, and setup-created repo-local config schema URLs. +The Rust CLI also centralizes SCE-owned web URI construction in `cli/src/services/agent_trace.rs`, with `SCE_WEB_BASE_URL` as the single Rust owner for `https://sce.crocoder.dev` and helpers consumed by Agent Trace conversation URLs, Agent Trace persisted trace URLs, Agent Trace session URLs, and setup-created repo-local config schema URLs. The config resolver separately owns `control_plane_base_url` and its `https://sce.crocoderlab.dev` baked sync default; the two URL owners must not be conflated. The same config resolver now also owns the attribution-hooks gate used by local hook runtime: opt-out env `SCE_ATTRIBUTION_HOOKS_DISABLED` overrides `policies.attribution_hooks.enabled` with inverted semantics, and the gate defaults to enabled unless explicitly disabled. The config service split now includes `cli/src/services/config/resolver.rs` as the focused owner for config-file discovery, file-layer merging, env/flag/default precedence, auth-key resolution, observability resolution, attribution-hooks resolution, and default-discovered invalid-file degradation; `cli/src/services/config/mod.rs` remains the facade/rendering orchestration surface while preserving existing `services::config` imports. Generated config now includes repo-local OpenCode plugin assets: `sce-bash-policy.ts` plus `sce-agent-trace.ts` are emitted under `config/.opencode/plugins/`; the OpenCode agent-trace plugin extracts `{ sessionID, diff, time, model_id }` from user `message.updated` events with diffs, tracks per-session OpenCode client version from `session.created`/`session.updated`, and sends payloads to `sce hooks diff-trace` with `tool_name="opencode"` plus optional `tool_version`. Claude generated config now routes supported `PostToolUse Write|Edit|MultiEdit|NotebookEdit` events directly to `sce hooks diff-trace`; it no longer registers a `SessionStart` hook or calls `sce hooks session-model`. Rust handles extraction, validation, and persistence without a TypeScript intermediary; the former `config/.claude/plugins/sce-agent-trace.ts` Bun runtime was removed in T07 of the `claude-rust-diff-trace` plan. The Rust hook validates required fields, uses only direct payload `model_id` and `tool_version` (no longer resolves from `session_models`), and persists tool-prefixed `session_id` values (`oc_` for OpenCode, `cc_` for Claude, `pi_` for Pi), `model_id`, `tool_name`, and nullable `tool_version` into `diff_traces` through AgentTraceDb. Bash-policy now delegates OpenCode enforcement to the Rust `sce policy bash` command: the generated OpenCode plugin at `config/.opencode/plugins/sce-bash-policy.ts` is a thin wrapper that calls `sce policy bash --input normalized --output json` via `spawnSync` and throws on deny decisions; it no longer contains independent TypeScript policy logic. The former `bash-policy/runtime.ts` TypeScript runtime has been removed. Preset... diff --git a/context/plans/separate-config-schema-and-control-plane-urls.md b/context/plans/separate-config-schema-and-control-plane-urls.md new file mode 100644 index 00000000..3826910b --- /dev/null +++ b/context/plans/separate-config-schema-and-control-plane-urls.md @@ -0,0 +1,93 @@ +# Plan: separate-config-schema-and-control-plane-urls + +## Change summary + +Separate the two SCE URL responsibilities that are currently conflated in the CLI's control-plane default. `sce trace sync` will use `https://sce.crocoderlab.dev` as its baked control-plane base URL, producing requests to `/agent-trace/ingestion/state` and `/agent-trace/ingestion/batch`. The existing `control_plane_base_url` configuration and `SCE_CONTROL_PLANE_BASE_URL` override remain supported. + +The config schema declaration remains `https://sce.crocoder.dev/config.json`; it is a schema URL for editor and validation tooling, not the sync service endpoint. Existing SCE web URLs for Agent Trace conversations, sessions, and traces also remain on `https://sce.crocoder.dev`. + +## Acceptance criteria + +- [x] AC1: With no `control_plane_base_url` override, `sce trace sync` resolves `https://sce.crocoderlab.dev` as its control-plane base and targets `POST /agent-trace/ingestion/state` and `POST /agent-trace/ingestion/batch` without changing request paths or authentication behavior. + - Validate: `nix flake check` passes the control-plane client and config resolver tests, including assertions for the baked default and composed ingestion paths. +- [x] AC2: `SCE_CONTROL_PLANE_BASE_URL` and the `control_plane_base_url` config key still override the new baked default according to the existing precedence contract. + - Validate: `nix flake check` passes the existing env/config precedence tests with the updated default expectation. +- [x] AC3: The config schema declaration remains `https://sce.crocoder.dev/config.json`, and Agent Trace conversation/session/trace URL construction remains unchanged. + - Validate: `nix run .#pkl-check-generated`; inspect the generated schema from `nix run .#pkl-generate -- "$(mktemp -d)"` and confirm its `$id`/`$schema` declaration remains the SCE web URL; `nix flake check` passes the existing Agent Trace URL coverage. +- [x] AC4: Durable CLI documentation distinguishes the control-plane base URL from the config schema and SCE web URL owners. + - Validate: inspect the updated config-precedence, trace-sync, and root architecture/context records for the two-URL contract. + +### Full validation + +- `nix flake check` +- `nix run .#pkl-check-generated` + +### Context sync + +- `context/cli/config-precedence-contract.md` — update the baked `control_plane_base_url` default while preserving the separate `$schema` URL. +- `context/cli/trace-command.md` — document the control-plane host used by `sce trace sync`. +- `context/cli/agent-trace-sync-command.md` — record the two-host boundary in the sync data flow. +- `context/overview.md` — distinguish the control-plane endpoint from the SCE web/schema URL. +- `context/architecture.md` — preserve `SCE_WEB_BASE_URL` ownership for web URLs while documenting the config-resolved control-plane owner. +- `context/glossary.md` — clarify the SCE web URL owner is not the sync control-plane default. + +## Constraints and non-goals + +- **In scope:** the baked `control_plane_base_url` default and its focused resolver/control-plane tests; documentation and durable context describing the split URL contract. +- **Out of scope:** changing the JSON schema URL, moving or republishing `https://sce.crocoder.dev/config.json`, changing Agent Trace conversation/session/trace URLs, changing WorkOS endpoints, changing ingestion route paths, or changing the control-plane server. +- **Constraints:** preserve `env > config file > baked default` precedence; keep the existing configurable base URL override; use the repository's Nix validation entrypoints; do not add a dependency. +- **Non-goal:** introducing a second runtime configuration key or a general URL registry for unrelated SCE web surfaces. + +## Assumptions + +- “Change to 2 URLs” means the production baked default should use `https://sce.crocoderlab.dev` for control-plane sync while the config schema and SCE web links remain on `https://sce.crocoder.dev`, as established in the preceding discussion. +- The existing `control_plane_base_url` setting is the intended configuration seam; only its default and contract documentation need to change. + +## Task stack + +- [x] T01: `Route Agent Trace sync through the dedicated control-plane host` (status:done) + - Task ID: T01 + - Goal: Change the baked control-plane default to `https://sce.crocoderlab.dev`, preserve all existing overrides and web/schema URL ownership, update focused tests and durable documentation, and leave ingestion paths/authentication unchanged. + - Boundaries (in/out of scope): In — `cli/src/services/config/resolver.rs`, related config/control-plane tests, and the context files listed under Context sync. Out — schema publication, SCE web URL construction, WorkOS endpoint changes, ingestion API changes, and generated target trees. + - Dependencies: none + - Done when: the default sync client resolves the dedicated control-plane host; env/config overrides continue to win; the schema remains identified by `https://sce.crocoder.dev/config.json`; existing Agent Trace web URLs are unchanged; focused tests and documentation reflect the split. + - Verification notes (commands or checks): `nix flake check`; `nix run .#pkl-check-generated`; inspect a fresh generated schema and the relevant URL-owner/context records. + - Completed: 2026-08-11 + - Files changed: `cli/src/services/config/resolver.rs` + - Evidence: Updated `CONTROL_PLANE_BASE_URL_BAKED_DEFAULT` to `https://sce.crocoderlab.dev`; existing resolver tests continue to cover baked default, config-file override, and environment-over-config precedence. The sync client continues to compose the unchanged `/agent-trace/ingestion/state` and `/agent-trace/ingestion/batch` routes from the resolved base URL, while `SCE_WEB_BASE_URL` and the generated schema remain unchanged. + - Verification: `nix flake check` passed; `nix run .#pkl-check-generated` passed with 101 files; fresh `nix run .#pkl-generate -- ` output retained `$id` and `$schema` declaration `https://sce.crocoder.dev/config.json`. + +## Open questions + +None. The requested two-host split and preservation of the existing schema URL are explicit. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-11 + +### Commands run + +- `nix flake check` -> exit 0 (all flake checks passed, including CLI tests, Clippy, formatting, and generated-output checks) +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral Pkl generation passed with 101 files) +- `nix run .#pkl-generate -- "$(mktemp -d)"` plus broad all-file schema-URL scan -> exit 1 (exploratory scan included non-schema generated files; no product failure) +- `nix run .#pkl-generate -- "$(mktemp -d)"` plus schema-only `$id`/`$schema` assertions -> exit 0 (both declarations remain `https://sce.crocoder.dev/config.json`) + +### Scaffolding removed + +- None. + +### Success-criteria verification + +- [x] AC1: With no `control_plane_base_url` override, `sce trace sync` resolves `https://sce.crocoderlab.dev` as its control-plane base and targets `POST /agent-trace/ingestion/state` and `POST /agent-trace/ingestion/batch` without changing request paths or authentication behavior -> `nix flake check` passed control-plane client/config resolver coverage; source inspection confirmed the unchanged routes and auth flow. +- [x] AC2: `SCE_CONTROL_PLANE_BASE_URL` and the `control_plane_base_url` config key still override the new baked default according to the existing precedence contract -> `nix flake check` passed the resolver precedence tests. +- [x] AC3: The config schema declaration remains `https://sce.crocoder.dev/config.json`, and Agent Trace conversation/session/trace URL construction remains unchanged -> generated schema `$id`/`$schema` assertions passed; `nix run .#pkl-check-generated` and `nix flake check` passed. +- [x] AC4: Durable CLI documentation distinguishes the control-plane base URL from the config schema and SCE web URL owners -> inspected the updated config-precedence, trace-sync, trace-command, overview, architecture, glossary, and context-map records. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified.