From 11cf81d869eff50869d8dda60281220ab084192c Mon Sep 17 00:00:00 2001 From: mchwang Date: Thu, 8 Oct 2026 15:52:09 -0700 Subject: [PATCH 1/3] Complete planning production acceptance --- docs/architecture.md | 2 +- docs/implementation/planning-screen.md | 6 +- test/browser/plans-production.spec.ts | 183 +++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 test/browser/plans-production.spec.ts diff --git a/docs/architecture.md b/docs/architecture.md index ab937d7e..e82074ff 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -413,7 +413,7 @@ These are the decisions that shape the structure. The full list, with the reason | F | Runner | Merged: F1 (lifecycle store, coordinator, shutdown wiring, startup recovery and the lock, planning API, feedback events, and stops that wait for their commit: #53, #74, #57, #59, #60, #96); F2a–F2b (execute prompts, audit, per-item execution, real workspace, durable safety findings: #67, #68, #92, #94, #128); F2d (already-fixed check, PR opening, branch pusher, production publishing, closing PRs on cancel: #84, #101, #110, #115, #116, #119, #123); production wiring and `start`/`resume` (#91, PRs #102, #105); planning through lane D (#117, #124, PRs #118, #120, #125); merging the task's own PR (#121, PR #126); start/resume approval, review-version, recovery and completed-prefix guards (#107, PR #130); continuation after a scope pause (#88, PR #134); the F3 durable trusted local-rebase foundation (#22, PR #136); the F4 foreign-conflict engine plus production sandbox adapter (#22, PRs #138 and #140); and sandboxed owned-commit conflict resolution (#22, PR #144). Next in #22: F5 owns current base/head refresh, rebase admission, post-rebase attribution and approval refresh, and head-bound `cmd:` execution. F6 owns rewritten-head push, fresh required-check refresh, the already-fixed rerun, and exact-pair guarded-merge handoff. The integrated flow pushes a rewritten head before it recomputes review state and reruns every plan item's head-bound `cmd:` checks; fresh required GitHub checks follow. | | Hardening | Allowlisted subprocess environments | Done: every Git command (#82, #83) and every `gh` runner (#84, #86). | | Tests | Test suite reliability | Done. The full suite runs ordinary tests first and Docker-backed files serially; CI and the real-Docker gate passed on PR #132 (#129). | -| G | Planning screen | G1 through G3 built (#145, PRs #148 and #150): display/import the current plan, request a next-revision draft or suggestion cards, render or dismiss the result, and revision-bound Apply with exact ambiguous replay. G4 real-provider integration acceptance remains. | +| G | Planning screen | Done (#145). The screen displays/imports the current plan, requests and renders a next-revision draft or suggestion cards, and applies results with revision-bound exact ambiguous replay. G4 composes the browser with the production planning setup, D-backed provider boundary, F endpoints and SQLite persistence; focused adapter and recorded-output suites plus the real-Docker planning gate cover the lower boundary. | | H | Issue ranking and Issues screen | Done. H4b (#108, PR #139) records author-bound trust, guards runner and publishing boundaries, controls which comments reach prompts, records prompt comment evidence, and provides trust/untrust controls on the Issues screen. | | I, J | Queue and run windows; learning from feedback | Not started (after F) | diff --git a/docs/implementation/planning-screen.md b/docs/implementation/planning-screen.md index 682927a9..47a3d2b2 100644 --- a/docs/implementation/planning-screen.md +++ b/docs/implementation/planning-screen.md @@ -1,6 +1,6 @@ # Planning screen -**Lane and steps:** lane G, G1 through G3. Tracking issue: #145. +**Lane and steps:** lane G, G1 through G4. Tracking issue: #145. G1 adds a Plans screen over the planning contracts already owned by lanes E and F. It does not add another plan writer: the browser imports through `POST /api/plan/import`, then reloads `GET /api/review` so the current Store revision remains the only displayed authority. @@ -8,6 +8,8 @@ G2 adds browser controls for the existing draft and suggestion request lifecycle G3 applies a ready draft or one suggestion card through the Store-owned revision-bound endpoint. The browser never constructs a new plan. It retains one action ID across an ambiguous Apply retry, accepts only the expected next revision, then reloads the authoritative review. Once Apply commits, a reload failure is reported separately and cannot re-enable the action or start authoring against the old revision; a manual refresh must observe the applied revision first. Applying one card consumes the response; sibling cards stay disabled and **Refresh suggestions** sends their bounded operation, item and summary descriptions in a new revision-bound request without replacing newer guidance in the textarea. +G4 connects those browser flows to the production planning setup rather than a browser-only provider stub. Its acceptance runs suggestions and whole-plan drafts through `productionPlanning`, the real `createPlanningProvider` boundary with deterministic lane D stand-ins, F's production HTTP endpoints and the SQLite Store. It applies each result in the browser, restarts the server between them and reopens SQLite after each Apply. The separate real-Docker suite remains the authority for the concrete clone, bounded storage and Claude adapter implementations. + ## Display contract The screen renders the complete current plan: issue, summary, revision, open questions, ordered items, dependencies, files (including both sides of a rename) and acceptance checks. Navigation uses `?view=plans`, announces the active destination with `aria-current`, and preserves the existing Review composer DOM while the Plans screen is open. An authoritative Review refresh reconciles a settled Plans revision or load-failure status with the response it applies, but only while no newer plan operation owns that status. A failed Review refresh still clears unavailable plan data, but likewise replaces the Plans status only while it retains ownership. @@ -32,4 +34,4 @@ Suggestion replies render as plain text followed by one line-separated row per t `test/browser/plans-authoring.spec.ts` covers duplicate activation, focus and disabled styling, request-time guidance preservation, exact ambiguous-start replay, a start refused after an intervening import, malformed-status ownership, failed states with impossible output, consumed-result stale labeling, rejection of mismatched bindings, wrong-operation payloads, incomplete edit cards and incomplete nested draft data, hostile markup and attribute text in both result types, retained completed history and invalidated-history labeling, suggestion rows, whole-draft preview without mutation, dismissal and ambiguous-cancellation reconciliation without a revision change, an older poll returning after dismissal, the interleaving where an import advances the plan before an older ready poll returns, single-flight draft Apply with Review text and snippet preservation, committed-Apply reporting when the authoritative reload fails, and exact ambiguous Apply replay followed by sibling refresh and a second revision-bound Apply. -G4 completes real-provider/store integration and the remaining T18 browser/adapter gate. +`test/browser/plans-production.spec.ts` is G4's composed browser/adapter acceptance. It proves the production issue description, provider schema mount and read-only planning invocation feed F's durable request lifecycle; the browser can render and Apply both result types; and revisions 2 and 3 survive independent Store reopens and a server restart. `test/planning-provider.test.ts`, `test/planning-production.test.ts` and `test/planning-recorded.test.ts` retain the focused adapter, production-wiring and real recorded-output checks. `test/agent-planning.test.ts` retains the real-Docker vendor-egress and cleanup gate. diff --git a/test/browser/plans-production.spec.ts b/test/browser/plans-production.spec.ts new file mode 100644 index 00000000..2c3b59a6 --- /dev/null +++ b/test/browser/plans-production.spec.ts @@ -0,0 +1,183 @@ +import { expect, test, type Page } from '@playwright/test'; +import { lstatSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import type { InvocationHandle, InvocationInput } from '../../agents/contract.ts'; +import type { AgentAdapterRequest } from '../../agents/adapters/types.ts'; +import type { TaskFilesystems } from '../../agents/container/storage.ts'; +import type { EditReply, Plan } from '../../core/plan.ts'; +import type { AuthorProvider, AuthorRequest } from '../../core/planning-author.ts'; +import type { IssueAccess, IssueText } from '../../github/issues.ts'; +import type { MergeGateway } from '../../github/merge.ts'; +import { createPlanningProvider, type PlanningDependencies } from '../../runner/planning-provider.ts'; +import type { PlanningAgent } from '../../runner/planning.ts'; +import type { ReviewConfig, ReviewService } from '../../runner/review.ts'; +import { Store } from '../../runner/store.ts'; +import { createDemo } from '../../scripts/demo.ts'; +import { productionPlanning } from '../../web/planning.ts'; +import { startServer } from '../../web/server.ts'; + +let root: string; +let app: Awaited> | undefined; + +test.beforeEach(() => { root = mkdtempSync(join(tmpdir(), 'codeboost-plans-production-browser-')); }); +test.afterEach(async () => { + await app?.close(); + app = undefined; + rmSync(root, { recursive: true, force: true }); +}); + +function productionConfig(): ReviewConfig { + const demo = createDemo(join(root, 'review')); + return { ...demo, demo: false, github: { repository: 'acme/retry-service', pullRequest: 7, issue: 3 } }; +} + +const issueAccess = async (number: number): Promise => + ({ number, authorLogin: 'maintainer', collaborator: true }); +const issueText = async (number: number): Promise => + ({ number, title: 'Retries ignore the cap', body: 'Keep retry delays bounded.', comments: ['Preserve the public API.'] }); + +const mergeGateway: MergeGateway = { + inspect: async () => ({ base: '0'.repeat(40), head: '1'.repeat(40), pullRequestState: 'OPEN', mergeable: 'MERGEABLE', rulesKnown: true, + atomicBaseGuard: true, mergeQueue: false, requiredChecks: [], alreadyFixed: 'clear', pullRequest: 7, draft: false }), + merge: async () => ({ url: 'https://github.com/acme/retry-service/pull/7' }), +}; + +function suggestion(revision: number): EditReply { + return { schema_version: 1, base_revision: revision, + reply: 'Name the operator-visible retry ceiling.', edits: [{ + op: 'set_field', item: 'P1', summary: 'Clarify the retry ceiling', + reason: 'The current item should name the observable limit.', field: 'title', + value: `Expose the bounded retry ceiling at r${revision + 1}`, file: null, check: null, + check_index: null, depends_on: null, new_item: null, + }] }; +} + +function draft(current: Plan): Plan { + return { ...structuredClone(current), revision: current.revision + 1, + summary: `Production draft for revision ${current.revision + 1}` }; +} + +/** + * Lane D stand-ins keep this browser acceptance deterministic while exercising createPlanningProvider itself. + * The real-Docker test owns the concrete clone, storage and Claude adapter implementations. + */ +function dBackedAgent(service: ReviewService, observations: { + invocations: InvocationInput[]; schemas: string[]; prompts: string[]; releases: number[]; +}): PlanningAgent { + const outputs = new Map(); + const filesystems = { fixture: true } as unknown as TaskFilesystems; + const deps: PlanningDependencies = { + env: { CLAUDE_CODE_OAUTH_TOKEN: 'acceptance-token' }, + buildImage: () => `sha256:${'b'.repeat(64)}`, + measureRepository: () => ({ checkoutBytes: 1_024, entries: 3, objectBytes: 2_048 }), + createClone: options => ({ id: 'acceptance-clone', taskId: options.taskId, directory: options.parent, head: options.head }), + prepareFilesystems: () => filesystems, + removeFilesystems: released => { expect(released).toBe(filesystems); observations.releases.push(1); }, + capture: input => { observations.invocations.push(input); return Object.freeze(input); }, + startClaude: (request: AgentAdapterRequest, credential: string): InvocationHandle => { + expect(credential).toBe('acceptance-token'); + expect(lstatSync(request.inputDirectory).mode & 0o222).toBe(0); + observations.schemas.push(readFileSync(join(request.inputDirectory, 'schema.json'), 'utf8')); + observations.prompts.push(request.prompt); + const stdout = outputs.get(request.invocation.attemptId); + if (stdout === undefined) throw new Error('Missing acceptance output.'); + return { attemptId: request.invocation.attemptId, cancel: () => undefined, + settled: Promise.resolve({ attemptId: request.invocation.attemptId, context: request.invocation.context, + exitCode: 0, signal: null, stdout, stderr: '' }) }; + }, + }; + const snapshot = service.store.getSnapshot(service.config.identity); + const provider = createPlanningProvider({ vendor: 'claude', repository: service.reviewRepository().path, + head: snapshot.head, snapshotId: snapshot.id, runnerOwner: '0123456789abcdef0123456789abcdef', deps }); + const agent: AuthorProvider & { close(): Promise } = { + async invoke(request: AuthorRequest, signal: AbortSignal) { + const current = service.store.getPlan(request.identity); + const expected = request.mode === 'draft' ? current.revision + 1 : current.revision; + if (request.revision !== expected) throw new Error('Planning request revision does not match its mode.'); + outputs.set(request.requestId, JSON.stringify(request.mode === 'suggest' ? suggestion(request.revision) : draft(current))); + return provider.invoke(request, signal); + }, + async close() { /* createPlanningProvider has no process-level owner */ }, + }; + return agent as PlanningAgent; +} + +async function boot(config: ReviewConfig, observations: { + invocations: InvocationInput[]; schemas: string[]; prompts: string[]; releases: number[]; +}) { + const setup = productionPlanning(config, { verifyLock: () => undefined, issues: { issueAccess, issueText }, + agent: service => dBackedAgent(service, observations) }); + expect(setup).toBeDefined(); + if (!setup) throw new Error('Production planning was not configured.'); + app = await startServer(config, 0, undefined, mergeGateway, undefined, undefined, undefined, setup); +} + +async function openPlans(page: Page) { + await page.goto(app!.url); + await page.getByRole('link', { name: 'Plans', exact: true }).click(); +} + +function runningApp() { + if (!app) throw new Error('Acceptance server is not running.'); + return app; +} + +test('persists browser-applied provider suggestions and drafts through the production Store', async ({ page }) => { + const config = productionConfig(); + const observations = { invocations: [] as InvocationInput[], schemas: [] as string[], prompts: [] as string[], releases: [] as number[] }; + await boot(config, observations); + await openPlans(page); + + await page.getByLabel('Guidance').fill('Make the retry ceiling explicit.'); + await page.getByRole('button', { name: 'Suggest edits' }).click(); + await expect(page.getByRole('status').filter({ hasText: 'Suggestions ready for r1.' })).toBeVisible(); + await expect(page.getByRole('article', { name: 'Clarify the retry ceiling' })).toBeVisible(); + await page.getByRole('button', { name: 'Apply this edit', exact: true }).click(); + await expect(page.locator('#plans-revision')).toHaveText('r2'); + + await runningApp().close(); + app = undefined; + const afterSuggestion = new Store(config.database); + const persistedSuggestion = afterSuggestion.getPlan(config.identity); + expect(persistedSuggestion.revision).toBe(2); + expect(persistedSuggestion.items.find(item => item.id === 'P1')).toMatchObject({ + title: 'Expose the bounded retry ceiling at r2', + }); + afterSuggestion.close(); + + await boot(config, observations); + await openPlans(page); + await expect(page.locator('#plans-revision')).toHaveText('r2'); + await page.getByRole('button', { name: 'Draft next revision' }).click(); + await expect(page.getByRole('status').filter({ hasText: 'Draft ready for r2.' })).toBeVisible(); + await expect(page.getByRole('region', { name: 'Draft Production draft for revision 3' })).toBeVisible(); + await page.getByRole('button', { name: 'Apply draft', exact: true }).click(); + await expect(page.locator('#plans-revision')).toHaveText('r3'); + + await runningApp().close(); + app = undefined; + const reopened = new Store(config.database); + const persistedDraft = reopened.getPlan(config.identity); + expect(persistedDraft).toMatchObject({ revision: 3, summary: 'Production draft for revision 3' }); + expect(persistedDraft.items.find(item => item.id === 'P1')).toMatchObject({ + title: 'Expose the bounded retry ceiling at r2', + }); + reopened.close(); + expect(observations.invocations).toHaveLength(2); + expect(observations.invocations.map(value => ({ phase: value.phase, argv: value.approvedArgv, + revision: value.context.planRevision, snapshot: value.context.snapshotId }))).toEqual([ + { phase: 'planning', argv: [], revision: 1, snapshot: observations.invocations[0]!.context.snapshotId }, + { phase: 'planning', argv: [], revision: 3, snapshot: observations.invocations[0]!.context.snapshotId }, + ]); + expect(observations.schemas).toHaveLength(2); + expect(observations.schemas.map(schema => (JSON.parse(schema) as { title: string }).title)).toEqual([ + 'codeboost plan edit', 'codeboost plan', + ]); + for (const prompt of observations.prompts) { + expect(prompt).toContain('acme/retry-service'); + expect(prompt).toContain('Retries ignore the cap'); + } + expect(observations.prompts[0]).toContain('Make the retry ceiling explicit.'); + expect(observations.releases).toHaveLength(2); +}); From 23a17984cb44d3965c818fdfac197ccee8ffa5bd Mon Sep 17 00:00:00 2001 From: mchwang Date: Thu, 8 Oct 2026 15:57:24 -0700 Subject: [PATCH 2/3] Strengthen planning acceptance evidence --- docs/architecture.md | 2 +- docs/designs/codeboost-plan-indexed-review.md | 5 +++-- docs/implementation/planning-acceptance.md | 11 ++++++----- test/browser/plans-production.spec.ts | 8 ++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index e82074ff..7bd8f0de 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -413,7 +413,7 @@ These are the decisions that shape the structure. The full list, with the reason | F | Runner | Merged: F1 (lifecycle store, coordinator, shutdown wiring, startup recovery and the lock, planning API, feedback events, and stops that wait for their commit: #53, #74, #57, #59, #60, #96); F2a–F2b (execute prompts, audit, per-item execution, real workspace, durable safety findings: #67, #68, #92, #94, #128); F2d (already-fixed check, PR opening, branch pusher, production publishing, closing PRs on cancel: #84, #101, #110, #115, #116, #119, #123); production wiring and `start`/`resume` (#91, PRs #102, #105); planning through lane D (#117, #124, PRs #118, #120, #125); merging the task's own PR (#121, PR #126); start/resume approval, review-version, recovery and completed-prefix guards (#107, PR #130); continuation after a scope pause (#88, PR #134); the F3 durable trusted local-rebase foundation (#22, PR #136); the F4 foreign-conflict engine plus production sandbox adapter (#22, PRs #138 and #140); and sandboxed owned-commit conflict resolution (#22, PR #144). Next in #22: F5 owns current base/head refresh, rebase admission, post-rebase attribution and approval refresh, and head-bound `cmd:` execution. F6 owns rewritten-head push, fresh required-check refresh, the already-fixed rerun, and exact-pair guarded-merge handoff. The integrated flow pushes a rewritten head before it recomputes review state and reruns every plan item's head-bound `cmd:` checks; fresh required GitHub checks follow. | | Hardening | Allowlisted subprocess environments | Done: every Git command (#82, #83) and every `gh` runner (#84, #86). | | Tests | Test suite reliability | Done. The full suite runs ordinary tests first and Docker-backed files serially; CI and the real-Docker gate passed on PR #132 (#129). | -| G | Planning screen | Done (#145). The screen displays/imports the current plan, requests and renders a next-revision draft or suggestion cards, and applies results with revision-bound exact ambiguous replay. G4 composes the browser with the production planning setup, D-backed provider boundary, F endpoints and SQLite persistence; focused adapter and recorded-output suites plus the real-Docker planning gate cover the lower boundary. | +| G | Planning screen | G1 through G3 are built (#145). G4's composed browser acceptance now covers the production planning setup, D-backed provider boundary, F endpoints and SQLite persistence; exact-head real-Docker planning validation remains before Lane G and T18 are complete. | | H | Issue ranking and Issues screen | Done. H4b (#108, PR #139) records author-bound trust, guards runner and publishing boundaries, controls which comments reach prompts, records prompt comment evidence, and provides trust/untrust controls on the Issues screen. | | I, J | Queue and run windows; learning from feedback | Not started (after F) | diff --git a/docs/designs/codeboost-plan-indexed-review.md b/docs/designs/codeboost-plan-indexed-review.md index 5575805b..0260f28d 100644 --- a/docs/designs/codeboost-plan-indexed-review.md +++ b/docs/designs/codeboost-plan-indexed-review.md @@ -2057,10 +2057,11 @@ These `T` IDs are requirement identifiers, not the build-order numbers. Current - Surfaced by: L4 (D24: A) - Files: web/learning, runner/metrics - Verify: repeated feedback matching an approved lesson raises its count and flags it -- [ ] **T18 (P2, human: ~2 days / CC: ~45 min)** — core, agents, web — Plan schema: draft plans with either agent, import YAML or JSON, apply typed suggestions +- [ ] **T18 (P2, human: ~2 days / CC: ~45 min)** — core, agents, web — Plan schema: draft plans with the supported planning agent, import YAML or JSON, apply typed suggestions - Surfaced by: P1 (approved 2026-09-22) - Files: schema/, docs/plan-format.md, prompts/plan-author.md, core/plan (schema and meaning checks), agents/claude, agents/codex, web/plans (Import plan, suggestion cards) - - Verify: both examples pass and 8 broken plans fail; JSON-compatible YAML parsing is deterministic and rejects duplicate decoded keys, anchors/aliases, merge keys, tags, non-JSON scalars, excessive depth/size, and extra documents before validation; recorded Claude and Codex answers pass; an imported plan for #412 is rejected when the selected task is #413 without silently changing either issue; a declared final symlink can be deleted or safely retargeted while symlink parents and escaping targets are rejected; writes through declared links to undeclared files, directory children, or dangling targets fail the target snapshot audit before out-of-scope classification, tests, or commit; a plan with a `..` path or a dependency loop cannot be approved; the edit schema's copied definitions match; dependent add → edit and rename → edit plans pass projected-state validation, while missing sources and occupied destinations fail; a new field requires a new schema version and old plans validate against the immutable registry-selected snapshot before conversion; registry paths exist, snapshot IDs are version-qualified and unique, and unversioned CLI schemas exactly match the current snapshots; directory/root declarations and children beneath a file or submodule are rejected; malformed edit payloads and invalid resulting plans cannot be applied; a delayed suggestion captured for plan A at revision 3 with P1 cannot apply to plan B with the same revision/item IDs, and B remains unchanged; applying one card increments the revision and disables all old sibling cards until the person refreshes and reviews regenerated suggestions; concurrent/replayed Apply cannot create duplicate mutations; command chains and delimiter-escape payloads are rejected or remain data; appended flags such as `go test -exec` do not match a shorter allowlist entry and cannot execute; hostile filenames, branch names, script-derived argv values, approved lessons, and revision feedback remain escaped JSON data in the authoring prompt and cannot break its delimiters; `codex exec` runs with stdin closed + - Verify: both examples pass and 8 broken plans fail; JSON-compatible YAML parsing is deterministic and rejects duplicate decoded keys, anchors/aliases, merge keys, tags, non-JSON scalars, excessive depth/size, and extra documents before validation; recorded Claude answers pass, while every Codex planning request is refused before Docker or Git work under the later #75/#93 decision; an imported plan for #412 is rejected when the selected task is #413 without silently changing either issue; a declared final symlink can be deleted or safely retargeted while symlink parents and escaping targets are rejected; writes through declared links to undeclared files, directory children, or dangling targets fail the target snapshot audit before out-of-scope classification, tests, or commit; a plan with a `..` path or a dependency loop cannot be approved; the edit schema's copied definitions match; dependent add → edit and rename → edit plans pass projected-state validation, while missing sources and occupied destinations fail; a new field requires a new schema version and old plans validate against the immutable registry-selected snapshot before conversion; registry paths exist, snapshot IDs are version-qualified and unique, and unversioned CLI schemas exactly match the current snapshots; directory/root declarations and children beneath a file or submodule are rejected; malformed edit payloads and invalid resulting plans cannot be applied; a delayed suggestion captured for plan A at revision 3 with P1 cannot apply to plan B with the same revision/item IDs, and B remains unchanged; applying one card increments the revision and disables all old sibling cards until the person refreshes and reviews regenerated suggestions; concurrent/replayed Apply cannot create duplicate mutations; command chains and delimiter-escape payloads are rejected or remain data; appended flags such as `go test -exec` do not match a shorter allowlist entry and cannot execute; hostile filenames, branch names, script-derived argv values, approved lessons, and revision feedback remain escaped JSON data in the authoring prompt and cannot break its delimiters + - Evidence (2026-10-08, pending final gate): E1–E4 cover schema, parsing, prompt/response validation, recorded Claude output and Store orchestration; G1–G3 cover browser import, rendering, lifecycle and revision-bound Apply; G4's `plans-production.spec.ts` composes production issue description, lane D's provider boundary, F's HTTP endpoints and Store persistence through browser Apply, server restart and independent SQLite reopen. `planning-provider.test.ts`, `planning-production.test.ts` and `planning-recorded.test.ts` pass on the G4 head. Completion still waits for exact-head `agent-planning.test.ts` real-Docker validation after the active F5 owner releases Docker. ### Unresolved decisions diff --git a/docs/implementation/planning-acceptance.md b/docs/implementation/planning-acceptance.md index 21dbc8dc..0334933e 100644 --- a/docs/implementation/planning-acceptance.md +++ b/docs/implementation/planning-acceptance.md @@ -82,8 +82,9 @@ suggestion timer and the request share one ten-minute budget. The recording script still runs the provider in its own process and prints its runner label if it leaves anything. -F/G still own live API and UI integration. The Store now guarantees revision/snapshot -binding and pending-only settlement across processes, and E3 requires the caller's -snapshot identity before admission. G must not treat these fixture-only results as a -completed production planning milestone. T18/T9 remain incomplete until all assigned -lane slices pass their original acceptance. +The Store guarantees revision/snapshot binding and pending-only settlement across +processes, and E3 requires the caller's snapshot identity before admission. G4 now +composes the browser with production planning setup, lane D's provider boundary, +F's HTTP lifecycle and Store persistence in `plans-production.spec.ts`. T18 completes +when G4's exact head also passes the real-Docker planning gate above. T9 remains +incomplete until its remaining F6 common-CI and all-suite gate passes. diff --git a/test/browser/plans-production.spec.ts b/test/browser/plans-production.spec.ts index 2c3b59a6..84c98b4e 100644 --- a/test/browser/plans-production.spec.ts +++ b/test/browser/plans-production.spec.ts @@ -125,6 +125,9 @@ function runningApp() { test('persists browser-applied provider suggestions and drafts through the production Store', async ({ page }) => { const config = productionConfig(); + const baselineStore = new Store(config.database); + const baseRef = baselineStore.getSnapshot(config.identity).base; + baselineStore.close(); const observations = { invocations: [] as InvocationInput[], schemas: [] as string[], prompts: [] as string[], releases: [] as number[] }; await boot(config, observations); await openPlans(page); @@ -176,8 +179,13 @@ test('persists browser-applied provider suggestions and drafts through the produ ]); for (const prompt of observations.prompts) { expect(prompt).toContain('acme/retry-service'); + expect(prompt).toContain(baseRef); expect(prompt).toContain('Retries ignore the cap'); + expect(prompt).toContain('Keep retry delays bounded.'); + expect(prompt).toContain('Preserve the public API.'); } expect(observations.prompts[0]).toContain('Make the retry ceiling explicit.'); + expect(observations.prompts[1]).toContain('Expose the bounded retry ceiling at r2'); + expect(observations.prompts[1]).toContain('"revision":2'); expect(observations.releases).toHaveLength(2); }); From d22609c6e9ecaae3f52a0f33829d1908441833ab Mon Sep 17 00:00:00 2001 From: mchwang Date: Thu, 8 Oct 2026 16:14:41 -0700 Subject: [PATCH 3/3] Record completed Lane G acceptance --- docs/architecture.md | 2 +- docs/designs/codeboost-plan-indexed-review.md | 4 ++-- docs/implementation/planning-acceptance.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 7bd8f0de..0c003a60 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -413,7 +413,7 @@ These are the decisions that shape the structure. The full list, with the reason | F | Runner | Merged: F1 (lifecycle store, coordinator, shutdown wiring, startup recovery and the lock, planning API, feedback events, and stops that wait for their commit: #53, #74, #57, #59, #60, #96); F2a–F2b (execute prompts, audit, per-item execution, real workspace, durable safety findings: #67, #68, #92, #94, #128); F2d (already-fixed check, PR opening, branch pusher, production publishing, closing PRs on cancel: #84, #101, #110, #115, #116, #119, #123); production wiring and `start`/`resume` (#91, PRs #102, #105); planning through lane D (#117, #124, PRs #118, #120, #125); merging the task's own PR (#121, PR #126); start/resume approval, review-version, recovery and completed-prefix guards (#107, PR #130); continuation after a scope pause (#88, PR #134); the F3 durable trusted local-rebase foundation (#22, PR #136); the F4 foreign-conflict engine plus production sandbox adapter (#22, PRs #138 and #140); and sandboxed owned-commit conflict resolution (#22, PR #144). Next in #22: F5 owns current base/head refresh, rebase admission, post-rebase attribution and approval refresh, and head-bound `cmd:` execution. F6 owns rewritten-head push, fresh required-check refresh, the already-fixed rerun, and exact-pair guarded-merge handoff. The integrated flow pushes a rewritten head before it recomputes review state and reruns every plan item's head-bound `cmd:` checks; fresh required GitHub checks follow. | | Hardening | Allowlisted subprocess environments | Done: every Git command (#82, #83) and every `gh` runner (#84, #86). | | Tests | Test suite reliability | Done. The full suite runs ordinary tests first and Docker-backed files serially; CI and the real-Docker gate passed on PR #132 (#129). | -| G | Planning screen | G1 through G3 are built (#145). G4's composed browser acceptance now covers the production planning setup, D-backed provider boundary, F endpoints and SQLite persistence; exact-head real-Docker planning validation remains before Lane G and T18 are complete. | +| G | Planning screen | Done (#145). The screen displays/imports the current plan, requests and renders a next-revision draft or suggestion cards, and applies results with revision-bound exact ambiguous replay. G4 composes the browser with the production planning setup, D-backed provider boundary, F endpoints and SQLite persistence; focused adapter, recorded-output and exact-head real-Docker planning suites cover the lower boundary. | | H | Issue ranking and Issues screen | Done. H4b (#108, PR #139) records author-bound trust, guards runner and publishing boundaries, controls which comments reach prompts, records prompt comment evidence, and provides trust/untrust controls on the Issues screen. | | I, J | Queue and run windows; learning from feedback | Not started (after F) | diff --git a/docs/designs/codeboost-plan-indexed-review.md b/docs/designs/codeboost-plan-indexed-review.md index 0260f28d..7fa45989 100644 --- a/docs/designs/codeboost-plan-indexed-review.md +++ b/docs/designs/codeboost-plan-indexed-review.md @@ -2057,11 +2057,11 @@ These `T` IDs are requirement identifiers, not the build-order numbers. Current - Surfaced by: L4 (D24: A) - Files: web/learning, runner/metrics - Verify: repeated feedback matching an approved lesson raises its count and flags it -- [ ] **T18 (P2, human: ~2 days / CC: ~45 min)** — core, agents, web — Plan schema: draft plans with the supported planning agent, import YAML or JSON, apply typed suggestions +- [x] **T18 (P2, human: ~2 days / CC: ~45 min)** — core, agents, web — Plan schema: draft plans with the supported planning agent, import YAML or JSON, apply typed suggestions - Surfaced by: P1 (approved 2026-09-22) - Files: schema/, docs/plan-format.md, prompts/plan-author.md, core/plan (schema and meaning checks), agents/claude, agents/codex, web/plans (Import plan, suggestion cards) - Verify: both examples pass and 8 broken plans fail; JSON-compatible YAML parsing is deterministic and rejects duplicate decoded keys, anchors/aliases, merge keys, tags, non-JSON scalars, excessive depth/size, and extra documents before validation; recorded Claude answers pass, while every Codex planning request is refused before Docker or Git work under the later #75/#93 decision; an imported plan for #412 is rejected when the selected task is #413 without silently changing either issue; a declared final symlink can be deleted or safely retargeted while symlink parents and escaping targets are rejected; writes through declared links to undeclared files, directory children, or dangling targets fail the target snapshot audit before out-of-scope classification, tests, or commit; a plan with a `..` path or a dependency loop cannot be approved; the edit schema's copied definitions match; dependent add → edit and rename → edit plans pass projected-state validation, while missing sources and occupied destinations fail; a new field requires a new schema version and old plans validate against the immutable registry-selected snapshot before conversion; registry paths exist, snapshot IDs are version-qualified and unique, and unversioned CLI schemas exactly match the current snapshots; directory/root declarations and children beneath a file or submodule are rejected; malformed edit payloads and invalid resulting plans cannot be applied; a delayed suggestion captured for plan A at revision 3 with P1 cannot apply to plan B with the same revision/item IDs, and B remains unchanged; applying one card increments the revision and disables all old sibling cards until the person refreshes and reviews regenerated suggestions; concurrent/replayed Apply cannot create duplicate mutations; command chains and delimiter-escape payloads are rejected or remain data; appended flags such as `go test -exec` do not match a shorter allowlist entry and cannot execute; hostile filenames, branch names, script-derived argv values, approved lessons, and revision feedback remain escaped JSON data in the authoring prompt and cannot break its delimiters - - Evidence (2026-10-08, pending final gate): E1–E4 cover schema, parsing, prompt/response validation, recorded Claude output and Store orchestration; G1–G3 cover browser import, rendering, lifecycle and revision-bound Apply; G4's `plans-production.spec.ts` composes production issue description, lane D's provider boundary, F's HTTP endpoints and Store persistence through browser Apply, server restart and independent SQLite reopen. `planning-provider.test.ts`, `planning-production.test.ts` and `planning-recorded.test.ts` pass on the G4 head. Completion still waits for exact-head `agent-planning.test.ts` real-Docker validation after the active F5 owner releases Docker. + - Evidence (2026-10-08): E1–E4 cover schema, parsing, prompt/response validation, recorded Claude output and Store orchestration; G1–G3 cover browser import, rendering, lifecycle and revision-bound Apply; G4's `plans-production.spec.ts` composes production issue description, lane D's provider boundary, F's HTTP endpoints and Store persistence through browser Apply, server restart and independent SQLite reopen. `planning-provider.test.ts`, `planning-production.test.ts` and `planning-recorded.test.ts` pass on the G4 head, and exact-head `agent-planning.test.ts` passes the real-Docker vendor-egress and cleanup gate. ### Unresolved decisions diff --git a/docs/implementation/planning-acceptance.md b/docs/implementation/planning-acceptance.md index 0334933e..3f23eb24 100644 --- a/docs/implementation/planning-acceptance.md +++ b/docs/implementation/planning-acceptance.md @@ -85,6 +85,6 @@ it leaves anything. The Store guarantees revision/snapshot binding and pending-only settlement across processes, and E3 requires the caller's snapshot identity before admission. G4 now composes the browser with production planning setup, lane D's provider boundary, -F's HTTP lifecycle and Store persistence in `plans-production.spec.ts`. T18 completes -when G4's exact head also passes the real-Docker planning gate above. T9 remains -incomplete until its remaining F6 common-CI and all-suite gate passes. +F's HTTP lifecycle and Store persistence in `plans-production.spec.ts`. The exact-head +real-Docker planning gate also passes, completing T18. T9 remains incomplete until +its remaining F6 common-CI and all-suite gate passes.