Pārlūkot izejas kodu

docs: remove completed implementation plans

Kit Langton 1 dienu atpakaļ
vecāks
revīzija
6a69aa752f
2 mainītis faili ar 0 papildinājumiem un 424 dzēšanām
  1. 0 163
      plans/config-normalization.md
  2. 0 261
      plans/session-generate.md

+ 0 - 163
plans/config-normalization.md

@@ -1,163 +0,0 @@
-# Mixed V1/V2 Config Normalization Plan
-
-Status: **Implemented and verified**
-
-## Goal
-
-Replace whole-document V1/V2 detection with one config-domain compatibility pipeline. Supported V1 fields, native V2 fields, and practical mixtures of both should load without an unrelated legacy key changing how the rest of the document is decoded.
-
-## Decision
-
-Normalize recognized fields independently into the encoded side of the V2 `Config.Info` schema, then perform one final complete-document V2 decode:
-
-```text
-JSON/JSONC encoded input
-    -> parse and retain source-property presence
-    -> validate each recognized field or collection entry
-    -> migrate supported V1 candidates to V2 encoded values
-    -> decode and re-encode native V2 candidates
-    -> merge with native V2 precedence
-    -> decode Config.Info once
-    -> log redacted diagnostics
-```
-
-There is no whole-document version classification and no independent whole-document V1 and V2 decode.
-
-The encoded boundary matters because schemas such as warming durations transform strings into runtime values. Decoded values must not be fed back into the encoded side of `Config.Info`.
-
-## Behavior
-
-| Situation                                         | Result                                              |
-| ------------------------------------------------- | --------------------------------------------------- |
-| Supported V1-only field                           | Migrate it to its canonical V2 destination.         |
-| Native V2 field                                   | Preserve it after schema decode and encode.         |
-| Disjoint V1 and V2 map entries                    | Preserve both.                                      |
-| Same canonical scalar, map entry, or nested leaf  | Valid native V2 wins regardless of JSON key order.  |
-| Malformed native value with valid legacy fallback | Skip native value, log it, and retain legacy value. |
-| Malformed collection entry                        | Skip only the explicitly supported recovery unit.   |
-| Unsupported accepted V1 setting                   | Omit it and log a redacted warning.                 |
-| Unknown field                                     | Continue ignoring it for forward compatibility.     |
-
-Valid supported V1 syntax does not warn merely because it is legacy.
-
-## Field Precedence
-
-| Destination              | Lowest to highest precedence                                             |
-| ------------------------ | ------------------------------------------------------------------------ |
-| `snapshots`              | `snapshot` < `snapshots`                                                 |
-| `share`                  | `autoshare` < `share`                                                    |
-| `references[name]`       | `reference[name]` < `references[name]`                                   |
-| `agents[name]`           | `agent[name]` < `mode[name]` < `agents[name]`                            |
-| `commands[name]`         | `command[name]` < `commands[name]`                                       |
-| `providers[name]`        | `provider[name]` < `providers[name]`                                     |
-| `permissions`            | `tools` rules < `permission` rules < native `permissions`                |
-| `plugins`                | migrated `plugin` items < native `plugins` items                         |
-| `media`                  | `attachment` < `media`                                                   |
-| `experimental.policies`  | enabled-provider policies < disabled-provider policies < native policies |
-| `mcp.servers[name]`      | direct legacy server < native `servers[name]`                            |
-| `mcp.timeout.*`          | `experimental.mcp_timeout` < native timeout leaf                         |
-| `compaction.keep.tokens` | `preserve_recent_tokens` < `keep.tokens`                                 |
-| `compaction.buffer`      | `reserved` < `buffer`                                                    |
-
-Ordered rules and plugin directives retain both forms, with migrated V1 entries first and native V2 entries last.
-
-## Shared Shapes
-
-### Skills
-
-- A V2 array retains each valid string item.
-- A V1 object combines valid `paths` followed by valid `urls`.
-- Empty and unknown-only V1 objects normalize to an empty array under permissive excess-property handling.
-
-### MCP
-
-- Direct entries under `mcp` are V1 servers.
-- Entries under `mcp.servers` are native V2 servers.
-- Both sets are merged by server name, with a complete native server replacing a duplicate legacy server.
-- A malformed native duplicate is skipped so a valid legacy server remains.
-- Native global timeout leaves override only matching values migrated from `experimental.mcp_timeout`.
-- Raw `type` and `enabled` discriminators preserve legacy servers that happen to be named `servers` or `timeout`.
-
-### Compaction
-
-- `preserve_recent_tokens` becomes `keep.tokens`.
-- `reserved` becomes `buffer`.
-- Native leaves win conflicts.
-- `tail_turns` and `prune` remain unsupported and produce warnings.
-
-### Experimental
-
-- `subagent_depth` is shared.
-- Legacy provider lists generate ordered canonical policies.
-- Native policies follow generated policies.
-- An explicit empty `enabled_providers` keeps deny-all behavior.
-- A non-empty list with no valid items contributes no policy, avoiding accidental deny-all from malformed input.
-
-## Recovery Units
-
-Named commands, agents, providers, MCP servers, formatters, language servers, and references recover independently. Plugin, permission, skill, instruction, provider-ID, and policy arrays recover by item. Top-level legacy permissions recover by action/resource rule. Complex interiors of one agent, provider, command, or MCP server remain atomic rather than being recursively salvaged.
-
-Every decoder preserves `propertyOrder: "original"` because V1 permission precedence depends on user order. Excess properties remain ignored except for the explicit unsupported inventory.
-
-## Provider IDs
-
-Provider ID compatibility remains a config migration concern only. Existing V1 agent, command, provider, and provider-policy adapters continue using the migration helper's retired-ID mapping.
-
-The shared top-level `model` field remains exact because its string and object forms are valid native V2 syntax and provider declarations may come from a different config layer. It is never reinterpreted based on unrelated legacy fields.
-
-This change does not add runtime provider aliases or modify provider policy evaluation, catalog state, model resolution, Sessions, plugins, Server behavior, or generation.
-
-## Diagnostics
-
-Diagnostics contain only source, JSON path, category, and action. They never include raw values because config may contain credentials after substitution.
-
-Malformed JSON, empty content, and valid non-object roots reject one document with a source-aware warning. Malformed recognized fields and entries are skipped at their recovery boundary while unrelated valid configuration continues loading.
-
-## Implementation
-
-- Add a pure `ConfigNormalize.normalize` module under `packages/core/src/config/`.
-- Reuse field migration primitives from `packages/core/src/v1/config/migrate.ts`.
-- Replace `ConfigMigrateV1.isV1` in `packages/core/src/config.ts` with normalization and one final V2 decode.
-- Log diagnostics uniformly for files, `OPENCODE_CONFIG_CONTENT`, and well-known virtual config.
-- Add property and table-driven config normalization tests.
-- Update migration and compaction documentation.
-
-## Verification
-
-The implementation must establish:
-
-1. Valid native V2 config preserves decoded meaning after encoded normalization.
-2. Supported V1 fields preserve existing behavior.
-3. Adding a legacy field cannot change unrelated native field interpretation.
-4. Native V2 wins canonical conflicts independent of key order.
-5. One malformed entry does not remove valid siblings.
-6. Mixed MCP, compaction, and experimental values normalize deterministically.
-7. Diagnostics are precise and value-redacted.
-8. False, zero, empty, and absent values retain distinct presence semantics.
-
-Run from `packages/core`:
-
-```sh
-bun test test/config
-bun typecheck
-```
-
-Run from `packages/www` after documentation changes:
-
-```sh
-bun typecheck
-bun validate
-bun run build
-```
-
-## Non-Goals
-
-- Runtime provider alias resolution.
-- Provider policy or catalog changes.
-- Model resolver or Session changes.
-- Plugin API changes.
-- Server or Protocol changes.
-- Generation lifecycle changes.
-- Recursive V1/V2 inference inside one agent, provider, command, or model.
-- Restoring removed V1 functionality.
-- Rewriting user files on disk.

+ 0 - 261
plans/session-generate.md

@@ -1,261 +0,0 @@
-# Session-Aware One-Shot Generation Plan
-
-Status: **In progress**
-
-## Decision
-
-Add a Session operation that prepares one request from the Session's active model context, appends a transient prompt, executes exactly one Physical Attempt, returns the assistant text, and leaves the Session unchanged:
-
-```ts
-const text = yield * session.generate({ sessionID, prompt: "Summarize where we left off." })
-```
-
-The operation belongs to Session because its meaning depends on Session History, the selected agent and model, instructions, provider transforms, hooks, and prompt-cache identity. The existing stateless `Generate.text` module remains unaware of Session.
-
-This feature should deepen the Session request-preparation module rather than add a second approximation of the runner. The durable runner and `session.generate` must share preparation, then diverge before provider output acquires durable consequences.
-
-## Why The Current Shape Resists This Operation
-
-`SessionRunner.attemptStep` currently interleaves six distinct concerns:
-
-1. It synchronizes instructions and promotes pending inputs.
-2. It resolves the Session's agent and model.
-3. It selects active Session History and initiates compaction when required.
-4. It constructs the provider request, materializes tools, and applies Session hooks.
-5. It performs one Physical Attempt.
-6. It projects assistant output, executes tools, records usage, and decides whether to continue.
-
-`session.generate` needs the middle of that sequence without the durable work on either side. Calling `session.prompt` would admit durable input and enter the full loop. Calling `Generate.text` would lose Session context and cache identity. Forking would preserve context but create temporary durable state and cleanup obligations.
-
-The desired architecture makes request preparation independently callable while preserving one canonical implementation.
-
-## Target Architecture
-
-```text
-session.prompt
-    -> SessionAdmission
-    -> SessionExecution
-    -> SessionContext.select/load
-    -> SessionModelRequest.prepare
-    -> LLMClient.stream
-    -> SessionSettlement
-
-session.generate
-    -> SessionContext.select
-    -> SessionHistory.preview
-    -> SessionGenerate request construction
-    -> LLMClient.generate
-    -> return text
-```
-
-The modules have distinct jobs:
-
-- `SessionAdmission` records and promotes durable input.
-- `SessionContext` resolves a read-only, internally consistent view of what the selected agent would see.
-- `SessionModelRequest` converts durable Step context into a provider request paired with tool capability.
-- `SessionGenerate` owns the one tool-free transient request shape rather than threading generation through the durable modules.
-- `LLMClient` executes one provider request without deciding what becomes durable.
-- `SessionSettlement` gives streamed provider events their durable Session meaning, executes local tools, records usage, and decides continuation.
-- `SessionCompaction` replaces oversized active history and remains a durable Session operation.
-
-Durability becomes a property of admission and settlement, not request preparation or provider execution.
-
-## The Core Seam
-
-The Location-scoped request module keeps its durable Step interface:
-
-```ts
-interface SessionModelRequest {
-  readonly prepare: (input: { context: SessionContext.Loaded; step: number }) => Effect<PreparedSessionModelRequest>
-}
-```
-
-```ts
-type PreparedSessionModelRequest = {
-  request: LLM.Request
-  resolveToolCall: (name: string) => ToolCallResolution
-}
-```
-
-`prepare` hides:
-
-- Session and Location validation;
-- plugin flush and selected-agent resolution;
-- selected-model and credential resolution;
-- instruction source loading and assembly;
-- active-history selection after the latest completed compaction;
-- conversion to provider messages;
-- provider system prompts and model headers;
-- tool permission filtering and definition materialization;
-- provider transforms and Session context hooks;
-- Session-based prompt-cache identity.
-
-Durable `prepare` advertises permitted tools and returns the capability used by settlement, except when the agent's Step limit disables tools. `SessionGenerate` constructs its request with no tool definitions and tool choice `none`, independently of agent permissions. Avoid a generic `step | generate | compaction` operation union or independently selectable behavior flags; the two operations own their concrete request shapes.
-
-## Read-Only Session Context
-
-Request preparation must not call `InstructionState.prepare`, promote pending input, or initiate compaction. Those operations mutate the Session.
-
-Split instruction behavior into two phases:
-
-```text
-resolve and assemble current instruction context   read-only
-commit the canonical model's instruction state    durable
-```
-
-Both a durable Step and `session.generate` resolve and assemble instructions. Only durable execution commits instruction-state changes. A transient request must not make the false durable claim that the canonical Session model saw an instruction update.
-
-The active history and committed instruction state are loaded from one consistent database view. A concurrent durable Step may advance the Session afterward; the already prepared request remains immutable. No reusable snapshot or revision protocol is needed for this operation.
-
-Pending inputs remain excluded. They are not Session History until promotion, and `session.generate` must not alter admission order or expose queued work early.
-
-If the Session currently has an unsettled assistant message, generation stops history at that boundary. In particular, it never appends the transient user prompt after an unresolved tool call.
-
-## One Physical Attempt Without Settlement
-
-Use the existing `LLMClient` interface directly. The durable runner consumes `llm.stream(prepared.request)`, while `session.generate` calls `llm.generate(prepared.request)` to collect the same event stream into its existing `LLMResponse` model. No additional provider-attempt module is needed.
-
-`LLMClient.generate` collects exactly one provider stream. It does not retry as a new logical Step, execute tools, continue after tool calls, publish Session events, capture filesystem snapshots, or update Session usage.
-
-The internal result is the collected text string. Keeping richer evidence internal avoids prematurely committing a public transport contract.
-
-If a provider returns tool calls, collection records and ignores them. No tool hook or execution path runs. Assistant text, including an empty string, remains a successful result. Empty text is required for cache-warming calls.
-
-## Compaction Does Not Belong In The First Operation
-
-Normal Step preparation may discover that active context requires compaction. Compaction is durable and usually requires another provider call. Automatically compacting from `session.generate` would violate both transcript immutability and the exactly-one-attempt contract.
-
-The first operation should use history after the latest completed compaction and fail with a typed context-overflow error when that snapshot cannot fit. It must not initiate compaction.
-
-Transient in-memory compaction can be considered later as a separate operation or explicit policy. It should not silently weaken the first contract.
-
-## Concurrency Uses Snapshot Semantics
-
-The first contract should state:
-
-> `session.generate` uses the latest committed model context captured when request preparation begins. Later Session changes do not alter the in-flight request.
-
-The operation does not acquire ownership of the durable Session Drain and does not fail merely because the Session is running. This keeps transient generation independent from durable scheduling.
-
-A later recap integration can suppress stale output by comparing the Session aggregate sequence captured by that caller. Core does not expose a generic revision protocol before a concrete consumer needs one.
-
-## Hooks Follow The Stage They Affect
-
-The existing Session context hook runs because it participates in normal request preparation. Admission, projection, Session settlement, and tool-execution hooks do not run because those stages do not occur. Add operation metadata only when a concrete hook consumer needs it; do not introduce a speculative operation union.
-
-The no-mutation guarantee covers OpenCode's durable Session state. Arbitrary plugin hooks may still perform external side effects.
-
-## Internal Contract
-
-Start with the smallest useful interface:
-
-```ts
-type SessionGenerateInput = {
-  sessionID: SessionID
-  prompt: string
-}
-```
-
-The operation:
-
-1. Resolves the Session or returns the normal Session-not-found error.
-2. Captures its latest committed active model context.
-3. Uses the selected agent, model, instructions, provider configuration, transforms, hooks, and Session cache key.
-4. Appends `prompt` only to the in-memory provider request.
-5. Executes exactly one Physical Attempt with tools disabled.
-6. Returns collected assistant text, including an empty string.
-7. Does not admit input, publish Session events, execute tools, initiate compaction, update usage, or mutate Session projections.
-
-Files, agent attachments, usage, model identity, finish metadata, and revision are follow-up extensions. They should be added only when a concrete caller needs them.
-
-## Implementation Sequence
-
-Each stage should preserve existing durable runner behavior before the next stage starts.
-
-### 1. Characterize Current Request Preparation
-
-Add focused tests around a normal Step's prepared request. Pin:
-
-- selected agent and model;
-- system and instruction assembly;
-- active history after compaction;
-- tool definitions and last-step behavior;
-- provider headers and prompt-cache key;
-- Session context-hook transformations.
-
-Use a recording LLM adapter rather than reproducing request-construction logic in tests.
-
-### 2. Extract Instruction Resolution From Durable Synchronization
-
-Move instruction source loading and read-only assembly behind one internal interface. Keep `InstructionState.prepare` in the durable runner path. Verify that normal Steps produce byte-equivalent instruction context and unchanged durable instruction events.
-
-This commit should not add `session.generate`.
-
-### 3. Extract Session Model Request Preparation
-
-Move model resolution, history selection, request construction, tool definitions, cache identity, and context hooks into the Location-scoped `SessionModelRequest` module. Make the durable runner its only caller first.
-
-Verify the recorded normal request before and after extraction. Keep compaction detection and pending promotion outside the new module if putting them inside would make preparation mutate state.
-
-### 4. Separate Provider Attempt From Durable Settlement
-
-Make the runner explicitly pass `llm.stream(prepared.request)` into durable settlement. Keep event publication, tool execution, snapshots, retries, usage, and continuation behavior unchanged.
-
-This stage should make the durable path read as orchestration:
-
-```ts
-promote -> snapshot -> compact if required -> prepare -> attempt -> settle
-```
-
-### 5. Add The Core `Session.generate` Operation
-
-Use read-only context and request preparation, append one transient user message, disable tools, collect one attempt, and return text. Add tests proving that messages, pending inputs, instruction state, Session events, and usage remain unchanged.
-
-Test concurrent Session advancement with deterministic synchronization around request dispatch. The generated request should retain its captured context while the source Session advances independently.
-
-### 6. Add Protocol, Server, Client, And Plugin Surfaces
-
-Add `POST /api/session/:sessionID/generate` to Protocol, a thin Server handler, generated Promise and Effect clients, and `ctx.session.generate` in the V2 plugin context. Regenerate clients from the assembled `HttpApi`; do not edit generated files manually.
-
-### 7. Build Recap As The First External Consumer
-
-Implement recap outside Core using `session.generate`. The recap integration owns idle/focus policy, the recap prompt, stale-result suppression, output cleaning, and display. Core owns only session-authentic transient generation.
-
-## Verification Laws
-
-The implementation is complete when tests establish these laws:
-
-1. **Request equivalence:** the durable runner's prepared request remains equivalent before and after extraction.
-2. **Transcript immutability:** `session.generate` leaves Session messages and pending inputs unchanged.
-3. **Instruction immutability:** transient generation does not advance instruction state or publish instruction events.
-4. **Single attempt:** one call produces exactly one `llm.generate` invocation and no continuation.
-5. **No tools:** transient requests advertise no tools and never reach tool settlement or tool hooks.
-6. **Cache identity:** normal Steps and transient generation use the same Session-derived prompt-cache key.
-7. **Hook parity:** Session request hooks see and may transform transient requests through the same preparation seam.
-8. **Empty success:** a provider response with no assistant text returns `""`.
-9. **Snapshot isolation:** concurrent Session advancement does not change an already prepared transient request.
-10. **No accounting mutation:** transient usage does not alter durable Session cost or token totals.
-
-## Rejected First Implementations
-
-### Prompt, Wait, And Read
-
-This path mutates Session History, enters the agent loop, may execute tools, and changes usage. Deleting projected messages afterward cannot undo durable events or external effects.
-
-### Durable Fork With Cleanup
-
-A fork is useful for a prototype but creates durable state, emits events, requires reliable deletion, and can execute tools unless the normal runner is changed anyway. It does not establish the reusable preparation seam.
-
-### Session Calling Stateless Generate
-
-`Generate.text` intentionally knows nothing about Session. Teaching it Session semantics reverses the intended dependency and duplicates request preparation outside the runner.
-
-### A General `persist: false` Runner Mode
-
-Persistence, tool execution, admission, compaction, and continuation are separate behaviors. One flag would leave callers responsible for understanding unsafe combinations and would keep the current concerns interleaved.
-
-## Expected Scope
-
-The narrow implementation is expected to touch Core Session and runner modules, Protocol, Server, generated clients, and the V2 plugin context. It should require no database migration and no new durable event.
-
-The architectural work is larger than the endpoint. Most risk lies in extracting instruction and request preparation without changing normal Step behavior. The staged sequence keeps that risk observable and gives every new seam two real callers before broadening its interface.