From b1d68a023e5c53a28a81dee93877b3ced76c6648 Mon Sep 17 00:00:00 2001 From: Travis Gilbert <1travisgilbert@gmail.com> Date: Wed, 5 Aug 2026 17:09:21 -0400 Subject: [PATCH] Retire Studio Copilot product host and repair sticky empty checkouts. Delete defaultChatAgent/voiceWsUrl via overlay + patch 0003 null-checks, strip extensions/copilot, seed chat.disableAIFeatures, and reseed empty git-init volumes from WORKSPACE_REPO + THEOREM_GIT_TOKEN. Adds the theorem-chat-register plan board for the Studio+/chat register follow-through. --- ...m-chat-register--plan-local-20260805a.json | 197 ++++++++++ .../FOLLOW-UP-CODE-SERVE-WEB.md | 22 +- docs/plans/theorem-chat-register/PLAN.md | 109 ++++++ .../SPEC-THEOREM-CHAT-REGISTER-1.0.md | 46 +++ packaging/commonplace-studio/LEDGER.md | 63 +++- ...03-retire-default-chat-agent-copilot.patch | 350 ++++++++++++++++++ .../commonplace-studio/patches/README.md | 5 +- .../commonplace-studio/product.overlay.json | 9 +- packaging/commonplace-studio/scripts/build.sh | 24 +- .../commonplace-studio/scripts/ledger-gate.sh | 6 + .../scripts/smoke-server.sh | 18 +- packaging/workspace/entrypoint.sh | 46 ++- packaging/workspace/railway.toml | 9 +- 13 files changed, 878 insertions(+), 26 deletions(-) create mode 100644 .harness/checklists/theorem-chat-register--plan-local-20260805a.json create mode 100644 docs/plans/theorem-chat-register/PLAN.md create mode 100644 docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md create mode 100644 packaging/commonplace-studio/patches/0003-retire-default-chat-agent-copilot.patch diff --git a/.harness/checklists/theorem-chat-register--plan-local-20260805a.json b/.harness/checklists/theorem-chat-register--plan-local-20260805a.json new file mode 100644 index 00000000..e47b17d2 --- /dev/null +++ b/.harness/checklists/theorem-chat-register--plan-local-20260805a.json @@ -0,0 +1,197 @@ +{ + "schema_version": 1, + "plan_id": "plan-theorem-chat-register-20260805a", + "tenant_slug": "Travis-Gilbert", + "room_id": "room:ungrouped", + "workstream": "theorem-chat-register", + "created_at": "2026-08-05", + "planning_skill": "theorems-harness:planning-theorem", + "execution_skill": "theorems-harness:execute", + "substrate_status": "local_projection_only", + "substrate_note": "Harness plan create timed out / product plugin discovery 404 at planning time; import with plan import when MCP recovers.", + "sources": [ + "docs/plans/theorem-chat-register/PLAN.md", + "docs/plans/ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md", + "docs/plans/console/SPEC-COMMONPLACE-OPENWORK-FORK-1.0.md", + "docs/records/013-vscode-surface.md", + "feat/studio-overlay-retire-copilot" + ], + "deferrals": [ + { + "id": "D1", + "title": "Theorem as real defaultChatAgent", + "reason": "Requires Chat participant / host APIs over ACP; this plan retires Copilot and mounts the Theorem register UI instead.", + "consent": "surfaced_in_plan_20260805" + }, + { + "id": "D2", + "title": "Full OpenWork fork archive cleanup", + "reason": "CR-007 supersedes the product path; historical OW* specs remain as record until a dedicated archive pass.", + "consent": "surfaced_in_plan_20260805" + } + ], + "items": [ + { + "id": "CR-000", + "title": "Durable plan + checklist projection", + "status": "done", + "oracle_class": "artifact", + "implementation_mode": "docs", + "evidence_class": "artifact", + "substitution_allowed": false, + "live_oracle_required": false, + "acceptance": [ + "PLAN.md and checklist projection exist and name CR-001..007", + "plan_id plan-theorem-chat-register-20260805a recorded" + ], + "proof_command": "test -f docs/plans/theorem-chat-register/PLAN.md && test -f .harness/checklists/theorem-chat-register--plan-local-20260805a.json" + }, + { + "id": "CR-001", + "title": "Land Copilot retirement + redeploy Studio image", + "status": "pending", + "oracle_class": "live", + "implementation_mode": "product", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": true, + "grounding": [ + "packaging/commonplace-studio/product.overlay.json", + "packaging/commonplace-studio/patches/0003-retire-default-chat-agent-copilot.patch", + "packaging/workspace/entrypoint.sh", + "feat/studio-overlay-retire-copilot" + ], + "acceptance": [ + "Branch merged to main and workspace redeployed", + "Live product.json has no defaultChatAgent / voiceWsUrl Copilot host", + "extensions/copilot absent from studio-server", + "Signed-in /IDE walkthrough title is Commonplace Studio" + ], + "proof_command": "cd packaging/commonplace-studio && ./scripts/ledger-gate.sh" + }, + { + "id": "CR-002", + "title": "Repair sticky empty /workspace/repo", + "status": "pending", + "oracle_class": "live", + "implementation_mode": "product", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": true, + "grounding": [ + "packaging/workspace/entrypoint.sh" + ], + "acceptance": [ + "Entrypoint repairs or reclones empty sticky git init when WORKSPACE_REPO_URL is set", + "Live /workspace/repo has package.json and a resolvable HEAD", + "/IDE Explorer and /chat share that tree" + ], + "proof_command": "railway ssh --service commonplace-workspace -- 'test -f /workspace/repo/package.json && git -C /workspace/repo rev-parse HEAD'" + }, + { + "id": "CR-003", + "title": "Theorem chat register package contract", + "status": "pending", + "oracle_class": "artifact", + "implementation_mode": "docs", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": false, + "grounding": [ + "docs/plans/theorem-chat-register/PLAN.md", + "docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md" + ], + "acceptance": [ + "SPEC names package path, register_impl id, Studio seam, /chat seam", + "Non-goal: IDefaultChatAgent without Chat participant APIs", + "Retirement inventory lists openwork.chat, OpenWork server, LLM leftovers" + ], + "proof_command": "test -f docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md" + }, + { + "id": "CR-004", + "title": "Implement Theorem chat register package", + "status": "pending", + "oracle_class": "test", + "implementation_mode": "product", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": false, + "grounding": [ + "packages/", + "apps/theorem-vscode/", + "apps/console/" + ], + "acceptance": [ + "Package builds in the pnpm workspace", + "Tests cover session open and one turn", + "Happy path has no openworklabs or opencode product dependency" + ], + "proof_command": "pnpm --filter test" + }, + { + "id": "CR-005", + "title": "Mount register in Studio agent/chat panel", + "status": "pending", + "oracle_class": "live", + "implementation_mode": "product", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": true, + "grounding": [ + "apps/theorem-vscode/", + "packaging/commonplace-studio/" + ], + "acceptance": [ + "Signed-in /IDE shows Theorem register UI in the agent/chat region", + "ACP / theorem.startSession works against the shared checkout", + "No GitHub Copilot sign-in wall for chat" + ], + "proof_command": "manual signed-in /IDE session smoke + ACP prompt receipt" + }, + { + "id": "CR-006", + "title": "Mount register at /chat; retire OpenWork door", + "status": "pending", + "oracle_class": "live", + "implementation_mode": "product", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": true, + "grounding": [ + "apps/console/src/views/OpenworkChatRegister.tsx", + "apps/console/src/middleware.ts", + "apps/console/src/views/registry.tsx", + ".commonplace-canonical", + "packaging/workspace/" + ], + "acceptance": [ + "Live /chat stamp is the Theorem register_impl, not openwork.chat", + "OpenWork/opencode is not the product /chat host", + "doctor + register-manifest green" + ], + "proof_command": "node scripts/check-register-manifest.mjs && node scripts/doctor.mjs" + }, + { + "id": "CR-007", + "title": "Retire residual LLM / OpenWork debt + EXECUTE-REPORT", + "status": "pending", + "oracle_class": "artifact", + "implementation_mode": "docs_and_delete", + "evidence_class": "pending", + "substitution_allowed": false, + "live_oracle_required": false, + "grounding": [ + "docs/plans/theorem-chat-register/EXECUTE-REPORT.md", + "apps/chat/MODIFICATIONS.md", + "docs/plans/console/SPEC-COMMONPLACE-OPENWORK-FORK-1.0.md" + ], + "acceptance": [ + "EXECUTE-REPORT lists deletions and live stamps", + "No product route serves OpenWork as the chat register", + "OW / FOLLOW-UP docs note supersession" + ], + "proof_command": "test -f docs/plans/theorem-chat-register/EXECUTE-REPORT.md" + } + ] +} diff --git a/docs/plans/ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md b/docs/plans/ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md index 593ac094..9f0cf47c 100644 --- a/docs/plans/ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md +++ b/docs/plans/ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md @@ -1,15 +1,19 @@ # Follow-up: Commonplace Studio `code serve-web` (D1 / V7) Parent plan: [`PLAN.md`](./PLAN.md) · Execute: [`EXECUTE-REPORT.md`](./EXECUTE-REPORT.md) +**Successor plan (Copilot hole → Theorem register + OpenWork retirement):** [`../theorem-chat-register/PLAN.md`](../theorem-chat-register/PLAN.md) (`plan-theorem-chat-register-20260805a`). Canonical decision: [`docs/records/013-vscode-surface.md`](../../records/013-vscode-surface.md) · Pipeline: [`packaging/commonplace-studio/`](../../../packaging/commonplace-studio/) ## Status -**Unparked, building.** The entrypoint carries Studio as the default IDE host -(CS-004..CS-006, spec amendment A14). Stock code-server remains the rollback -behind `--build-arg BUILD_STUDIO_SERVER=0` and is still what the live deploy -runs until CS-007 (`IDE_HOST=code-server` on Railway). The door and pack were -always live; this follow-up swaps the **workbench binary**. +**CS-007 host live; Microsoft/Copilot retirement in flight.** Deploy +`c4636818` runs `IDE_HOST=studio`. Session smoke showed Commonplace Studio +chrome plus status-bar Theorem, but upstream Copilot product keys, the built-in +`extensions/copilot` tree, and “VS Code for the Web” walkthrough titles still +shipped. Branch `feat/studio-overlay-retire-copilot` deletes those product keys +(overlay null → remove), adds patch `0003` null-checks, strips the copilot +extension from the server artifact, seeds `chat.disableAIFeatures`, and retitles +the web walkthrough. Stock code-server remains the image rollback until CS-008. **2026-08-04 scar after #185.** Dropping `-min` was necessary but not sufficient. `gulpfile.reh.ts` starts every `vscode-reh-web-*` task with @@ -81,13 +85,13 @@ Authenticated `/IDE` serves Commonplace Studio’s web workbench (`code serve-we | ID | Task | Grounding | Proof | Status | |---|---|---|---|---| | CS-000 | Durable follow-up (this file) + link from parent plan | `FOLLOW-UP-CODE-SERVE-WEB.md`, checklist note | file exists | done | -| CS-001 | Clear disk floors; run `build.sh prepare` then the deployable target on pinned `UPSTREAM_TAG` | `packaging/commonplace-studio/scripts/build.sh`, `RUNBOOK.md` | server artifact; ledger-gate pass | **doing** (Railway). Mangler OOM cleared by patch 0002 (deploy `b1baa84f` got through compile/bundle). Next failure: vsce/`npm list` on staged `theorem-vscode` with `workspace:*` deps — ship manifest strips deps (dist already bundled) | -| CS-002 | Local smoke: the server boots; pack activates; OpenVSX/telemetry/identity checks | Studio RUNBOOK §5 web bullets | written smoke receipt | harness landed (`scripts/smoke-server.sh`, shellcheck clean); awaiting the CS-001 artifact to produce the receipt | -| CS-003 | OW5 amendment: workspace image replaces `code-server` install with Studio server output | `packaging/workspace/{Dockerfile,entrypoint.sh}`, Studio README | amendment text + `/opt/commonplace/studio-server/bin/commonplace-studio-server` present in the running container | **source done**; live proof still open. Stage gated on `BUILD_STUDIO_SERVER`, `COPY --from=studio-server` present. Last SUCCESS image is still `4e33d620` (no Studio). All later deploys FAILED through `7c2690ab` | +| CS-001 | Clear disk floors; run `build.sh prepare` then the deployable target on pinned `UPSTREAM_TAG` | `packaging/commonplace-studio/scripts/build.sh`, `RUNBOOK.md` | server artifact; ledger-gate pass | **done** (Railway `c4636818` built Studio; ship-pack + mangler scars closed) | +| CS-002 | Local smoke: the server boots; pack activates; OpenVSX/telemetry/identity checks | Studio RUNBOOK §5 web bullets | written smoke receipt | **expanded**: smoke now asserts no `defaultChatAgent` / Copilot endpoints / `extensions/copilot`. Full receipt awaits next Studio image | +| CS-003 | OW5 amendment: workspace image replaces `code-server` install with Studio server output | `packaging/workspace/{Dockerfile,entrypoint.sh}`, Studio README | amendment text + `/opt/commonplace/studio-server/bin/commonplace-studio-server` present in the running container | **done** (live on `c4636818`) | | CS-004 | Entrypoint: start the Studio server (host/port, user-data, extensions, proposed APIs) without stealing `$PORT` from OpenWork | today’s `env -u PORT` pattern for code-server | `/health` + IDE port respond; chat still on 8787 | **done** (host branch; `env -u PORT` kept; shellcheck clean) | | CS-005 | Edge proxy / register: keep `/IDE` path strip; rename or note register impl if product id changes | `edge-proxy.mjs`, `.commonplace-canonical`, `IdeRegister` | register-manifest + proxy tests | **done** (proxy unchanged by design; manifest notes the selectable host and defers the rename to CS-008) | | CS-006 | Preserve substrate env: bootstrap `editor.env`, `CONSOLE_EDITOR_SUBSTRATE_URL`, ACP vars | `bootstrap-editor-substrate.mjs`, Railway vars | doctor substrate green; pack GraphQL + SSE | **done** (one `ide_env` array both hosts pass identically) | -| CS-007 | Live cutover + authenticated `/IDE` smoke (session cookie) | Railway workspace + console | pack providers + one ACP prompt | pending (CS-002) | +| CS-007 | Live cutover + authenticated `/IDE` smoke (session cookie) | Railway workspace + console | pack providers + one ACP prompt | **host cutover done**; Copilot/Microsoft retirement pending redeploy of overlay+0003; ACP prompt still open | | CS-008 | Retire stock code-server from workspace image once Studio is proven; update EXECUTE-REPORT; rename `manifest_impl` and `REGISTER_IMPL` to `commonplace-studio.ide` | Dockerfile, EXECUTE-REPORT, `.commonplace-canonical`, `edge-proxy.mjs` | image no longer ships code-server binary as host | pending (CS-007) | ## Sequence diff --git a/docs/plans/theorem-chat-register/PLAN.md b/docs/plans/theorem-chat-register/PLAN.md new file mode 100644 index 00000000..867cb1cd --- /dev/null +++ b/docs/plans/theorem-chat-register/PLAN.md @@ -0,0 +1,109 @@ +# Planning-Theorem: Theorem chat register + Studio Copilot hole + +- **Plan id:** `plan-theorem-chat-register-20260805a` +- **Tenant:** `Travis-Gilbert` +- **Created:** 2026-08-05 +- **Parent scars:** [`ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md`](../ide-substrate-wire/FOLLOW-UP-CODE-SERVE-WEB.md) (CS-007 host live; Copilot product keys still present until CR-001), [`console/SPEC-COMMONPLACE-OPENWORK-FORK-1.0.md`](../console/SPEC-COMMONPLACE-OPENWORK-FORK-1.0.md) (openwork.chat is today's `/chat` register), [`docs/records/013-vscode-surface.md`](../../records/013-vscode-surface.md) (Theorem ships thin over ACP, not a third-party chat extension) +- **Harness:** `user-theorems-harness-local` `plan create` timed out this session; `plugin-theorems-harness` discovery 404. This file + `.harness/checklists/theorem-chat-register--plan-local-20260805a.json` are the executable board until `plan import`. + +## Executive summary + +- **Goal:** One Theorem chat register fills the agent hole Copilot leaves in Commonplace Studio **and** replaces OpenWork at console `/chat`, over the same repaired workspace checkout. +- **Intent:** Do not fake Theorem into `IDefaultChatAgent` until a Chat-participant host exists. Retire Microsoft Copilot product wiring first; mount a real Theorem register package in both doors; then delete OpenWork / residual LLM hosts. +- **Sequence:** Copilot retirement → volume repair → register contract → package → Studio mount → `/chat` swap + OpenWork retirement → debt cleanup. + +## Current condition (grounded) + +| Fact | Evidence | +|---|---| +| Studio host is live (`IDE_HOST=studio`, deploy `c4636818`) | FOLLOW-UP CS-007; entrypoint log `IDE host is Commonplace Studio` | +| Copilot still in product.json + `extensions/copilot` | Live SSH; CHAT Agent panel + “VS Code for the Web” in session smoke | +| Copilot retirement **implemented on branch**, not yet on main | `feat/studio-overlay-retire-copilot` (overlay null-delete, patch 0003, strip copilot, `chat.disableAIFeatures`) | +| `/workspace/repo` is sticky empty `git init` | Entrypoint only clones when `.git` absent; volume init'd without `WORKSPACE_REPO_URL` | +| `/chat` is `openwork.chat` | `OpenworkChatRegister`, middleware stamps, production cutover EXECUTE-REPORT | +| Theorem pack agent path is ACP | `apps/theorem-vscode` → `@commonplace/theorem-acp`; no VS Code Chat participant | + +## Goal + +Authenticated users get: + +1. **Studio** without GitHub Copilot / Microsoft chat product host. +2. **Theorem chat UI** in Studio’s agent/chat region (fills the hole). +3. **Same Theorem chat UI** at `https://v2.theoremharness.com/chat` (register swap). +4. **Same real checkout** under `/workspace/repo` for IDE Explorer and chat. +5. **OpenWork / opencode LLM door** off the product path once the register is live. + +## Non-goals + +- Stuffing `commonplace.theorem-vscode` into `defaultChatAgent` without Chat participant / entitlement-shaped APIs (that labels Copilot machinery “Theorem” and lies). +- Keeping OpenWork as a long-term parallel chat register “just in case.” +- Desktop Studio distribution. +- Repairing every historical OpenWork fork doc in one pass (CR-007 records supersession; full archive can follow). + +## Named decisions (load-bearing) + +1. **Two mounts, one package.** Studio panel and console `/chat` import the same register package. Divergence is a regression. +2. **ACP / Theorem heads are the transport.** Not OpenWork→opencode as the product happy path. +3. **Copilot hole ≠ `defaultChatAgent` Theorem.** Product keys are deleted; UI is our register. A future Chat-participant deliverable may later earn a real `defaultChatAgent` binding — out of scope here except as a deferral. +4. **Volume repair before dual-mount proof.** Empty repo makes Studio+chat “same checkout” unfalsifiable. +5. **OpenWork retirement is a deliverable, not a hope.** Register stamp, middleware, workspace image chat door, and doctor must stop advertising `openwork.chat`. + +## Checklist + +| ID | Task | Grounding | Proof | Status | +|---|---|---|---|---| +| CR-000 | Durable plan + checklist (this file) | `PLAN.md`, checklist JSON | files exist; plan_id named | **done** | +| CR-001 | Land Copilot retirement (`feat/studio-overlay-retire-copilot`) + redeploy | overlay, patch 0003, entrypoint, smoke | ledger-gate; live product.json; `/IDE` smoke | pending | +| CR-002 | Repair sticky empty `/workspace/repo` | `packaging/workspace/entrypoint.sh`, Railway volume | `package.json` + `git rev-parse HEAD` on live volume; Explorer non-empty | pending | +| CR-003 | Register contract: package path, `register_impl`, dual seams, retirement inventory | SPEC draft under this plan | named ids + non-goal on `IDefaultChatAgent` | pending | +| CR-004 | Implement Theorem chat register package | new package + pack/console adapters | package tests; no openworklabs/opencode on happy path | pending | +| CR-005 | Mount register in Studio agent/chat panel | theorem-vscode + Studio product settings | signed-in `/IDE` shows Theorem register; no Copilot sign-in wall | pending | +| CR-006 | Mount register at `/chat`; retire OpenWork door | `OpenworkChatRegister`, middleware, `.commonplace-canonical`, workspace image | stamp ≠ `openwork.chat`; doctor + register-manifest | pending | +| CR-007 | Retire residual LLM / OpenWork surface debt + EXECUTE-REPORT | docs + deletions | EXECUTE-REPORT; no product route serves OpenWork as chat | pending | + +## Sequence + +``` +1. CR-000 board → verify: PLAN + checklist +2. CR-001 Copilot retirement → verify: live Studio product identity +3. CR-002 volume repair → verify: real shared checkout +4. CR-003 contract → verify: named package + register_impl + retirement map +5. CR-004 package → verify: package tests +6. CR-005 Studio mount → verify: /IDE panel +7. CR-006 /chat + OpenWork out → verify: stamp + doctor +8. CR-007 debt + report → verify: EXECUTE-REPORT +``` + +## Validation commands + +```bash +# Copilot retirement +cd packaging/commonplace-studio && ./scripts/ledger-gate.sh +# after deploy: +railway ssh --service commonplace-workspace -- \ + 'node -e "const p=require(\"/opt/commonplace/studio-server/product.json\"); if(p.defaultChatAgent) process.exit(1);"' \ + && test ! -d /opt/commonplace/studio-server/extensions/copilot + +# Volume +railway ssh --service commonplace-workspace -- \ + 'test -f /workspace/repo/package.json && git -C /workspace/repo rev-parse HEAD' + +# Register / console +node scripts/check-register-manifest.mjs +node scripts/doctor.mjs +``` + +## Deferrals + +| ID | Title | Reason | +|---|---|---| +| D1 | Theorem as real `defaultChatAgent` | Requires Chat participant / host APIs over ACP; CR retires Copilot and mounts our register instead | +| D2 | Full OpenWork fork archive cleanup | CR-007 supersedes product path; historical OW* specs stay as record until a dedicated archive pass | + +## Import + +When harness MCP recovers: + +```text +plan import (plan_id=plan-theorem-chat-register-20260805a, source=docs/plans/theorem-chat-register/PLAN.md) +``` diff --git a/docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md b/docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md new file mode 100644 index 00000000..7a615a5e --- /dev/null +++ b/docs/plans/theorem-chat-register/SPEC-THEOREM-CHAT-REGISTER-1.0.md @@ -0,0 +1,46 @@ +# SPEC-THEOREM-CHAT-REGISTER-1.0 (draft skeleton for CR-003) + +Status: **draft — fill under CR-003**. This skeleton locks the decisions from +[`PLAN.md`](./PLAN.md) so implementation cannot silently widen or shrink scope. + +## 1. Problem + +Commonplace Studio’s Copilot `defaultChatAgent` hole and console `/chat` +(OpenWork) must become **one** Theorem chat register. Two hosts, one package. + +## 2. Package (to name in CR-003) + +| Field | Value (fill) | +|---|---| +| Package path | `packages/…` or `apps/…` | +| Export surface | composer + thread + session opener | +| Transport | Theorem ACP / heads (not OpenWork→opencode) | +| `register_impl` | e.g. `theorem.chat` (final string in CR-003) | + +## 3. Mounts + +| Host | Seam | Acceptance | +|---|---|---| +| Commonplace Studio | Agent/chat panel (post Copilot retirement) | Signed-in `/IDE` shows Theorem register; ACP session against `/workspace/repo` | +| Console | `/chat` (+ registry descriptors that today point at `openwork.chat`) | Stamp is Theorem `register_impl`; doctor green | + +## 4. Explicit non-goal + +Do **not** set `product.json` `defaultChatAgent` to `commonplace.theorem-vscode` +until the pack implements VS Code Chat participant / host APIs. Overlay deletion ++ our register UI is the honest cut. + +## 5. Retirement inventory (expand in CR-003) + +- [ ] `openwork.chat` register stamps (`OpenworkChatRegister`, middleware) +- [ ] Workspace OpenWork `:8787` as product `/chat` host (retire or gate) +- [ ] opencode / LLM-only happy path behind OpenWork +- [ ] assistant-ui-only chat components superseded by this register (if still reachable) +- [ ] Doctor / `.commonplace-canonical` / register-manifest rows + +## 6. Oracles + +- Package tests (CR-004) +- Live `/IDE` panel smoke (CR-005) +- Live `/chat` stamp + `check-register-manifest` + doctor (CR-006) +- EXECUTE-REPORT (CR-007) diff --git a/packaging/commonplace-studio/LEDGER.md b/packaging/commonplace-studio/LEDGER.md index 111e8bbc..3f13595f 100644 --- a/packaging/commonplace-studio/LEDGER.md +++ b/packaging/commonplace-studio/LEDGER.md @@ -4,13 +4,14 @@ Oracle-debt style: one entry per patch, its reason, the upstream link, and what would let it be deleted. A patch with no entry fails `scripts/ledger-gate.sh`, and the gate runs in CI. -**Patch count: 2.** +**Patch count: 3.** Named choice 1 still holds for capability. Everything V1 through V8 asks for is either extension API, which ships in `apps/theorem-vscode` and runs in stock hosts, or `product.json`, which is an overlay rather than a patch. The patches -below buy no capability at all: they are build-breaks or builder-memory limits -in upstream's own tree. +below buy no capability at all for the pack: they are build-breaks, builder +memory limits, or the null-checks required once the overlay *deletes* +upstream's Copilot product host. ## Entries @@ -87,6 +88,59 @@ Ideal upstream change: reh-web non-min tasks should match default again; or when upstream routes unminified reh-web through without-mangling itself. +### 0003-retire-default-chat-agent-copilot.patch + +**Finding.** CS-007 live smoke on deploy `c4636818` (`IDE_HOST=studio`) proved +the name overlay alone does not retire Microsoft Copilot from the workbench: + +- Welcome still featured "Get Started with VS Code for the Web". +- The CHAT Agent panel still targeted GitHub Copilot and GitHub auth. +- Shipped `product.json` still carried `defaultChatAgent` (aka.ms Copilot + URLs), `builtInExtensionsEnabledWithAutoUpdates: ["GitHub.copilot-chat"]`, + `trustedExtensionAuthAccess` for GitHub Copilot Chat, and + `voiceWsUrl` on `falcon-caas.mai.microsoft.com`. +- The reh-web artifact still contained `extensions/copilot`. + +Deleting `defaultChatAgent` via the overlay is configuration. Booting without +it is not: at pinned `1.131.0`, dozens of call sites read +`productService.defaultChatAgent.*` without a guard (extension gallery, +accounts, chat widget, chat status, onboarding module top-level, …). Setting +the key to `null` without deleting it, or deleting it without null-checks, +either leaves Copilot wired or crashes the workbench. Phonon IDE's public +diff against upstream used the same shape (remove the key + null-checks). + +**Why no API expresses this.** There is no supported `product.json` value that +means "no default chat agent" while the TypeScript contract still requires the +object. `chat.disableAIFeatures` hides UI but leaves the Copilot product host +and Microsoft endpoints in the shipped file. The Theorem pack is ACP, not a +replacement for `IDefaultChatAgent`. + +**The patch.** Against `1.131.0` (`3a03d6f7`): + +1. Make `IProductConfiguration.defaultChatAgent` optional. +2. Guard the crash-on-boot call sites (gallery, abstract extension management, + extensions workbench, language-model tools, chat widget, chat status, + agent-sessions welcome, default-account contribution). +3. Stub onboarding's module-level `defaultChat` when the key is absent so the + module can load. +4. Retitle the web getting-started walkthrough from "VS Code for the Web" to + "Commonplace Studio". + +Companion configuration (not this patch): overlay deletes `defaultChatAgent` +and `voiceWsUrl`, clears Copilot auto-update/auth grants, `build.sh` removes +`extensions/copilot` from the server artifact, and the workspace entrypoint +seeds `chat.disableAIFeatures: true` when unset. + +**Upstream.** Pattern matches public fork work that removes `defaultChatAgent` +and adds null-checks. Not filed as a microsoft/vscode PR: upstream wants the +OSS product to ship with Copilot development config. + +**Delete it when** upstream makes `defaultChatAgent` optional end-to-end and +ships a supported "no default agent" product shape, or when +`UPSTREAM_TAG` moves to a tree that already null-checks every site this patch +touches. Check by dropping the patch, deleting the key in the overlay, and +booting reh-web. + ## Candidates, not yet owed Recorded here so that if one is ever asked for, the finding is already half @@ -94,9 +148,8 @@ written and the temptation to patch first is smaller. | Candidate | Would need a patch because | Delete it when | Upstream | | --- | --- | --- | --- | -| Hiding the stock chat and agent UI | Unknown. `product.json` and default settings may already cover it, and every needed patch is a ledger entry before it is written, so this stays a Verify-first item until someone checks a build. | Configuration is confirmed sufficient, or upstream adds a supported way to hide it. | (to be recorded when checked) | | Custom updater endpoint | `updateUrl` is null today, so the app never checks for updates. Wiring our own service is configuration; changing update *behaviour* would not be. | An updater service exists and `product.json` alone drives it. | n/a | -| Welcome and walkthrough branding beyond `product.json` | Some first-run surfaces read from in-tree resources rather than product configuration. | Upstream exposes them as product configuration. | (to be recorded when checked) | +| Deeper welcome media beyond walkthrough titles | Some first-run markdown/media still say "VS Code" inside `media/` trees. Titles are covered by 0003. | Upstream exposes remaining copy as product configuration, or we accept residual media strings. | (re-check after next tag) | ## Rule diff --git a/packaging/commonplace-studio/patches/0003-retire-default-chat-agent-copilot.patch b/packaging/commonplace-studio/patches/0003-retire-default-chat-agent-copilot.patch new file mode 100644 index 00000000..4c2dd58b --- /dev/null +++ b/packaging/commonplace-studio/patches/0003-retire-default-chat-agent-copilot.patch @@ -0,0 +1,350 @@ +diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts +index 46881e33..63a48e2b 100644 +--- a/src/vs/base/common/product.ts ++++ b/src/vs/base/common/product.ts +@@ -264,7 +264,7 @@ export interface IProductConfiguration { + readonly commonlyUsedSettings?: string[]; + readonly aiGeneratedWorkspaceTrust?: IAiGeneratedWorkspaceTrust; + +- readonly defaultChatAgent: IDefaultChatAgent; ++ readonly defaultChatAgent?: IDefaultChatAgent; + readonly chatParticipantRegistry?: string; + readonly chatSessionRecommendations?: IChatSessionRecommendation[]; + readonly emergencyAlertUrl?: string; +diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +index e506b831..bf59b56a 100644 +--- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts ++++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +@@ -984,7 +984,7 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio + if (checked.indexOf(extension) !== -1) { + return []; + } +- if (areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId })) { ++ if (this.productService.defaultChatAgent && areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId })) { + return []; + } + checked.push(extension); +diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +index a48e4629..6f10bf95 100644 +--- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts ++++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +@@ -1157,7 +1157,7 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle + for (let index = 0; index < extensions.length; index++) { + const extension = extensions[index]; + setTelemetry(extension, ((query.pageNumber - 1) * query.pageSize) + index, options.source); +- if (areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId, })) { ++ if (this.productService.defaultChatAgent && areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId, })) { + defaultChatAgentExtension = extension; + } else { + result.push(extension); +@@ -1989,15 +1989,17 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle + } + } + +- deprecated[this.productService.defaultChatAgent.extensionId.toLowerCase()] = { +- disallowInstall: true, +- extension: { +- id: this.productService.defaultChatAgent.chatExtensionId, +- displayName: 'GitHub Copilot Chat', +- autoMigrate: { storage: false, donotDisable: true }, +- preRelease: this.productService.quality !== 'stable' +- } +- }; ++ if (this.productService.defaultChatAgent) { ++ deprecated[this.productService.defaultChatAgent.extensionId.toLowerCase()] = { ++ disallowInstall: true, ++ extension: { ++ id: this.productService.defaultChatAgent.chatExtensionId, ++ displayName: 'GitHub Copilot Chat', ++ autoMigrate: { storage: false, donotDisable: true }, ++ preRelease: this.productService.quality !== 'stable' ++ } ++ }; ++ } + + return { malicious, deprecated, search, autoUpdate }; + } +diff --git a/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts b/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts +index 47a4ffc5..8d129dd9 100644 +--- a/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts ++++ b/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts +@@ -124,7 +124,7 @@ export class MainThreadLanguageModelTools extends Disposable implements MainThre + + // Convert source from DTO, matching the isBuiltinTool logic from languageModelToolsContribution + const isBuiltinTool = this._productService.defaultChatAgent?.chatExtensionId +- ? ExtensionIdentifier.equals(extensionId, this._productService.defaultChatAgent.chatExtensionId) ++ ? !!this._productService.defaultChatAgent && ExtensionIdentifier.equals(extensionId, this._productService.defaultChatAgent.chatExtensionId) + : false; + const source: ToolDataSource = isBuiltinTool + ? ToolDataSource.Internal +diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts +index e67b4dba..8f76b654 100644 +--- a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts ++++ b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts +@@ -482,7 +482,7 @@ export class ChatStatusDashboard extends DomWidget { + // Update status text when completions setting changes + if (statusEl) { + this._store.add(this.configurationService.onDidChangeConfiguration(e => { +- if (e.affectsConfiguration(defaultChat.completionsEnablementSetting)) { ++ if (defaultChat && e.affectsConfiguration(defaultChat.completionsEnablementSetting)) { + statusEl!.textContent = getStatusText(); + } + })); +@@ -600,7 +600,7 @@ export class ChatStatusDashboard extends DomWidget { + + let descriptionText: string | MarkdownString; + let descriptionClass = '.description'; +- if (newUser && anonymousUser) { ++ if (newUser && anonymousUser && defaultChat) { + descriptionText = new MarkdownString(localize({ key: 'activeDescriptionAnonymous', comment: ['{Locked="]({2})"}', '{Locked="]({3})"}'] }, "By continuing with {0} Copilot, you agree to {1}'s [Terms]({2}) and [Privacy Statement]({3})", defaultChat.provider.default.name, defaultChat.provider.default.name, defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl), { isTrusted: true }); + descriptionClass = `${descriptionClass}.terms`; + } else if (newUser) { +@@ -939,6 +939,7 @@ export class ChatStatusDashboard extends DomWidget { + } + + private createSettings(container: HTMLElement): void { ++ if (!defaultChat) { return; } + const modeId = this.editorService.activeTextEditorLanguageId; + const settings = container.appendChild($('div.settings')); + +@@ -1008,10 +1009,12 @@ export class ChatStatusDashboard extends DomWidget { + } + + private createInlineSuggestionsSetting(container: HTMLElement, label: string, modeId: string | undefined): void { ++ if (!defaultChat) { return; } + this.createSetting(container, [defaultChat.completionsEnablementSetting], label, this.getCompletionsSettingAccessor(modeId)); + } + + private createTriStateLanguageSetting(container: HTMLElement, label: string, modeId: string, onStateChange: () => void): void { ++ if (!defaultChat) { return; } + const settingId = defaultChat.completionsEnablementSetting; + + const getState = (): boolean | 'mixed' => { +@@ -1114,6 +1117,7 @@ export class ChatStatusDashboard extends DomWidget { + } + + private findConfiguredCompletionsValues(modeId?: string): { target: ConfigurationTarget; value: Record }[] { ++ if (!defaultChat) { return []; } + const inspected = this.configurationService.inspect>(defaultChat.completionsEnablementSetting); + const result: { target: ConfigurationTarget; value: Record }[] = []; + for (const target of completionsConfigurationTargets) { +@@ -1126,6 +1130,9 @@ export class ChatStatusDashboard extends DomWidget { + } + + private getCompletionsSettingAccessor(modeId = '*'): ISettingsAccessor { ++ if (!defaultChat) { ++ return { readSetting: () => false, writeSetting: async () => { } }; ++ } + const settingId = defaultChat.completionsEnablementSetting; + + return { +@@ -1148,6 +1155,7 @@ export class ChatStatusDashboard extends DomWidget { + } + + private createNextEditSuggestionsSetting(container: HTMLElement, label: string, completionsSettingAccessor: ISettingsAccessor): void { ++ if (!defaultChat) { return; } + const nesSettingId = defaultChat.nextEditSuggestionsSetting; + const completionsSettingId = defaultChat.completionsEnablementSetting; + const resource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); +@@ -1254,7 +1262,7 @@ export class ChatStatusDashboard extends DomWidget { + })); + + this._store.add(this.configurationService.onDidChangeConfiguration(e => { +- if (e.affectsConfiguration(defaultChat.completionsEnablementSetting)) { ++ if (defaultChat && e.affectsConfiguration(defaultChat.completionsEnablementSetting)) { + button.enabled = isEnabled(); + } + updateIntervalTimer(); +diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts +index 26864fda..97052f50 100644 +--- a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts ++++ b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts +@@ -1156,7 +1156,7 @@ export class ChatWidget extends Disposable implements IChatWidget { + if (!numItems) { + const defaultAgent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentModeKind); + let additionalMessage: string | IMarkdownString | undefined; +- if (this.chatEntitlementService.anonymous && !this.chatEntitlementService.sentiment.completed) { ++ if (product.defaultChatAgent && this.chatEntitlementService.anonymous && !this.chatEntitlementService.sentiment.completed) { + const providers = product.defaultChatAgent.provider; + additionalMessage = new MarkdownString(localize({ key: 'settings', comment: ['{Locked="]({2})"}', '{Locked="]({3})"}'] }, "By continuing with {0} Copilot, you agree to {1}'s [Terms]({2}) and [Privacy Statement]({3}).", providers.default.name, providers.default.name, product.defaultChatAgent.termsStatementUrl, product.defaultChatAgent.privacyStatementUrl), { isTrusted: true }); + } else { +diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +index 6e1f8a4f..5e41f846 100644 +--- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts ++++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +@@ -269,7 +269,7 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri + + // If OSS and the product.json is not set up, fall back to checking api proposal + const isBuiltinTool = productService.defaultChatAgent?.chatExtensionId ? +- ExtensionIdentifier.equals(extension.description.identifier, productService.defaultChatAgent.chatExtensionId) : ++ !!productService.defaultChatAgent && ExtensionIdentifier.equals(extension.description.identifier, productService.defaultChatAgent.chatExtensionId) : + isProposedApiEnabled(extension.description, 'chatParticipantPrivate'); + + const source: ToolDataSource = isBuiltinTool +@@ -337,7 +337,7 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri + } + + const isBuiltinTool = productService.defaultChatAgent?.chatExtensionId ? +- ExtensionIdentifier.equals(extension.description.identifier, productService.defaultChatAgent.chatExtensionId) : ++ !!productService.defaultChatAgent && ExtensionIdentifier.equals(extension.description.identifier, productService.defaultChatAgent.chatExtensionId) : + isProposedApiEnabled(extension.description, 'chatParticipantPrivate'); + + const source: ToolDataSource = isBuiltinTool +diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +index a58d77d1..0085c092 100644 +--- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts ++++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +@@ -2864,7 +2864,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension + } + + const extensionsToUninstall: UninstallExtensionInfo[] = [{ extension: extension.local }]; +- if (!areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId })) { ++ if (!this.productService.defaultChatAgent || !areSameExtensions(extension.identifier, { id: this.productService.defaultChatAgent.extensionId })) { + for (const packExtension of this.getAllPackedExtensions(extension, this.local)) { + if (packExtension.local && !extensionsToUninstall.some(e => areSameExtensions(e.extension.identifier, packExtension.identifier))) { + extensionsToUninstall.push({ extension: packExtension.local }); +diff --git a/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.ts b/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.ts +index ac320709..6f5bb563 100644 +--- a/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.ts ++++ b/src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.ts +@@ -743,8 +743,8 @@ export class AgentSessionsWelcomePage extends EditorPane { + { key: 'tosDescription', comment: ['{Locked="]({1})"}', '{Locked="]({2})"}'] }, + "By continuing, you agree to {0}'s [Terms]({1}) and [Privacy Statement]({2}).", + providers.default.name, +- this.productService.defaultChatAgent.termsStatementUrl, +- this.productService.defaultChatAgent.privacyStatementUrl ++ this.productService.defaultChatAgent?.termsStatementUrl ?? '', ++ this.productService.defaultChatAgent?.privacyStatementUrl ?? '' + ), + { isTrusted: true } + ); +diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +index 168a3d01..1069d964 100644 +--- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts ++++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +@@ -257,7 +257,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ + isFeatured: true, + icon: setupIcon, + when: '!isWeb', +- walkthroughPageTitle: localize('gettingStarted.setup.walkthroughPageTitle', 'Setup VS Code'), ++ walkthroughPageTitle: localize('gettingStarted.setup.walkthroughPageTitle', 'Setup Commonplace Studio'), + next: 'Beginner', + content: { + type: 'steps', +@@ -288,13 +288,13 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ + + { + id: 'SetupWeb', +- title: localize('gettingStarted.setupWeb.title', "Get Started with VS Code for the Web"), ++ title: localize('gettingStarted.setupWeb.title', "Get Started with Commonplace Studio"), + description: localize('gettingStarted.setupWeb.description', "Customize your editor, learn the basics, and start coding"), + isFeatured: true, + icon: setupIcon, + when: 'isWeb', + next: 'Beginner', +- walkthroughPageTitle: localize('gettingStarted.setupWeb.walkthroughPageTitle', 'Setup VS Code Web'), ++ walkthroughPageTitle: localize('gettingStarted.setupWeb.walkthroughPageTitle', 'Setup Commonplace Studio'), + content: { + type: 'steps', + steps: [ +@@ -380,7 +380,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ + icon: setupIcon, + when: CONTEXT_ACCESSIBILITY_MODE_ENABLED.key, + next: 'Setup', +- walkthroughPageTitle: localize('gettingStarted.setupAccessibility.walkthroughPageTitle', 'Setup VS Code Accessibility'), ++ walkthroughPageTitle: localize('gettingStarted.setupAccessibility.walkthroughPageTitle', 'Setup Commonplace Studio Accessibility'), + content: { + type: 'steps', + steps: [ +diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts +index c1fb3e12..8ad5896d 100644 +--- a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts ++++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts +@@ -77,8 +77,45 @@ type OnboardingActionEvent = { + + type EnterpriseSignInUiState = 'options' | 'instance' | 'progress'; + +-assertDefined(product.defaultChatAgent, 'Onboarding requires a default chat agent product configuration.'); +-const defaultChat = product.defaultChatAgent; ++const defaultChat = product.defaultChatAgent ?? { ++ extensionId: '', ++ chatExtensionId: '', ++ chatExtensionOutputId: '', ++ chatExtensionOutputExtensionStateCommand: '', ++ documentationUrl: '', ++ skusDocumentationUrl: '', ++ optimizeUsageDocumentationUrl: '', ++ publicCodeMatchesUrl: '', ++ managePlanUrl: '', ++ upgradePlanUrl: '', ++ signUpUrl: '', ++ termsStatementUrl: '', ++ privacyStatementUrl: '', ++ provider: { ++ default: { id: '', name: '' }, ++ enterprise: { id: '', name: '' }, ++ google: { id: '', name: '' }, ++ apple: { id: '', name: '' }, ++ }, ++ providerExtensionId: '', ++ providerUriSetting: '', ++ providerScopes: [], ++ entitlementUrl: '', ++ entitlementSignupLimitedUrl: '', ++ tokenEntitlementUrl: '', ++ mcpRegistryDataUrl: '', ++ managedSettingsUrl: '', ++ chatQuotaExceededContext: '', ++ completionsQuotaExceededContext: '', ++ walkthroughCommand: '', ++ completionsMenuCommand: '', ++ chatRefreshTokenCommand: '', ++ generateCommitMessageCommand: '', ++ resolveMergeConflictsCommand: '', ++ completionsAdvancedSetting: '', ++ completionsEnablementSetting: '', ++ nextEditSuggestionsSetting: '', ++}; + + /** + * Variation A — Classic Wizard Modal +diff --git a/src/vs/workbench/services/accounts/browser/defaultAccount.ts b/src/vs/workbench/services/accounts/browser/defaultAccount.ts +index b267dcbd..a56aa16e 100644 +--- a/src/vs/workbench/services/accounts/browser/defaultAccount.ts ++++ b/src/vs/workbench/services/accounts/browser/defaultAccount.ts +@@ -144,7 +144,24 @@ export class DefaultAccountService extends Disposable implements IDefaultAccount + @IProductService productService: IProductService, + ) { + super(); +- this.defaultAccountConfig = toDefaultAccountConfig(productService.defaultChatAgent); ++ if (productService.defaultChatAgent) { ++ this.defaultAccountConfig = toDefaultAccountConfig(productService.defaultChatAgent); ++ } else { ++ this.defaultAccountConfig = { ++ preferredExtensions: [], ++ authenticationProvider: { ++ default: { id: '', name: '' }, ++ enterprise: { id: '', name: '' }, ++ enterpriseProviderConfig: '', ++ enterpriseProviderUriSetting: '', ++ scopes: [], ++ }, ++ entitlementUrl: '', ++ tokenEntitlementUrl: '', ++ mcpRegistryDataUrl: '', ++ managedSettingsUrl: '', ++ }; ++ } + } + + async getDefaultAccount(): Promise { +@@ -1175,6 +1192,9 @@ class DefaultAccountProviderContribution extends Disposable implements IWorkbenc + @IDefaultAccountService defaultAccountService: IDefaultAccountService, + ) { + super(); ++ if (!productService.defaultChatAgent) { ++ return; ++ } + const defaultAccountProvider = this._register(instantiationService.createInstance(DefaultAccountProvider, toDefaultAccountConfig(productService.defaultChatAgent))); + defaultAccountService.setDefaultAccountProvider(defaultAccountProvider); + } diff --git a/packaging/commonplace-studio/patches/README.md b/packaging/commonplace-studio/patches/README.md index 4a7b034c..38bd4b7a 100644 --- a/packaging/commonplace-studio/patches/README.md +++ b/packaging/commonplace-studio/patches/README.md @@ -1,12 +1,13 @@ # Patch queue -**Patch count: 2** (build-only; see `LEDGER.md`). Target state remains zero -capability patches. +**Patch count: 3** (build-only + Copilot product-host retirement; see +`LEDGER.md`). Capability still lives in `apps/theorem-vscode` and the overlay. | Patch | Why | | --- | --- | | `0001-mangler-keep-session-changes-overrides-protected.patch` | `-min` mangler refuses public overrides of protected methods | | `0002-reh-web-unminified-skip-mangler.patch` | Unminified reh-web still ran mangling and OOM'd the Railway builder | +| `0003-retire-default-chat-agent-copilot.patch` | Deleting upstream `defaultChatAgent` needs null-checks; retitles web walkthrough | Patches are applied in filename order by `scripts/build.sh`, named `NNNN-short-name.patch`, and each one must have a `LEDGER.md` entry naming its diff --git a/packaging/commonplace-studio/product.overlay.json b/packaging/commonplace-studio/product.overlay.json index 7b981980..6698d49a 100644 --- a/packaging/commonplace-studio/product.overlay.json +++ b/packaging/commonplace-studio/product.overlay.json @@ -29,14 +29,21 @@ }, "builtInExtensions": [], + "builtInExtensionsEnabledWithAutoUpdates": [], + "trustedExtensionAuthAccess": {}, "configurationSync.store": null, "updateUrl": null, + "voiceWsUrl": null, + "defaultChatAgent": null, "_notes": [ "extensionEnabledApiProposals is the whole reason V4 can bind search to the index spine: both provider shapes are still under vscode.proposed.* at microsoft/vscode main as of 2026-08-02. In any build without this grant the pack registers no search providers and VS Code's ripgrep search stands, which is named choice 8.", "updateUrl is null until an updater endpoint exists that is ours. A fork pointing at Microsoft's update service is both a trademark problem and a way to overwrite this build with theirs.", "configurationSync.store is null because Settings Sync is Microsoft's service and requires their accounts.", - "builtInExtensions stays empty: the Theorem pack is staged into the build as a preinstalled extension by scripts/build.sh, not fetched from a gallery at build time." + "builtInExtensions stays empty: the Theorem pack is staged into the build as a preinstalled extension by scripts/build.sh, not fetched from a gallery at build time.", + "defaultChatAgent is deleted (null → key removed by overlay_product). Upstream 1.131.0 wires GitHub Copilot here; live CS-007 smoke proved the CHAT Agent panel and aka.ms URLs survive name overlay alone. Deletion needs patch 0003 null-checks.", + "builtInExtensionsEnabledWithAutoUpdates and trustedExtensionAuthAccess clear the Copilot-chat auto-update and GitHub auth grants that still pointed at GitHub.copilot-chat after the identity overlay.", + "voiceWsUrl is deleted: the live reh-web product pointed it at falcon-caas.mai.microsoft.com." ] } diff --git a/packaging/commonplace-studio/scripts/build.sh b/packaging/commonplace-studio/scripts/build.sh index 029aaf3a..84f5213b 100755 --- a/packaging/commonplace-studio/scripts/build.sh +++ b/packaging/commonplace-studio/scripts/build.sh @@ -243,12 +243,33 @@ overlay_product() { const overlay = JSON.parse(fs.readFileSync(process.env.OVERLAY_PATH, "utf8")); for (const key of Object.keys(overlay)) { if (key.startsWith("_")) continue; - base[key] = overlay[key]; + // null means delete: used to retire upstream Copilot/Microsoft + // product keys (defaultChatAgent, voiceWsUrl, …) without leaving + // a `"key": null` the workbench still treats as present. + if (overlay[key] === null) { + delete base[key]; + } else { + base[key] = overlay[key]; + } } fs.writeFileSync(target, `${JSON.stringify(base, null, "\t")}\n`); ' } +# reh-web still ships upstream's built-in `extensions/copilot` tree even after +# product.json stops naming GitHub Copilot. Drop it from the deployable server +# so the workbench cannot activate a Microsoft chat host next to Theorem. +retire_upstream_copilot_extension() { + local server_dir=$1 + local copilot_dir="$server_dir/extensions/copilot" + if [[ -d "$copilot_dir" ]]; then + log "removing upstream built-in copilot extension from $copilot_dir" + rm -rf "$copilot_dir" + else + log "no upstream copilot extension under $server_dir/extensions (already absent)" + fi +} + stage_pack() { local target="$BUILD_DIR/extensions/theorem-vscode" @@ -406,6 +427,7 @@ build_server() { echo "The fork's serverApplicationName drives that filename; check product.overlay.json" >&2 exit 1 fi + retire_upstream_copilot_extension "$out" log "server at $out" log "smoke it with: $out/bin/commonplace-studio-server --host 127.0.0.1 --port 8080 --without-connection-token --accept-server-license-terms" } diff --git a/packaging/commonplace-studio/scripts/ledger-gate.sh b/packaging/commonplace-studio/scripts/ledger-gate.sh index 28315b93..54fa51c8 100755 --- a/packaging/commonplace-studio/scripts/ledger-gate.sh +++ b/packaging/commonplace-studio/scripts/ledger-gate.sh @@ -112,6 +112,12 @@ check_built_tree() { } if (product.enableTelemetry !== false) problems.push("enableTelemetry is not false"); if (product.updateUrl) problems.push(`updateUrl survived as ${JSON.stringify(product.updateUrl)}`); + if (product.defaultChatAgent) problems.push("defaultChatAgent survived (Copilot product host still wired)"); + if (product.voiceWsUrl) problems.push(`voiceWsUrl survived as ${JSON.stringify(product.voiceWsUrl)}`); + const auto = product.builtInExtensionsEnabledWithAutoUpdates; + if (Array.isArray(auto) && auto.some((id) => /copilot/i.test(String(id)))) { + problems.push(`builtInExtensionsEnabledWithAutoUpdates still names Copilot: ${JSON.stringify(auto)}`); + } const gallery = product.extensionsGallery?.serviceUrl ?? ""; if (!gallery.includes("open-vsx.org")) { problems.push(`extensionsGallery is ${JSON.stringify(gallery)}, expected Open VSX`); diff --git a/packaging/commonplace-studio/scripts/smoke-server.sh b/packaging/commonplace-studio/scripts/smoke-server.sh index f1fd680d..dae96c01 100755 --- a/packaging/commonplace-studio/scripts/smoke-server.sh +++ b/packaging/commonplace-studio/scripts/smoke-server.sh @@ -153,11 +153,27 @@ check_identity() { indent <<< "$microsoft_hits" fi - local update_url sync_store + local update_url sync_store voice_url chat_agent update_url="$(json_get "$product" updateUrl)" sync_store="$(json_get "$product" configurationSync.store)" + voice_url="$(json_get "$product" voiceWsUrl)" + chat_agent="$(json_get "$product" defaultChatAgent)" expect_unset updateUrl "$update_url" expect_unset "configurationSync.store" "$sync_store" + expect_unset voiceWsUrl "$voice_url" + expect_unset defaultChatAgent "$chat_agent" + + if grep -Eiq 'GitHub\.copilot|aka\.ms/github-copilot|falcon-caas\.mai\.microsoft' "$product"; then + fail "product.json still names Copilot or Microsoft Copilot endpoints" + else + pass "no Copilot / Microsoft Copilot endpoints in product.json" + fi + + if [[ -d "$SERVER_DIR/extensions/copilot" ]]; then + fail "upstream built-in copilot extension still present under $SERVER_DIR/extensions/copilot" + else + pass "upstream built-in copilot extension is not shipped" + fi } check_pack() { diff --git a/packaging/workspace/entrypoint.sh b/packaging/workspace/entrypoint.sh index 6c97d532..c29abfe3 100755 --- a/packaging/workspace/entrypoint.sh +++ b/packaging/workspace/entrypoint.sh @@ -35,10 +35,31 @@ fi mkdir -p "${WORKSPACE_DIR}" +# Prefer an explicit clone URL. Otherwise compose one from WORKSPACE_REPO + +# THEOREM_GIT_TOKEN so the secret never has to be duplicated into +# WORKSPACE_REPO_URL on the service (CR-002: production had the token but no +# URL, so the volume stuck on an empty `git init`). +if [ -z "${WORKSPACE_REPO_URL:-}" ] && [ -n "${WORKSPACE_REPO:-}" ] && [ -n "${THEOREM_GIT_TOKEN:-}" ]; then + WORKSPACE_REPO_URL="https://x-access-token:${THEOREM_GIT_TOKEN}@github.com/${WORKSPACE_REPO}.git" + echo "workspace: composed WORKSPACE_REPO_URL from WORKSPACE_REPO=${WORKSPACE_REPO}" +fi + +# True when the volume has no usable checkout: missing .git, or a sticky empty +# `git init` with no HEAD (the Aug 3 production scar). +workspace_repo_needs_seed() { + if [ ! -d "${WORKSPACE_DIR}/.git" ]; then + return 0 + fi + if ! git -C "${WORKSPACE_DIR}" rev-parse --verify HEAD >/dev/null 2>&1; then + return 0 + fi + return 1 +} + # A fresh volume is an empty directory, not a repository. Both doors behave # better against a real one (git status, diffs, the daemon's VCS reads), and -# an operator who mounts an existing checkout keeps theirs untouched. -if [ ! -d "${WORKSPACE_DIR}/.git" ]; then +# an operator who mounts an existing checkout with commits keeps theirs untouched. +if workspace_repo_needs_seed; then if [ -n "${WORKSPACE_REPO_URL:-}" ]; then # A private HTTPS clone URL can carry a deploy token in its userinfo. # Printing it publishes the credential to container logs, and git would @@ -47,7 +68,13 @@ if [ ! -d "${WORKSPACE_DIR}/.git" ]; then # recoverable independently of it. Log a redacted form, and rewrite the # stored remote to the same URL without userinfo after cloning. redacted_url="$(printf '%s' "${WORKSPACE_REPO_URL}" | sed -E 's#(://)[^/@]*@#\1#')" - echo "workspace: cloning ${redacted_url} into ${WORKSPACE_DIR}" + if [ -d "${WORKSPACE_DIR}/.git" ]; then + echo "workspace: repairing sticky empty git init at ${WORKSPACE_DIR} from ${redacted_url}" + # Drop only the broken checkout contents; the parent volume keeps state/. + find "${WORKSPACE_DIR}" -mindepth 1 -maxdepth 1 -exec rm -rf {} + + else + echo "workspace: cloning ${redacted_url} into ${WORKSPACE_DIR}" + fi git clone --depth "${WORKSPACE_CLONE_DEPTH:-1}" "${WORKSPACE_REPO_URL}" "${WORKSPACE_DIR}" git -C "${WORKSPACE_DIR}" remote set-url origin "${redacted_url}" @@ -64,8 +91,12 @@ if [ ! -d "${WORKSPACE_DIR}/.git" ]; then '!f() { printf "%s\n" "url=${WORKSPACE_REPO_URL}"; }; f' fi else - echo "workspace: initializing an empty repository at ${WORKSPACE_DIR}" - git init --quiet "${WORKSPACE_DIR}" + if [ -d "${WORKSPACE_DIR}/.git" ]; then + echo "workspace: sticky empty git init at ${WORKSPACE_DIR} and no WORKSPACE_REPO_URL/WORKSPACE_REPO; leaving it" >&2 + else + echo "workspace: initializing an empty repository at ${WORKSPACE_DIR}" + git init --quiet "${WORKSPACE_DIR}" + fi fi fi @@ -194,6 +225,11 @@ set('theorem.agentUrl', process.env.THEOREM_ACP_WS_URL); if (process.env.THEOREM_EDITOR_WRITE_TOKEN_TO_SETTINGS === '1') { set('theorem.token', process.env.THEOREM_EDITOR_API_KEY); } +// Hide upstream Copilot/chat chrome. Theorem ACP lives in the pack, not the +// stock CHAT Agent panel. Only seed when unset so a user can flip it back. +if (!Object.prototype.hasOwnProperty.call(current, 'chat.disableAIFeatures')) { + current['chat.disableAIFeatures'] = true; +} fs.writeFileSync(path, `${JSON.stringify(current, null, 2)}\n`); NODE diff --git a/packaging/workspace/railway.toml b/packaging/workspace/railway.toml index 5785c331..15a8c13c 100644 --- a/packaging/workspace/railway.toml +++ b/packaging/workspace/railway.toml @@ -26,8 +26,13 @@ # THEOREM_CONSOLE_ORIGIN deep links (default v2 console) # # Optional environment: -# WORKSPACE_REPO_URL Cloned into WORKSPACE_DIR on a fresh volume. Absent, -# the volume is initialized as an empty repository. +# WORKSPACE_REPO_URL Cloned into WORKSPACE_DIR on a fresh or sticky-empty +# volume. Absent, see WORKSPACE_REPO. +# WORKSPACE_REPO owner/name (e.g. Travis-Gilbert/CommonPlace). When +# WORKSPACE_REPO_URL is unset, composed with +# THEOREM_GIT_TOKEN into an HTTPS clone URL so the +# token is not duplicated into a second secret. +# THEOREM_GIT_TOKEN GitHub token used only when composing the clone URL. # THEOREM_MCP_URL The head's graph door (OW2). Absent, the head runs # without the graph and says so. # THEOREM_API_KEY Bearer credential for the above.