From 92b09c0f2a43ff8ba74cc877e1530c8c9bf72821 Mon Sep 17 00:00:00 2001 From: Zeph Gillen Date: Sat, 11 Jul 2026 10:45:42 +0200 Subject: [PATCH] docs(fork): add FORK.md upstream-merge friction map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture the fork's divergence from upstream in one reference so the conflict-prone code paths are clear on every sync. - FORK.md: divergence-at-a-glance + per-feature friction tables keyed by the actual fork file changes (git log --no-merges upstream/main.. local/daily-driver), with a conflict-risk column grounded in real upstream churn (6-month commit counts per file), fork-only vs modified split, recurring release-mechanics conflicts, a post-merge checklist, and the commands to regenerate the map. - AGENTS.md: trim "Local fork status" to a pointer at FORK.md; relocate the operational nuggets (pnpm vsix --force rebuild, 22-failure test baseline, per-provider reasoning guards) into FORK.md so nothing is lost. FORK.md is a file upstream doesn't have, so it never conflicts on merge — unlike AGENTS.md, which upstream actively edits. --- AGENTS.md | 28 +---------- FORK.md | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 FORK.md diff --git a/AGENTS.md b/AGENTS.md index 218f841591..f291691537 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,33 +4,9 @@ This file provides guidance to agents when working with code in this repository. ## Local fork status -This repo is a long-running fork of [`Zoo-Code-Org/Zoo-Code`](https://github.com/Zoo-Code-Org/Zoo-Code) (git remote `upstream`). Day-to-day work happens on `local/daily-driver`; upstream tags are merged in on `local/merge-upstream-vX.Y.Z` branches and then fast-forwarded into `local/daily-driver`. The local-only architectural changes below are intentional and must be preserved across upstream merges. +This repo is a long-running fork of [`Zoo-Code-Org/Zoo-Code`](https://github.com/Zoo-Code-Org/Zoo-Code) (git remote `upstream`). Day-to-day work happens on `local/daily-driver`; upstream tags are merged in on `local/merge-upstream-vX.Y.Z` branches and then fast-forwarded into `local/daily-driver`. -After bumping the version in `src/package.json` (e.g. during an upstream merge), rebuild with `pnpm vsix --force` — turbo's cache does not track `src/package.json` as an input for the webview build, so the webview bundle will otherwise keep the old version inlined. - -### Effort-based Anthropic reasoning (local-only architecture) - -Anthropic Opus 4.6 / 4.7 / 4.8 use Anthropic's adaptive-thinking API with `output_config.effort`, not the legacy `budget_tokens` / binary toggle. The fork's static registry shape for these models is the contract that drives the request payload: - -- `packages/types/src/providers/anthropic.ts` — Opus 4.6 / 4.7 / 4.8 declare `supportsReasoningEffort: [...]` (with `xhigh` only on 4.7 and 4.8 per Anthropic docs), `requiredReasoningEffort: true`, and `supportsTemperature: false`. They do NOT declare `supportsReasoningBudget`. -- `src/api/transform/reasoning.ts` — `getAnthropicReasoning` branches on `supportsReasoningEffort && !supportsReasoningBudget` and emits `{ thinking: { type: "adaptive" }, output_config: { effort } }`. Anything that re-declares `supportsReasoningBudget` on these models drops them onto the legacy budget path, which Opus 4.7+ rejects with a 400. -- `src/api/providers/anthropic.ts` and `src/api/providers/anthropic-vertex.ts` — destructure `reasoning?.thinking` and `reasoning?.output_config` from `getModel()` and spread both into the request. `getModelParams({ format: "anthropic" })` already wires reasoning; do NOT add a redundant `getAnthropicProviderReasoning` call. - -Provider-specific notes: - -- **Vertex** has no provider-side adaptive-thinking guard. The registry shape alone decides the payload. Opus 4.8 has been converted to the effort shape; **Opus 4.7 / 4.6 still declare `supportsReasoningBudget` and will 400 on the live API for 4.7 — known follow-up from `fd93c5bde`.** Do not "fix" the Bedrock-style mismatch by reintroducing the budget shape on Vertex 4.8. -- **Bedrock** has its own `isAdaptiveThinkingModel(modelId)` guard in `src/api/providers/bedrock.ts` that overrides the request payload regardless of the registry shape (matches `opus-4-7`, `opus-4-8`, `sonnet-4-7`, `sonnet-4-8` after `parseBaseModelId`). The bedrock registry entries declare the upstream-style `supportsReasoningBudget` shape — that is intentional and correct here. -- **OpenRouter** uses a dynamic model fetcher that reports `supportsReasoningEffort` as a boolean. The fetcher (`src/api/providers/fetchers/openrouter.ts`) patches known IDs (`anthropic/claude-opus-4.7`, `anthropic/claude-opus-4.8`, OpenAI gpt-5.5 family) to mirror the static effort arrays so `xhigh` / `max` remain reachable from the UI. - -When adding a new effort-capable model, mirror the existing 4.7 / 4.8 entries and add a parametrized test in `src/api/providers/__tests__/anthropic.spec.ts` (effort assertion, `requiredReasoningEffort` always-on, user-chosen effort) rather than the upstream-style budget/binary assertions. - -### Workspace-scoped code-index config (local-only) - -`src/services/code-index/` has been extended with a two-scope config model: global (extension settings) plus a per-project `.roo/codebase-index.json` dotfile. Project scope overrides global; secrets are reconciled through `config-resolver.ts` and a file watcher reloads on dotfile changes. The published JSON Schema is at `packages/types/schemas/codebase-index.schema.json`; the UI surfaces the active scope and a "pinned-by-dotfile" badge. Keep the schema, the resolver tests, and the dotfile loader in sync when changing the shape. - -### Known pre-existing test failures - -`pnpm -w test` reports 22 failures on `local/daily-driver` from fixture drift introduced by `fd93c5bde`, all in `src/api/transform/__tests__/reasoning.spec.ts` and `src/api/transform/__tests__/model-params.spec.ts`. They predate every recent upstream merge. A clean validation pass on this fork = exactly those 22 failures and nothing else; a 23rd is the regression to investigate. +**Before an upstream sync, read [`FORK.md`](FORK.md).** It is the single record of the local-only changes — they are intentional and must be preserved across merges — and maps each one to the files it touches, rates upstream-merge conflict risk (grounded in real upstream churn), and carries the post-merge checklist: the `pnpm vsix --force` rebuild after a version bump, the known 22-failure test baseline (fixture drift from `fd93c5bde`), the effort-based Anthropic reasoning contract, and the per-provider (Vertex / Bedrock / OpenRouter) reasoning guards. ## Conventions diff --git a/FORK.md b/FORK.md new file mode 100644 index 0000000000..6690d6ac70 --- /dev/null +++ b/FORK.md @@ -0,0 +1,141 @@ +# FORK.md — local divergence & upstream-merge friction map + +This repo is a long-running fork of [`Zoo-Code-Org/Zoo-Code`](https://github.com/Zoo-Code-Org/Zoo-Code) +(git remote `upstream`). Day-to-day work happens on `local/daily-driver`; upstream tags are +merged in on `local/merge-upstream-vX.Y.Z` branches and then fast-forwarded into +`local/daily-driver`. + +This document is the single reference for **what we changed and where it will rub against +upstream on the next sync**. The local-only changes below are intentional and must be preserved +across merges. `AGENTS.md` only points here. + +## How to use this during an upstream sync + +1. Merge the upstream tag onto a `local/merge-upstream-vX.Y.Z` branch. +2. Resolve conflicts feature-by-feature using the tables below — the **conflict-prone** files are + where merge markers land; the **fork-only** files won't conflict textually but may need + re-wiring if upstream restructured the surrounding code. +3. Run the [post-merge checklist](#post-merge-checklist). +4. Fast-forward `local/daily-driver`. + +> **Limitation:** the file lists here come from our fork's *non-merge* commits +> (`git log --no-merges upstream/main..local/daily-driver`). Conflicts that were already resolved +> *inside* past merge commits are not captured. When a new area starts conflicting, add a row. + +## Divergence at a glance + +| # | Local feature | Origin commit(s) | Nature | +|---|---|---|---| +| 1 | Effort-based Anthropic reasoning (Opus 4.6/4.7/4.8) | `fd93c5bde`, `64fc5fc98` | modifies shared provider logic | +| 2 | OpenRouter effort-array mirroring + gpt-5.5 defs | `062657a7d`, `64fc5fc98` | modifies shared fetcher/registry | +| 3 | Claude Fable 5 + safety-refusal handling | `811b5ca55` | modifies shared provider logic | +| 4 | `"max"` reasoningEffort i18n label | `dd675fd3b` | mechanical i18n | +| 5 | Workspace-scoped code-index config (`.roo/codebase-index.json`) | `3efa0728e`→`8f54e2274` (phases 1–5) | mostly new files + isolated wiring | + +## Conflict-prone code paths (shared files we modified) + +Risk = **upstream churn** (commits touching the file on `upstream/main` in the last 6 months) × +**change nature** (isolated additive wiring merges cleanly; changes to shared *logic* conflict +hard). High churn with an isolated add is usually a clean 3-way merge; low churn on keystone logic +can still be the ugliest conflict. + +### Feature 1+3 — Anthropic reasoning + Fable + +| File | Upstream churn (6mo) | Our change | Change nature | Risk | +|---|---:|---|---|---| +| `packages/types/src/providers/anthropic.ts` | 10 | Effort-shape model entries (Opus 4.6/4.7/4.8, Fable 5): `supportsReasoningEffort`, `requiredReasoningEffort`, `supportsTemperature:false`, **no** `supportsReasoningBudget` | modifies contract registry | **HIGH** (this shape drives the request payload) | +| `src/api/transform/reasoning.ts` | 5 | `getAnthropicReasoning` branch on `supportsReasoningEffort && !supportsReasoningBudget` → `{ thinking:{type:"adaptive"}, output_config:{effort} }` | keystone logic | **HIGH** (low churn but ugliest if upstream refactors reasoning extraction) | +| `src/api/providers/anthropic.ts` | 23 | Spread `reasoning.thinking` + `reasoning.output_config` into request; Fable `stop_reason:"refusal"` → category-aware text chunk | modifies shared logic | **HIGH** | +| `src/api/providers/anthropic-vertex.ts` | 17 | Destructure + spread `reasoning.thinking`/`output_config`; no provider-side adaptive guard (registry shape alone decides payload) | modifies shared logic | **MED** | +| `packages/types/src/provider-settings.ts` | 17 | Effort field/enum plumbing | modifies shared types | **MED** | +| `src/shared/api.ts` | 12 | Effort plumbing | modifies shared types | **MED** | +| `packages/types/src/model.ts` | 4 | Effort type support | modifies shared types | **LOW–MED** | + +**Provider guards to preserve (semantic, easy to break on merge):** +- **Vertex** has *no* provider-side adaptive-thinking guard — the registry shape alone decides the + payload. Opus 4.8 is on the effort shape; **Opus 4.7/4.6 still declare `supportsReasoningBudget` + and will 400 on the live API for 4.7 — known follow-up from `fd93c5bde`.** Do not "fix" this by + reintroducing the budget shape on Vertex 4.8. +- **Bedrock** has its own `isAdaptiveThinkingModel(modelId)` guard in + `src/api/providers/bedrock.ts` (matches `opus-4-7`, `opus-4-8`, `sonnet-4-7`, `sonnet-4-8` after + `parseBaseModelId`) that overrides the payload regardless of registry shape. Bedrock registry + entries keep the upstream-style `supportsReasoningBudget` shape — intentional and correct there. +- Anything that re-declares `supportsReasoningBudget` on the effort models drops them onto the + legacy budget path, which Opus 4.7+ rejects with a 400. + +When adding a new effort-capable model, mirror the existing 4.7/4.8 entries and add a parametrized +test in `src/api/providers/__tests__/anthropic.spec.ts` (effort assertion, `requiredReasoningEffort` +always-on, user-chosen effort) rather than upstream-style budget/binary assertions. + +### Feature 2 — OpenRouter / OpenAI effort + +| File | Upstream churn (6mo) | Our change | Change nature | Risk | +|---|---:|---|---|---| +| `packages/types/src/providers/openai.ts` | 8 | gpt-5.5 defs + static effort arrays | modifies registry | **MED** | +| `src/api/providers/fetchers/openrouter.ts` | 6 | Dynamic fetcher patches known IDs (`anthropic/claude-opus-4.7`, `anthropic/claude-opus-4.8`, gpt-5.5 family) to mirror the static effort arrays so `xhigh`/`max` stay reachable from the UI | modifies shared fetcher | **MED** | + +### Feature 5 — Workspace-scoped code-index (modified shared files) + +| File | Upstream churn (6mo) | Our change | Change nature | Risk | +|---|---:|---|---|---| +| `src/core/webview/ClineProvider.ts` | 81 | Wire code-index scope | isolated additive | **MED** (high churn, but additive — usually clean 3-way) | +| `src/core/webview/webviewMessageHandler.ts` | 59 | Code-index scope message handlers | isolated additive | **MED** | +| `packages/types/src/vscode-extension-host.ts` | 52 | Code-index host type | isolated additive | **MED** | +| `webview-ui/src/components/chat/CodeIndexPopover.tsx` | 2 | Scope switcher + pinned-by-dotfile badge | UI additive | **LOW–MED** | +| `src/services/code-index/manager.ts` | 2 | Two-scope wiring | our changes dominate | **LOW–MED** | +| `src/services/code-index/config-manager.ts` | 1 | Two-scope config model | our changes dominate | **LOW** | +| `packages/types/src/codebase-index.ts` | 1 | Config shape/types | our changes dominate | **LOW** | + +### i18n + +| Files | Our change | Risk | +|---|---|---| +| `webview-ui/src/i18n/locales/*/settings.json` (18 locales) | `"max"` reasoningEffort label + code-index scope strings | **MED** — frequent but mechanical; resolve by taking both sides / regenerating | + +## Fork-only files (added — no textual conflict, watch for semantic drift) + +These don't exist upstream, so they never produce merge markers. The risk is *drift*: if upstream +restructures the code-index service or the types package, these need re-wiring, not merging. + +- `packages/types/schemas/codebase-index.schema.json` — published JSON Schema for the dotfile +- `packages/types/src/__tests__/codebase-index.test.ts` +- `src/services/code-index/config-resolver.ts` (+ `__tests__/config-resolver.spec.ts`) — reconciles + global (extension settings) vs project (`.roo/codebase-index.json`) scope; secrets resolved here +- `src/services/code-index/dotfile-loader.ts` (+ `__tests__/dotfile-loader.spec.ts`) — loads the + dotfile and watches it for reloads + +Keep the schema, the resolver tests, and the dotfile loader in sync when the config shape changes. + +## Recurring / expected conflicts (release mechanics) + +These conflict on essentially **every** upstream merge and are expected — resolve mechanically, +don't investigate them as regressions. + +- `src/package.json` — version string → re-bump (see checklist re: VSIX) +- `CHANGELOG.md` — take the union; keep our fork entries +- `README.md`, `locales/*/README.md`, `webview-ui/src/i18n/locales/*/chat.json` — release/marketing churn +- `AGENTS.md` — now just a pointer paragraph, so the footprint is small + +## Post-merge checklist + +- [ ] **Rebuild VSIX after any `src/package.json` version bump:** `pnpm vsix --force`. Turbo's cache + does not track `src/package.json` as an input for the webview build, so the webview bundle + otherwise keeps the old version inlined. +- [ ] **Test baseline:** `pnpm -w test` reports **exactly 22 failures** on `local/daily-driver`, + all in `src/api/transform/__tests__/reasoning.spec.ts` and + `src/api/transform/__tests__/model-params.spec.ts` (fixture drift from `fd93c5bde`, predates + every recent merge). A clean run = those 22 and nothing else; a 23rd is the regression to + investigate. +- [ ] Verify the Anthropic effort payload still emits `output_config.effort` (not `budget_tokens`) + for Opus 4.7/4.8 — see the provider guards above. + +## Regenerate this map + +```bash +# Files our fork's non-merge commits touch (the divergence set): +git log --no-merges --name-status \ + --pretty=format:'>>> %h | %an | %s' upstream/main..local/daily-driver + +# Upstream churn (6mo) for a given file — feeds the risk column: +git log --oneline --since='6 months ago' upstream/main -- | wc -l +```