Skip to content

finalize(v0.20.1): first-run polish + a11y + distribution audit - #30

Merged
omerakben merged 37 commits into
mainfrom
finalize/v0.20.1-first-run-polish
May 14, 2026
Merged

finalize(v0.20.1): first-run polish + a11y + distribution audit#30
omerakben merged 37 commits into
mainfrom
finalize/v0.20.1-first-run-polish

Conversation

@omerakben

@omerakben omerakben commented May 14, 2026

Copy link
Copy Markdown
Owner

DO NOT MERGE

Summary

Finalizes the v0.20.1 first-run polish and distribution audit fixes.

  • Closes all block-ship first-run findings from FIRST_RUN_AUDIT.md.
  • Adds line-specific intervention pointers, SIGTERM STOP handling, first-run fake fixture gating, release smoke SHA fallbacks, GUI live-run routing, fixture-run approval refusal, and GUI a11y coverage.
  • Updates docs and release guidance for provider setup, GUI startup, brownfield scope, and v0.20.1 notes.

Fix matrix

See docs/handoffs/codex-finalize/FIRST_RUN_FIXES.md.

Verification

  • bun test: 3390 pass / 2 skip / 0 fail.
  • bun run typecheck: clean.
  • bun run build:binaries: produced dist/code-oz-v0.20.0-alpha.0-handoff.tar.gz.
  • bun run smoke: passed against the handoff tarball.
  • code-oz-gui: bun test, bun run typecheck, and bun run test:e2e passed.
  • code-oz-gui: env DISABLE_HMR=true bun run test:a11y passed outside the sandbox after sandboxed Chromium hit a macOS Mach-port permission failure.

Notes

  • Standalone a11y was also covered inside bun run test:e2e.
  • Do not merge until CI is green and Ozzy explicitly approves merge.

Summary by CodeRabbit

  • New Features

    • resume command to continue interrupted runs; release workflow now smoke-tests packaged tarballs before publish; deterministic "failure-gates" demo and CLI demo added.
    • Playwright accessibility baseline tests (axe) added.
  • Bug Fixes

    • Improved CLI binary resolution and provider auth-expiration detection; interrupt handling reliably records stop events.
    • Approval endpoints now reject fixture-mode approvals and return clearer 409/503 responses.
  • Documentation

    • Major README/trust/security/provider setup updates, changelog and release notes for v0.20.1.
    • New CONTRIBUTING, SECURITY, TRUST, and demo docs.
  • UI / Accessibility

    • Improved focus management, reduced-motion support, text wrapping, contrast/typography tweaks, and clearer provider-family display.

Review Change Stack

Copilot AI review requested due to automatic review settings May 14, 2026 00:49
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 623e8363-8b89-4785-9de9-a04e75dac80e

📥 Commits

Reviewing files that changed from the base of the PR and between deabc46 and c1f412b.

📒 Files selected for processing (58)
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/demo_failure.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/ISSUE_TEMPLATE/install_problem.yml
  • .github/pull_request_template.md
  • CHANGELOG.md
  • CLAUDE.md
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • docs/ABOUT.md
  • docs/TRUST.md
  • docs/benchmarks/agent-gate-bench.md
  • docs/code-oz-gpt-pro-research-prompt.md
  • docs/comparisons/ai-coding-agents.md
  • docs/contracts/PROVIDERS.md
  • docs/demo/02-failure-gates/README.md
  • docs/demo/02-failure-gates/fixtures/01-tampered-artifact/SPEC.md
  • docs/demo/02-failure-gates/fixtures/02-scope-escape/SPEC.md
  • docs/demo/02-failure-gates/fixtures/03-verify-fail/SPEC.md
  • docs/demo/02-failure-gates/fixtures/04-same-family-review/SPEC.md
  • docs/demo/02-failure-gates/fixtures/05-reviewer-blocks-risk/SPEC.md
  • docs/demo/02-failure-gates/output/01-tampered-artifact/actual.txt
  • docs/demo/02-failure-gates/output/01-tampered-artifact/events-sketch.jsonl
  • docs/demo/02-failure-gates/output/02-scope-escape/actual.txt
  • docs/demo/02-failure-gates/output/02-scope-escape/events-sketch.jsonl
  • docs/demo/02-failure-gates/output/03-verify-fail/NEEDS_INTERVENTION.json
  • docs/demo/02-failure-gates/output/03-verify-fail/actual.txt
  • docs/demo/02-failure-gates/output/03-verify-fail/events-sketch.jsonl
  • docs/demo/02-failure-gates/output/04-same-family-review/actual.txt
  • docs/demo/02-failure-gates/output/04-same-family-review/events-sketch.jsonl
  • docs/demo/02-failure-gates/output/05-reviewer-blocks-risk/REVIEW.md
  • docs/demo/02-failure-gates/output/05-reviewer-blocks-risk/actual.txt
  • docs/demo/02-failure-gates/output/05-reviewer-blocks-risk/events-sketch.jsonl
  • docs/design/CODEX_BRIEFING_V0_20_1_POLISH.md
  • docs/design/CODEX_RESPONSE_V0_20_1_POLISH_R0.md
  • docs/design/CODEX_RESPONSE_V0_20_1_POLISH_R1.md
  • docs/design/CODEX_RESPONSE_V0_20_1_POLISH_R2.md
  • docs/design/ROADMAP.md
  • docs/design/V0_20_1_FIRST_RUN_POLISH_DESIGN.md
  • docs/handoffs/2026-05-14-v0.20.0-release-notes-backfill.md
  • docs/handoffs/2026-05-14-v0.20.1-release-notes.md
  • docs/handoffs/2026-05-14-v0.20.1-tech-debt.md
  • docs/planning/V0_20_1_POLISH_PLAN.md
  • npm-wrapper/index.cjs
  • package.json
  • scripts/build-binaries.ts
  • scripts/demo/02-failure-gates/run-demo.ts
  • scripts/install.sh
  • scripts/release/fresh-clone-smoke.sh
  • src/cli.ts
  • src/config/schema.ts
  • tests/cli-init.test.ts
  • tests/demo/failure-gates.test.ts
  • tests/m5-fix-first.test.ts
  • tests/smoke-test.test.ts

📝 Walkthrough

Walkthrough

This PR hardens first-run CLI and distribution flows, adds intervention event pointers and stop gates, updates GUI live-run and accessibility behavior, expands tests and fixtures, introduces staged-release tarball smoke checks, and refreshes release/provider/trust documentation.

Changes

First-run polish

Layer / File(s) Summary
Release docs and product guidance
README.md, docs/ABOUT.md, docs/PROVIDER_SETUP.md, CHANGELOG.md, docs/design/*, docs/handoffs/*, docs/TRUST.md, SECURITY.md, CONTRIBUTING.md
Updated product framing, release notes, provider setup guidance, trust/security documents, changelog, and extensive handoff/audit reports for the v0.20.1 first-run polish.
Distribution integrity and release smoke checks
.github/workflows/release.yml, scripts/build-binaries.ts, scripts/smoke-test.ts, npm-wrapper/index.cjs, scripts/install.sh, scripts/release/fresh-clone-smoke.sh, tests/ci-workflows.test.ts, tests/npm-wrapper.test.ts
Switched packaging to a handoff tarball naming scheme; added CI smoke step that validates staged tarballs (manifest/sha/contents and conditional runtime init); strengthened npm-wrapper and installer checksum/HTTPS behavior and added recovery hints; added fresh-clone smoke script and CI workflow test.
Intervention event pointers and gate validation
src/state/events.ts, src/state/gates.ts, src/state/schemas.ts, src/phases/*, src/providers/invoke.ts, src/worktree/load-or-create-run-worktree.ts, tests/state-gates.test.ts, tests/build-phase.test.ts
appendEvent() now returns line numbers; intervention writers append events first and write NEEDS_INTERVENTION gates including eventPointer and createdAt; gate validation rejects empty actionableSuggestions and requires line-scoped eventPointer.
CLI first-run execution and provider diagnostics
src/cli.ts, src/commands/run.ts, src/commands/doctor.ts, src/providers/* (claude/codex/first-run-fake-fixture), tests/cli-run-args.test.ts, tests/commands-run.test.ts, tests/commands-doctor.test.ts, tests/providers-*.test.ts
Added resume alias; parseRunArgs supports --resume and legacy effort aliases; installed interrupt stop-gate writers; runtime fake-provider fallback and deterministic first-run fake fixture; doctor aggregates sub-command checks and supports help flags; provider health/invoke classify expired-auth separately.
GUI live-run wiring and run metadata
code-oz-gui/app/api/*, code-oz-gui/lib/* (repo-root, code-oz-spawn, run-store, types), code-oz-gui/app/page.tsx, code-oz-gui/app/globals.css, code-oz-gui/lib/run-registry.ts, code-oz-gui/tests/unit/*
Backend: resolve repo root for CLI, version-aware spawn, run-scoped approval writing, expose providerProvenance; API routes return clearer helper-unavailable errors for missing GEMINI key and handle approve flows (live vs fixture) including invoking code-oz approve.
GUI presentation, accessibility, fixtures, and harness
code-oz-gui/components/*, code-oz-gui/fixtures/*, code-oz-gui/playwright.config.ts, code-oz-gui/scripts/capture-screenshots.ts, code-oz-gui/tests/e2e/*, code-oz-gui/package.json, code-oz-gui/eslint.config.mjs
UI tweaks to provider-family display, decision matching, drawer focus-trap and a11y, reduced-motion CSS, workspace submission handling, fixture updates, Playwright webServer/baseURL, screenshot capture improvements, and added e2e a11y tests and GUI unit tests.
Demo fixtures and tests
scripts/demo/02-failure-gates/run-demo.ts, docs/demo/02-failure-gates/*, tests/demo/failure-gates.test.ts
Added deterministic failure-gates demo with five fixtures, demo runner, fixture specs and output, and characterization tests asserting production gate enforcement paths.
Misc tests and CI
tests/* (various additions/updates)
Added and updated unit, integration, and e2e tests to validate new behaviors: approve route, code-oz binary resolution, helper ask missing-key behavior, run-store approval writes, CLI parsing, interrupt gate behavior, smoke-test expectations, homebrew audit guidance, and others.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • omerakben/code-oz#30: This PR is the current head PR; it contains the staged tarball smoke verification plus the CLI/GUI/run-store changes represented above.

🐇 I sniffed the tarballs, chased event lines,
I hopped through docs and fixed some signs.
The drawer now keeps focus true and tight,
Fake runs hum steady in the night.
Tests and demos tidy up the burrow's light.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch finalize/v0.20.1-first-run-polish

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Finalizes v0.20.1 first-run polish by closing block-ship audit findings: tightens the NEEDS_INTERVENTION gate contract (mandatory line-specific event pointer + non-empty actionable suggestions), hardens the npm wrapper and install script around download/cache integrity, adds SIGTERM STOP handling, gates the first-run fake fixture, refactors the GUI live-run/approve routing with a11y improvements, and renames the build artifact to a unified handoff tarball with cross-platform smoke verification in CI.

Changes:

  • State contract: NeedsInterventionGate.eventPointer is now required and validated, and all writers compute the event line before writing the gate; non-empty actionableSuggestions are enforced.
  • Distribution: npm wrapper enforces HTTPS, verifies cached binaries against a .sha256 sidecar, and the install script / release workflow gain retry/manual hints and cross-platform sha256 verification; tarball renamed to *-handoff.tar.gz.
  • CLI/GUI UX: code-oz resume alias, --effort low|medium|high deprecation aliases, bare doctor aggregate report, GUI live-run routing with fixture-run approval refusal, Drawer focus trap fix, and a11y test wiring.

Reviewed changes

Copilot reviewed 85 out of 91 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/state/schemas.ts Adds required eventPointer to NeedsInterventionGate.
src/state/gates.ts Enforces non-empty suggestions and line-specific event pointer regex.
src/state/events.ts appendEvent now returns the appended line number.
src/worktree/load-or-create-run-worktree.ts Computes event line first, populates eventPointer on the gate.
tests/state-gates.test.ts Tests for new validator rules.
tests/providers-codex.test.ts, tests/providers-claude.test.ts Coverage for provider_auth_expired mapping.
tests/npm-wrapper.test.ts Cache-tamper rejection and HTTPS-only enforcement tests.
tests/install-script.test.ts Asserts retry/manual guidance in download failure stderr.
tests/homebrew-formula.test.ts Asserts tap/name brew audit syntax.
tests/commands-run.test.ts Effort-alias deprecation, fake-fixture default, resume alias, SIGTERM STOP gate.
tests/commands-doctor.test.ts Bare doctor aggregate UX test.
tests/cli-run-args.test.ts --resume and effort-alias parsing.
tests/ci-workflows.test.ts Smoke step expectations in release workflow.
tests/build-phase.test.ts Asserts non-empty suggestions on intervention.
tests/build-binaries.test.ts Updates to -handoff tarball naming.
Files not reviewed (1)
  • code-oz-gui/fixtures/sample-run/events.jsonl: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/state/events.ts
Comment on lines +2873 to +2888
async function nextEventLineNumber(file: string): Promise<number> {
let content: string
try {
content = await readFile(file, 'utf8')
} catch (err: unknown) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
return 1
}
throw err
}
if (content.length === 0) {
return 1
}
const canonical = content.endsWith('\n') ? content.slice(0, -1) : content
return canonical.split('\n').length + 1
}

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request delivers a comprehensive "first-run polish" for the code-oz tool, resolving distribution, accessibility, and error-handling issues identified in a recent audit. Key updates include a cost-free fake provider fallback for initial CLI runs, improved binary resolution for the GUI, and SHA verification for cached binaries in the npm wrapper. Accessibility is bolstered by a complete focus trap implementation, reduced-motion support, and automated axe-core testing. The CLI now handles interrupts more gracefully and provides line-specific pointers in intervention reports. Reviewer feedback recommends adding a redirect limit to the downloader, ensuring a run_ended event is emitted upon interruption, and optimizing the event log line-counting logic for better performance.

Comment thread npm-wrapper/index.cjs
return parsed
}

async function download(url, destination) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The download function recursively follows redirects but does not implement a limit on the number of redirects. This could lead to infinite loops or stack overflow if a server is misconfigured or malicious. It is recommended to add a redirect counter and cap it at a reasonable value (e.g., 5).

Comment thread src/commands/run.ts
Comment on lines +431 to +436
export async function writeInterruptStopGate(
runPaths: RunPaths,
runId: string,
signal: 'SIGINT' | 'SIGTERM' = 'SIGINT',
now: () => string = () => new Date().toISOString(),
): Promise<void> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The writeInterruptStopGate function writes the STOP.json gate file but does not append a corresponding run_ended event to the event log. As noted in the audit report (F6.3), a clean stop should include both the gate file and an event so that state projections (like the GUI) can accurately reflect the run's outcome. Consider appending a run_ended event with outcome: 'stopped' or 'aborted' before writing the gate.

Comment thread src/state/events.ts
Comment on lines +2873 to +2888
async function nextEventLineNumber(file: string): Promise<number> {
let content: string
try {
content = await readFile(file, 'utf8')
} catch (err: unknown) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
return 1
}
throw err
}
if (content.length === 0) {
return 1
}
const canonical = content.endsWith('\n') ? content.slice(0, -1) : content
return canonical.split('\n').length + 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The nextEventLineNumber function reads the entire event log file into memory and splits it by newlines to count lines. This is an O(N) operation that makes appendEvent O(N) as well, leading to O(N^2) performance for a full run. For large event logs, this will consume significant memory and slow down the CLI. Consider using a stream-based approach to count lines or tracking the line count in the InvokeContext to keep appends efficient.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: deabc4653a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/run.ts
Comment on lines +411 to +413
const health = await registry.get(provider).health()
if (health.authStatus !== 'ok') return 'fake'
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict fake-provider fallback to explicit missing auth

defaultToFakeIfRequiredProvidersUnavailable currently forces providerOverride='fake' for any status other than ok, including transient unknown health results (for example, temporary CLI/HTTP failures). Because this override is computed before active-run handling and then passed into resume dispatch, a real run can silently switch to FakeProvider mid-run and continue with fixture responses instead of surfacing the provider outage, which can corrupt artifacts and mask production failures.

Useful? React with 👍 / 👎.

Comment thread src/commands/run.ts
Comment on lines +756 to +757
if (value in aliases) {
return { kind: 'ok', value: aliases[value]!, alias: value }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use own-key checks for effort aliases

The alias parser uses value in aliases, which also matches prototype keys like __proto__; that path returns kind: 'ok' with a non-effort value, so invalid input bypasses validation and later budget scaling can receive an invalid multiplier instead of failing with a usage error. This should use an own-property check (or explicit key list) so only low|medium|high are accepted aliases.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/build-binaries.ts (1)

463-466: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update stale tarball error wording to match handoff flow.

Line 465 still says “Darwin tarball layout,” but this path now stages the handoff tarball. This will mislead debugging when packaging fails.

Suggested fix
-      errors: [`build-binaries: failed to stage Darwin tarball layout: ${formatUnknownError(err)}`],
+      errors: [`build-binaries: failed to stage handoff tarball layout: ${formatUnknownError(err)}`],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build-binaries.ts` around lines 463 - 466, The error message returned
from the staging step is stale: replace the phrase "Darwin tarball layout" with
wording that reflects the handoff tarball (e.g., "handoff tarball layout") where
the return object is constructed in scripts/build-binaries.ts so failures report
the correct artifact; keep the existing formatUnknownError(err) usage and
overall return shape intact.
src/state/events.ts (1)

2821-2888: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid rescanning events.jsonl on every append.

appendEvent() is now doing a full readFile() + line split before every write, and that work happens on the same hot path that every phase uses to emit events. As the log grows, appends become O(n) and the full run becomes O(n²), while also holding the per-run lock longer for other writers. Consider making pointer generation opt-in or persisting a cursor/sidecar instead of recounting the whole file each time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/state/events.ts` around lines 2821 - 2888, appendEvent currently calls
nextEventLineNumber which does a full readFile+split of events.jsonl on every
append, making appends O(n); change this by making line-number generation
optional and/or by persisting a cursor/sidecar so we avoid rescanning the whole
file on each write: add an option flag (e.g.,
AppendEventOptions.generateLineNumber) defaulting to false or preserve existing
behavior when explicitly requested, and implement a sidecar counter (e.g.,
events.jsonl.count) updated atomically inside writeOnce/withLock (or maintain a
process-scoped in-memory counter initialized from the sidecar on startup) so
appendEvent and nextEventLineNumber no longer call readFile on every append;
ensure the LockBusyError and error-handling paths still work and update
references to nextEventLineNumber/appendEvent to use the sidecar/in-memory
cursor when the opt-in flag is not set.
🧹 Nitpick comments (3)
code-oz-gui/tests/unit/approve-route.test.ts (1)

17-28: ⚡ Quick win

Add coverage for the new live approve execution branches.

This test only locks fixture-mode refusal. Please add cases for live approve success, live approve failure (approve-failed), and unknown runId (404) so the new route control-flow is protected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code-oz-gui/tests/unit/approve-route.test.ts` around lines 17 - 28, Add unit
tests in approve-route.test.ts next to the existing fixture-mode test to cover
live-run branches: (1) a successful live approve where
POST(approveRequest('approve')) with params resolving to a real runId returns
200 and expected success payload, (2) a live approve failure where the service
returns the domain error and response.status is 409 with body.error ===
'approve-failed', and (3) an unknown runId where POST with params resolving to a
non-existent runId yields 404. Reuse the existing helpers POST and
approveRequest, and assert on response.status and body.error/body.detail as done
for FIXTURE_RUN_ID; mock or seed the run state so the tests exercise the live
approval code paths rather than fixture-mode refusal.
code-oz-gui/tests/e2e/happy-path.e2e.ts (1)

32-45: 💤 Low value

Consider splitting this test for better maintainability.

The test has expanded beyond its original scope ("renders, opens drawer, switches tabs persistently, helper expand/collapse") to include testing multiple decision types across different cards. While the current implementation works, splitting into focused tests (e.g., "renders provider provenance", "displays cross-family review decisions", "shows budget alerts") would improve maintainability and make test failures easier to diagnose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code-oz-gui/tests/e2e/happy-path.e2e.ts` around lines 32 - 45, The e2e test
in happy-path.e2e.ts has grown too broad; split the wide scenario into focused
tests by extracting the blocks that interact with reviewCard and buildCard into
separate test cases: one test should cover the "Write failing RED test for the
Safari iOS bug" flow (use reviewCard, open Decisions tab via
dialog.getByRole('tab', { name: 'Decisions' }), and assert the CROSS-FAMILY
REVIEW and AI DEBATE decision texts), and another test should cover the "Surface
failure instead of silently swallowing in finalizeCart" flow (use buildCard,
open Decisions tab, and assert the BUDGET ALERT text). Keep shared setup (page
keyboard Escape, scrollIntoViewIfNeeded) in beforeEach or a small helper to
avoid duplication and name tests clearly (e.g., "displays cross-family review
decisions" and "shows budget alerts"); ensure each new test uses the same
selectors (reviewCard, buildCard, dialog) so behavior remains identical.
tests/commands-run.test.ts (1)

240-240: ⚡ Quick win

Prefer process.execPath for consistency.

Line 93 was updated to use process.execPath instead of the 'bun' string literal for more robust runtime resolution. This spawn should follow the same pattern.

♻️ Suggested fix
-    cmd: ['bun', '--eval', script],
+    cmd: [process.execPath, '--eval', script],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/commands-run.test.ts` at line 240, The test currently spawns using the
literal 'bun' in the cmd array (cmd: ['bun', '--eval', script]); update that to
use process.execPath for consistent runtime resolution by replacing the 'bun'
string with process.execPath so the spawn uses the current Node executable;
ensure the same change is applied wherever the cmd array is constructed in
tests/commands-run.test.ts (refer to the cmd variable and the script argument).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 136-141: The smoke step currently checks README.md and install.sh
on disk but not inside the tarball; update the validation to verify those files
are present in the archive by adding tar -tzf "${ASSET_NAME}" | grep -F
"${STAGE_NAME}/README.md" and tar -tzf "${ASSET_NAME}" | grep -F
"${STAGE_NAME}/install.sh" (use the same ASSET_NAME and STAGE_NAME variables as
the other tar checks) and keep or reorder the existing test -s
"${STAGE_NAME}/README.md" and test -x "${STAGE_NAME}/install.sh" as on-disk
sanity checks.

In `@code-oz-gui/lib/code-oz-spawn.ts`:
- Around line 545-549: The error-aggregation loses data because stdout/stderr
may be Buffer objects when execFileAsync (called earlier) omits an encoding;
update the error handling in code-oz-spawn.ts (the block that derives output,
stderr, stdout, message) to detect Buffer-like values (e.g.,
Buffer.isBuffer(output.stderr) / Buffer.isBuffer(output.stdout) or typeof
.toString === 'function') and convert them to strings (e.g.,
.toString('utf8').trim()) before joining; keep the existing fallback for
non-string/non-Buffer cases and preserve the existing message fallback ("code-oz
approve failed") for output.message.

In `@code-oz-gui/package.json`:
- Line 12: The "clean" npm script currently uses the unsupported command "next
clean"; update the "clean" script (the package.json "clean" entry) to use a
valid cleanup command such as removing Next's build artifacts (for example
replace "next clean" with a platform-safe remover like "rimraf .next" and add
rimraf as a devDependency) or simply remove the script—ensure the change targets
the "clean" script and does not rely on the non-existent "next clean" CLI
command.

In `@docs/design/RELEASE_NOTES_v0.20.1-alpha.0.md`:
- Around line 40-44: Update the release notes checklist to match the actual
verification results: change the `bun test` total threshold from `>=3366` to
`>=3390` and replace the `bun run scripts/smoke-test.ts` entry with `bun run
smoke` so the lines referencing `bun test` and `bun run scripts/smoke-test.ts`
reflect the reported `3390` passing tests and the actual smoke command used in
the release check.

In `@docs/handoffs/codex-finalize/A10-hygiene.md`:
- Around line 191-192: The Markdown table cells at the two lines include inline
code containing unescaped pipe characters which breaks table parsing (MD056);
update the table by escaping each pipe inside the inline code (e.g., replace |
with \| in the command strings like the `git grep -nE 'TODO\|FIXME\|XXX'` entry
and the longer command containing `:!code-oz-gui/bun.lock`), or alternatively
move those commands out of the table into a fenced code block so the `git grep
-nE 'TODO|FIXME|XXX'` and the `git grep -nE 'TODO|FIXME|XXX' -- src tests
code-oz-gui ':!code-oz-gui/bun.lock'` lines render without breaking the table.
- Line 10: The Summary line currently reads “Four findings” and shows a 4-item
severity mix that does not match the five findings documented (F10.1–F10.5);
update the header to state “Five findings” and correct the severity breakdown to
reflect the actual counts across F10.1–F10.5 (adjust the numbers so they sum to
five and match the labels used elsewhere in the doc).

In `@docs/handoffs/codex-finalize/CODEX_GOAL_R2.txt`:
- Around line 24-26: The BLOCK-PUSH items are inconsistently labeled: after
"BP-1"…"BP-6" the next items are labeled "B3" and "B4"; change those two labels
to "BP-7" and "BP-8" respectively so the sequence remains contiguous and
unambiguous (update the occurrences of "B3" → "BP-7" and "B4" → "BP-8" in the
BLOCK-PUSH section, preserving the existing lines about the approve/revise
fixture behavior and Drawer focus-trap details).

In `@docs/handoffs/codex-finalize/FIRST_RUN_AUDIT.md`:
- Line 8: Update the Phase 1 audit summary sentence to reflect the actual counts
in the findings table: change "45 findings: 12 block-ship, 29 fix-soon, and 4
nit" to "43 findings: 12 block-ship, 27 fix-soon, and 4 nit" so the summary
matches the findings table; ensure the sentence in the document header that
references the Phase 1 audit totals is the one you edit.

In `@npm-wrapper/index.cjs`:
- Around line 94-102: The redirect handling in download() calls itself
recursively without a limit, risking infinite loops; modify download (and its
callers) to accept a redirect counter (e.g., redirectCount with default 0) and a
MAX_REDIRECTS constant, increment redirectCount on each redirect, and before
recursing check if redirectCount >= MAX_REDIRECTS and reject with a clear error
(e.g., "too many redirects"); keep the existing protocol check for
redirected.protocol and pass updated redirectCount when calling
download(redirected.href, destination).

In `@src/phases/build.ts`:
- Around line 1003-1027: The guidance in buildInterventionSuggestions for the
codes 'build_plan_missing' and 'build_patch_apply_failed' assumes PLAN.md and
BUILD_REPORT.md exist; change those branches in the buildInterventionSuggestions
function so they do not reference artifacts that may be absent—instead point
users to artifacts that are preserved (e.g., NEEDS_INTERVENTION.json) and safe
remediation steps (e.g., run code-oz doctor run, inspect .code-oz state files,
or rerun code-oz approve plan / code-oz run after correcting the source) so the
messages are accurate even when PLAN.md or BUILD_REPORT.md were not produced.

---

Outside diff comments:
In `@scripts/build-binaries.ts`:
- Around line 463-466: The error message returned from the staging step is
stale: replace the phrase "Darwin tarball layout" with wording that reflects the
handoff tarball (e.g., "handoff tarball layout") where the return object is
constructed in scripts/build-binaries.ts so failures report the correct
artifact; keep the existing formatUnknownError(err) usage and overall return
shape intact.

In `@src/state/events.ts`:
- Around line 2821-2888: appendEvent currently calls nextEventLineNumber which
does a full readFile+split of events.jsonl on every append, making appends O(n);
change this by making line-number generation optional and/or by persisting a
cursor/sidecar so we avoid rescanning the whole file on each write: add an
option flag (e.g., AppendEventOptions.generateLineNumber) defaulting to false or
preserve existing behavior when explicitly requested, and implement a sidecar
counter (e.g., events.jsonl.count) updated atomically inside writeOnce/withLock
(or maintain a process-scoped in-memory counter initialized from the sidecar on
startup) so appendEvent and nextEventLineNumber no longer call readFile on every
append; ensure the LockBusyError and error-handling paths still work and update
references to nextEventLineNumber/appendEvent to use the sidecar/in-memory
cursor when the opt-in flag is not set.

---

Nitpick comments:
In `@code-oz-gui/tests/e2e/happy-path.e2e.ts`:
- Around line 32-45: The e2e test in happy-path.e2e.ts has grown too broad;
split the wide scenario into focused tests by extracting the blocks that
interact with reviewCard and buildCard into separate test cases: one test should
cover the "Write failing RED test for the Safari iOS bug" flow (use reviewCard,
open Decisions tab via dialog.getByRole('tab', { name: 'Decisions' }), and
assert the CROSS-FAMILY REVIEW and AI DEBATE decision texts), and another test
should cover the "Surface failure instead of silently swallowing in
finalizeCart" flow (use buildCard, open Decisions tab, and assert the BUDGET
ALERT text). Keep shared setup (page keyboard Escape, scrollIntoViewIfNeeded) in
beforeEach or a small helper to avoid duplication and name tests clearly (e.g.,
"displays cross-family review decisions" and "shows budget alerts"); ensure each
new test uses the same selectors (reviewCard, buildCard, dialog) so behavior
remains identical.

In `@code-oz-gui/tests/unit/approve-route.test.ts`:
- Around line 17-28: Add unit tests in approve-route.test.ts next to the
existing fixture-mode test to cover live-run branches: (1) a successful live
approve where POST(approveRequest('approve')) with params resolving to a real
runId returns 200 and expected success payload, (2) a live approve failure where
the service returns the domain error and response.status is 409 with body.error
=== 'approve-failed', and (3) an unknown runId where POST with params resolving
to a non-existent runId yields 404. Reuse the existing helpers POST and
approveRequest, and assert on response.status and body.error/body.detail as done
for FIXTURE_RUN_ID; mock or seed the run state so the tests exercise the live
approval code paths rather than fixture-mode refusal.

In `@tests/commands-run.test.ts`:
- Line 240: The test currently spawns using the literal 'bun' in the cmd array
(cmd: ['bun', '--eval', script]); update that to use process.execPath for
consistent runtime resolution by replacing the 'bun' string with
process.execPath so the spawn uses the current Node executable; ensure the same
change is applied wherever the cmd array is constructed in
tests/commands-run.test.ts (refer to the cmd variable and the script argument).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b413985b-7109-4c29-b4ac-755ce7721b86

📥 Commits

Reviewing files that changed from the base of the PR and between 0c9b99a and deabc46.

⛔ Files ignored due to path filters (5)
  • code-oz-gui/bun.lock is excluded by !**/*.lock
  • code-oz-gui/docs/screenshots/decisions-task.png is excluded by !**/*.png
  • code-oz-gui/docs/screenshots/events-errors.png is excluded by !**/*.png
  • code-oz-gui/docs/screenshots/hero.png is excluded by !**/*.png
  • code-oz-gui/docs/screenshots/workspace-form.png is excluded by !**/*.png
📒 Files selected for processing (86)
  • .github/workflows/release.yml
  • README.md
  • code-oz-gui/README.md
  • code-oz-gui/app/api/helper/ask/route.ts
  • code-oz-gui/app/api/run/[runId]/approve/route.ts
  • code-oz-gui/app/globals.css
  • code-oz-gui/app/page.tsx
  • code-oz-gui/components/Board.tsx
  • code-oz-gui/components/Card.tsx
  • code-oz-gui/components/Composer.tsx
  • code-oz-gui/components/DecisionsView.tsx
  • code-oz-gui/components/Drawer.tsx
  • code-oz-gui/components/EventsView.tsx
  • code-oz-gui/components/Footer.tsx
  • code-oz-gui/components/PhaseColumn.tsx
  • code-oz-gui/eslint.config.mjs
  • code-oz-gui/fixtures/sample-run/README.md
  • code-oz-gui/fixtures/sample-run/current.json
  • code-oz-gui/fixtures/sample-run/events.jsonl
  • code-oz-gui/lib/code-oz-spawn.ts
  • code-oz-gui/lib/repo-root.ts
  • code-oz-gui/lib/run-registry.ts
  • code-oz-gui/lib/run-store.ts
  • code-oz-gui/lib/types.ts
  • code-oz-gui/next.config.ts
  • code-oz-gui/package.json
  • code-oz-gui/playwright.config.ts
  • code-oz-gui/scripts/capture-screenshots.ts
  • code-oz-gui/tests/e2e/a11y.e2e.ts
  • code-oz-gui/tests/e2e/happy-path.e2e.ts
  • code-oz-gui/tests/unit/approve-route.test.ts
  • code-oz-gui/tests/unit/code-oz-spawn.test.ts
  • code-oz-gui/tests/unit/helper-ask.test.ts
  • code-oz-gui/tests/unit/run-store-approval.test.ts
  • docs/ABOUT.md
  • docs/PROVIDER_SETUP.md
  • docs/design/RELEASE_NOTES_v0.20.0-alpha.0.md
  • docs/design/RELEASE_NOTES_v0.20.1-alpha.0.md
  • docs/handoffs/codex-finalize/A1-cli-first-run.md
  • docs/handoffs/codex-finalize/A10-hygiene.md
  • docs/handoffs/codex-finalize/A2-gui-first-run.md
  • docs/handoffs/codex-finalize/A3-distribution.md
  • docs/handoffs/codex-finalize/A4-binaries.md
  • docs/handoffs/codex-finalize/A5-docs.md
  • docs/handoffs/codex-finalize/A6-errors.md
  • docs/handoffs/codex-finalize/A7-providers.md
  • docs/handoffs/codex-finalize/A8-visual.md
  • docs/handoffs/codex-finalize/A9-a11y.md
  • docs/handoffs/codex-finalize/CODEX_GOAL_R2.txt
  • docs/handoffs/codex-finalize/FIRST_RUN_AUDIT.md
  • docs/handoffs/codex-finalize/FIRST_RUN_FIXES.md
  • docs/handoffs/codex-finalize/PR_BODY.md
  • docs/homebrew/README.md
  • npm-wrapper/index.cjs
  • scripts/build-binaries.ts
  • scripts/install.sh
  • scripts/smoke-test.ts
  • src/cli.ts
  • src/commands/doctor.ts
  • src/commands/run.ts
  • src/phases/build.ts
  • src/phases/define.ts
  • src/phases/plan.ts
  • src/phases/review.ts
  • src/phases/schedule-attempt.ts
  • src/phases/verify.ts
  • src/providers/claude.ts
  • src/providers/codex.ts
  • src/providers/first-run-fake-fixture.ts
  • src/providers/invoke.ts
  • src/state/events.ts
  • src/state/gates.ts
  • src/state/schemas.ts
  • src/worktree/load-or-create-run-worktree.ts
  • tests/build-binaries.test.ts
  • tests/build-phase.test.ts
  • tests/ci-workflows.test.ts
  • tests/cli-run-args.test.ts
  • tests/commands-doctor.test.ts
  • tests/commands-run.test.ts
  • tests/homebrew-formula.test.ts
  • tests/install-script.test.ts
  • tests/npm-wrapper.test.ts
  • tests/providers-claude.test.ts
  • tests/providers-codex.test.ts
  • tests/state-gates.test.ts

Comment on lines +136 to +141
tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/code-oz"
tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/manifest.json"
test -x "${STAGE_NAME}/code-oz"
test -f "${STAGE_NAME}/manifest.json"
test -s "${STAGE_NAME}/README.md"
test -x "${STAGE_NAME}/install.sh"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Smoke step should validate README/install inside the archive too.

Line 140 and Line 141 validate staged files on disk, not tarball contents. Add tar listing checks for these files so the smoke step actually enforces archive completeness.

Suggested fix
           tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/code-oz"
           tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/manifest.json"
+          tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/README.md"
+          tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/install.sh"
           test -x "${STAGE_NAME}/code-oz"
           test -f "${STAGE_NAME}/manifest.json"
           test -s "${STAGE_NAME}/README.md"
           test -x "${STAGE_NAME}/install.sh"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 136 - 141, The smoke step
currently checks README.md and install.sh on disk but not inside the tarball;
update the validation to verify those files are present in the archive by adding
tar -tzf "${ASSET_NAME}" | grep -F "${STAGE_NAME}/README.md" and tar -tzf
"${ASSET_NAME}" | grep -F "${STAGE_NAME}/install.sh" (use the same ASSET_NAME
and STAGE_NAME variables as the other tar checks) and keep or reorder the
existing test -s "${STAGE_NAME}/README.md" and test -x
"${STAGE_NAME}/install.sh" as on-disk sanity checks.

Comment on lines +545 to +549
const output = error as { readonly stdout?: unknown; readonly stderr?: unknown; readonly message?: unknown };
const stderr = typeof output.stderr === 'string' ? output.stderr.trim() : '';
const stdout = typeof output.stdout === 'string' ? output.stdout.trim() : '';
const message = typeof output.message === 'string' ? output.message : 'code-oz approve failed';
throw new Error([stderr, stdout, message].filter((part) => part.length > 0).join('\n'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n code-oz-gui/lib/code-oz-spawn.ts | sed -n '540,555p'

Repository: omerakben/code-oz

Length of output: 773


🏁 Script executed:

# Check context around the error handling
cat -n code-oz-gui/lib/code-oz-spawn.ts | sed -n '520,560p'

Repository: omerakben/code-oz

Length of output: 1587


🏁 Script executed:

# Find execFile calls to understand how it's being used
rg -n "execFile" code-oz-gui/lib/code-oz-spawn.ts -A 5 -B 2

Repository: omerakben/code-oz

Length of output: 1781


🏁 Script executed:

# Verify Node.js execFile behavior by checking TypeScript types or documentation
rg -i "execFile" code-oz-gui/lib/code-oz-spawn.ts | head -20

Repository: omerakben/code-oz

Length of output: 432


🏁 Script executed:

# Check if there are other error handling patterns in the file that might inform this
rg -n "Buffer\.isBuffer|\.toString\(" code-oz-gui/lib/code-oz-spawn.ts | head -20

Repository: omerakben/code-oz

Length of output: 380


Handle Buffer stderr/stdout in approve error aggregation.

The execFileAsync call at line 538 does not specify an encoding option, so on error, execFile returns stdout and stderr as Buffer objects. Lines 546–547 check only for typeof === 'string', causing Buffer values to silently become empty strings and loss of error details.

Suggested fix
   } catch (error) {
     const output = error as { readonly stdout?: unknown; readonly stderr?: unknown; readonly message?: unknown };
-    const stderr = typeof output.stderr === 'string' ? output.stderr.trim() : '';
-    const stdout = typeof output.stdout === 'string' ? output.stdout.trim() : '';
+    const stderr = Buffer.isBuffer(output.stderr)
+      ? output.stderr.toString('utf8').trim()
+      : typeof output.stderr === 'string'
+        ? output.stderr.trim()
+        : '';
+    const stdout = Buffer.isBuffer(output.stdout)
+      ? output.stdout.toString('utf8').trim()
+      : typeof output.stdout === 'string'
+        ? output.stdout.trim()
+        : '';
     const message = typeof output.message === 'string' ? output.message : 'code-oz approve failed';
     throw new Error([stderr, stdout, message].filter((part) => part.length > 0).join('\n'));
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const output = error as { readonly stdout?: unknown; readonly stderr?: unknown; readonly message?: unknown };
const stderr = typeof output.stderr === 'string' ? output.stderr.trim() : '';
const stdout = typeof output.stdout === 'string' ? output.stdout.trim() : '';
const message = typeof output.message === 'string' ? output.message : 'code-oz approve failed';
throw new Error([stderr, stdout, message].filter((part) => part.length > 0).join('\n'));
const output = error as { readonly stdout?: unknown; readonly stderr?: unknown; readonly message?: unknown };
const stderr = Buffer.isBuffer(output.stderr)
? output.stderr.toString('utf8').trim()
: typeof output.stderr === 'string'
? output.stderr.trim()
: '';
const stdout = Buffer.isBuffer(output.stdout)
? output.stdout.toString('utf8').trim()
: typeof output.stdout === 'string'
? output.stdout.trim()
: '';
const message = typeof output.message === 'string' ? output.message : 'code-oz approve failed';
throw new Error([stderr, stdout, message].filter((part) => part.length > 0).join('\n'));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code-oz-gui/lib/code-oz-spawn.ts` around lines 545 - 549, The
error-aggregation loses data because stdout/stderr may be Buffer objects when
execFileAsync (called earlier) omits an encoding; update the error handling in
code-oz-spawn.ts (the block that derives output, stderr, stdout, message) to
detect Buffer-like values (e.g., Buffer.isBuffer(output.stderr) /
Buffer.isBuffer(output.stdout) or typeof .toString === 'function') and convert
them to strings (e.g., .toString('utf8').trim()) before joining; keep the
existing fallback for non-string/non-Buffer cases and preserve the existing
message fallback ("code-oz approve failed") for output.message.

Comment thread code-oz-gui/package.json
"typecheck": "tsc --noEmit",
"test": "bun test tests/unit",
"lint": "eslint .",
"clean": "next clean",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In the official Next.js CLI docs for version 15.x, list supported commands and confirm whether next clean exists.

💡 Result:

Supported Next.js CLI commands in the official Next.js v15.x next CLI docs are: dev, build, start, info, lint, telemetry, typegen, upgrade, and experimental-analyze [1][2]. next clean: The official Next.js v15.x CLI command list does not include a clean command, so next clean is not a supported command in the official CLI docs [1][2].

Citations:


next clean is not a supported Next.js CLI command

Line 12 uses next clean, which does not exist in the Next.js v15.x CLI (supported commands are: dev, build, start, info, lint, telemetry, typegen, upgrade, and experimental-analyze). The bun run clean command will fail.

Proposed fix
-    "clean": "next clean",
+    "clean": "node -e \"require('node:fs').rmSync('.next', { recursive: true, force: true })\"",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code-oz-gui/package.json` at line 12, The "clean" npm script currently uses
the unsupported command "next clean"; update the "clean" script (the
package.json "clean" entry) to use a valid cleanup command such as removing
Next's build artifacts (for example replace "next clean" with a platform-safe
remover like "rimraf .next" and add rimraf as a devDependency) or simply remove
the script—ensure the change targets the "clean" script and does not rely on the
non-existent "next clean" CLI command.

Comment on lines +40 to +44
- `bun test` with total `>=3366`
- `bun run typecheck`
- `bun run build:binary`
- `bun run scripts/smoke-test.ts`
- `cd code-oz-gui && bun test && bun run typecheck && bun run test:e2e && bun run test:a11y`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Sync validation targets with current verification commands/results.

Line 40 still targets >=3366, but this PR reports 3390 passing tests. Line 43 also uses bun run scripts/smoke-test.ts while the reported release check uses bun run smoke. Keeping these stale makes the checklist ambiguous.

Suggested doc patch
-- `bun test` with total `>=3366`
+- `bun test` with total `>=3390`
 - `bun run typecheck`
 - `bun run build:binary`
-- `bun run scripts/smoke-test.ts`
+- `bun run smoke`
 - `cd code-oz-gui && bun test && bun run typecheck && bun run test:e2e && bun run test:a11y`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `bun test` with total `>=3366`
- `bun run typecheck`
- `bun run build:binary`
- `bun run scripts/smoke-test.ts`
- `cd code-oz-gui && bun test && bun run typecheck && bun run test:e2e && bun run test:a11y`
- `bun test` with total `>=3390`
- `bun run typecheck`
- `bun run build:binary`
- `bun run smoke`
- `cd code-oz-gui && bun test && bun run typecheck && bun run test:e2e && bun run test:a11y`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/RELEASE_NOTES_v0.20.1-alpha.0.md` around lines 40 - 44, Update
the release notes checklist to match the actual verification results: change the
`bun test` total threshold from `>=3366` to `>=3390` and replace the `bun run
scripts/smoke-test.ts` entry with `bun run smoke` so the lines referencing `bun
test` and `bun run scripts/smoke-test.ts` reflect the reported `3390` passing
tests and the actual smoke command used in the release check.


## Summary

Four findings. Severity mix: 0 block-ship, 3 fix-soon, 1 nit. Root CLI typecheck is clean, GUI manual `tsc` is clean, no committed real `.env` file was found, and no high-confidence secret pattern was found in JSON/JSONL fixtures. The main hygiene risks are GUI lint not being usable as a clean gate, GUI typecheck not being exposed as a script or covered by root typecheck, unresolved source TODOs without issue links, and stale GUI dependency/export surface.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix findings count/severity mix mismatch in the Summary.

Line 10 says “Four findings” with a 4-item severity mix, but this document contains five findings (F10.1–F10.5). Please align the summary counts with the actual sections.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/handoffs/codex-finalize/A10-hygiene.md` at line 10, The Summary line
currently reads “Four findings” and shows a 4-item severity mix that does not
match the five findings documented (F10.1–F10.5); update the header to state
“Five findings” and correct the severity breakdown to reflect the actual counts
across F10.1–F10.5 (adjust the numbers so they sum to five and match the labels
used elsewhere in the doc).

Comment on lines +191 to +192
| `git grep -nE 'TODO|FIXME|XXX'` | repo root | 0 |
| `git grep -nE 'TODO|FIXME|XXX' -- src tests code-oz-gui ':!code-oz-gui/bun.lock'` | repo root | 0 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Escape pipe characters in table command cells to keep Markdown table valid.

Lines 191-192 include inline code with unescaped |, which breaks table column parsing (MD056). Escape pipes (\|) or move the command list to a fenced code block.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 191-191: Table column count
Expected: 3; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 192-192: Table column count
Expected: 3; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/handoffs/codex-finalize/A10-hygiene.md` around lines 191 - 192, The
Markdown table cells at the two lines include inline code containing unescaped
pipe characters which breaks table parsing (MD056); update the table by escaping
each pipe inside the inline code (e.g., replace | with \| in the command strings
like the `git grep -nE 'TODO\|FIXME\|XXX'` entry and the longer command
containing `:!code-oz-gui/bun.lock`), or alternatively move those commands out
of the table into a fenced code block so the `git grep -nE 'TODO|FIXME|XXX'` and
the `git grep -nE 'TODO|FIXME|XXX' -- src tests code-oz-gui
':!code-oz-gui/bun.lock'` lines render without breaking the table.

Comment on lines +24 to +26
B3 code-oz-gui/app/api/run/[runId]/approve/route.ts and lib/run-store.ts: fixture-mode approve/revise still writes to fixtures/sample-run/requests/. Refuse with HTTP 409 when getRunRecord(runId)?.kind === 'fixture'; include a one-line recovery hint. RED first.

B4 code-oz-gui/components/Drawer.tsx:65-82 focus-trap misses textareas. Use selector `button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])`. Record document.activeElement as openerRef on open; restore on close. Playwright in tests/e2e/a11y.e2e.ts: Tab cycles through textareas, Escape restores opener.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inconsistent numbering prefix for block-push items.

The BLOCK-PUSH section uses "BP-1" through "BP-6" (lines 14-22), then switches to "B3" and "B4" (lines 24-26) without the "P" suffix. This creates ambiguity about whether these are part of the same sequence or a different categorization.

Consider renumbering to "BP-7" and "BP-8" for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/handoffs/codex-finalize/CODEX_GOAL_R2.txt` around lines 24 - 26, The
BLOCK-PUSH items are inconsistently labeled: after "BP-1"…"BP-6" the next items
are labeled "B3" and "B4"; change those two labels to "BP-7" and "BP-8"
respectively so the sequence remains contiguous and unambiguous (update the
occurrences of "B3" → "BP-7" and "B4" → "BP-8" in the BLOCK-PUSH section,
preserving the existing lines about the approve/revise fixture behavior and
Drawer focus-trap details).


## Summary

The Phase 1 audit filed 45 findings: 12 block-ship, 29 fix-soon, and 4 nit. The first-run blockers cluster around five surfaces:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Incorrect finding count in summary.

The summary states "45 findings: 12 block-ship, 29 fix-soon, and 4 nit," but the findings table contains only 43 items:

  • Block-ship: 12 (lines 22-34) ✓
  • Fix-soon: 27 (lines 35-61), not 29
  • Nit: 4 (lines 62-65) ✓

The fix-soon count and total should be corrected to match the actual table contents.

📊 Proposed fix
-The Phase 1 audit filed 45 findings: 12 block-ship, 29 fix-soon, and 4 nit. The first-run blockers cluster around five surfaces:
+The Phase 1 audit filed 43 findings: 12 block-ship, 27 fix-soon, and 4 nit. The first-run blockers cluster around five surfaces:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The Phase 1 audit filed 45 findings: 12 block-ship, 29 fix-soon, and 4 nit. The first-run blockers cluster around five surfaces:
The Phase 1 audit filed 43 findings: 12 block-ship, 27 fix-soon, and 4 nit. The first-run blockers cluster around five surfaces:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/handoffs/codex-finalize/FIRST_RUN_AUDIT.md` at line 8, Update the Phase
1 audit summary sentence to reflect the actual counts in the findings table:
change "45 findings: 12 block-ship, 29 fix-soon, and 4 nit" to "43 findings: 12
block-ship, 27 fix-soon, and 4 nit" so the summary matches the findings table;
ensure the sentence in the document header that references the Phase 1 audit
totals is the one you edit.

Comment thread npm-wrapper/index.cjs
Comment on lines 94 to 102
if (status >= 300 && status < 400 && response.headers.location) {
response.resume()
download(response.headers.location, destination).then(resolve, reject)
const redirected = new URL(response.headers.location, parsed)
if (redirected.protocol !== 'https:') {
reject(new Error(`redirect must stay on https: ${redirected.href}`))
return
}
download(redirected.href, destination).then(resolve, reject)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cap redirect depth to avoid unbounded recursive downloads.

Line 101 recursively calls download() with no redirect limit. A redirect loop can hang first-run install and exhaust resources.

Suggested fix
-async function download(url, destination) {
+async function download(url, destination, redirectCount = 0) {
+  if (redirectCount > 5) {
+    throw new Error(`too many redirects while downloading ${url}`)
+  }
   const parsed = parseDownloadUrl(url)
@@
-        download(redirected.href, destination).then(resolve, reject)
+        download(redirected.href, destination, redirectCount + 1).then(resolve, reject)
         return
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@npm-wrapper/index.cjs` around lines 94 - 102, The redirect handling in
download() calls itself recursively without a limit, risking infinite loops;
modify download (and its callers) to accept a redirect counter (e.g.,
redirectCount with default 0) and a MAX_REDIRECTS constant, increment
redirectCount on each redirect, and before recursing check if redirectCount >=
MAX_REDIRECTS and reject with a clear error (e.g., "too many redirects"); keep
the existing protocol check for redirected.protocol and pass updated
redirectCount when calling download(redirected.href, destination).

Comment thread src/phases/build.ts
Comment on lines +1003 to +1027
function buildInterventionSuggestions(code: string): readonly string[] {
switch (code) {
case 'build_plan_missing':
case 'build_task_id_unknown':
return Object.freeze([
'open .code-oz/artifacts/PLAN.md and confirm the task exists',
'rerun code-oz approve plan after correcting PLAN.md',
])
case 'restart_state_drift':
return Object.freeze([
'run code-oz doctor run to inspect the active task cursor',
'rerun code-oz run without editing .code-oz state files by hand',
])
case 'build_patch_apply_failed':
return Object.freeze([
'open .code-oz/artifacts/BUILD_REPORT.md and inspect the failed patch',
'rerun code-oz run so BUILD can produce a corrected patch',
])
default:
return Object.freeze([
'run code-oz doctor run to inspect the active run state',
'rerun code-oz run after fixing the cause shown in NEEDS_INTERVENTION.json',
])
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the recovery hints for failure paths that do not produce those artifacts.

build_plan_missing tells the operator to open PLAN.md, and build_patch_apply_failed tells them to inspect BUILD_REPORT.md, but neither file is guaranteed to exist when those codes are raised. That makes the intervention guidance misleading at the exact point the user needs a valid recovery path. Please point these cases at artifacts that are actually preserved on those paths, or use a remediation step that does not assume the file already exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/phases/build.ts` around lines 1003 - 1027, The guidance in
buildInterventionSuggestions for the codes 'build_plan_missing' and
'build_patch_apply_failed' assumes PLAN.md and BUILD_REPORT.md exist; change
those branches in the buildInterventionSuggestions function so they do not
reference artifacts that may be absent—instead point users to artifacts that are
preserved (e.g., NEEDS_INTERVENTION.json) and safe remediation steps (e.g., run
code-oz doctor run, inspect .code-oz state files, or rerun code-oz approve plan
/ code-oz run after correcting the source) so the messages are accurate even
when PLAN.md or BUILD_REPORT.md were not produced.

omerakben added 12 commits May 14, 2026 10:04
Pulls Phase 3 trust/community/proof tasks from behind M17 (Option D) into
v0.20.1 finalize polish, ahead of M17 AUDIT runtime in v0.21.

Trigger: GPT-5.5 Pro third-party audit returned 1000-star readiness 3.5/10
despite engineering 8.0/10 — public surface signals 'alpha toy' while the
engineering is real. Pull-forward fixes truth, trust, proof gaps in days,
not weeks behind M17.

Codex R0 verdict: accept-with-modifications (thread 019e26b5).
- 5 block-approve closures folded: CLAUDE.md truth-sync, roadmap
  single-authority, rule 9 exemption for user-invoked demo scripts,
  canonical demo:failure-gates command, no-new-gate-authority cut clause.
- 5 medium closures: precise Community Standards target, PROVIDERS
  three-section restructure, fresh-clone smoke, Ozzy-only release ops,
  9-surface drift pass.
- 5 missed risks integrated into risk register.

Implementation plan: 20 commits across 5 tracks + 2 review checkpoints,
~14h Maestro execution, single finalize branch, ~$0 incremental Codex
spend (ChatGPT subscription auth).

Scope guard: no new gate authority introduced in v0.20.1 (rule 20).
M17 and Phase 5 launch sequence stay locked unchanged in Option D.
GPT Pro audit + Codex R0 closures applied.

- Replace dense architecture-first hero ('Repo-native agentic SDLC
  runtime...') with 'CI-style gates for AI coding agents'.
- Frame for risky repos, not fastest-loop coding (Codex R0
  missed-risk #4).
- Frame FakeProvider honestly: proves lifecycle gates and ledger
  determinism, NOT model quality (Codex R0 missed-risk #1).
- Add 'Why not just Claude Code or Codex?', 'What is real today?',
  'What is simulated?', 'How is this different?', 'Who is this for?',
  'Failure demo', 'Star this repo if...' sections.
- Add Trust/security, Contributing, Roadmap links (forward references
  to files landing in C6-C13).
- Architecture and historical context section consolidates pointers
  to docs/ABOUT.md for the demoted dense detail and metaphor.
- Update test badge 3366 -> 3390 (Codex R0 N1).
- Update curl install version reference v0.20.0 -> v0.20.1.
- Drop --effort beast from public demo block (Codex R0 N2).
- Scope GEMINI_API_KEY to the separate GUI helper (Codex R0 N3).

Above-fold verification: 0 'Repo-native agentic SDLC runtime'; 0
'simulation'; 0 'AI software company' (one historical reference at
the Architecture/history section near end, intentionally framed).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C1. Opens C19 public-claims
bundle review.
…re-gates

- description: 'Multi-agent software-company simulation CLI...' ->
  'CI-style gates for AI coding agents -- local-first governed
  delivery loop'. Kills 'simulation' word per GPT Pro audit issue #19.
- keywords: replace 9 entries (incl. 'gemini', 'multi-agent',
  'orchestrator') with 10 entries focused on AI/coding-agent/devtools
  positioning. No 'gemini' until Gemini is live (Codex R0 N4).
- scripts: add 'demo:failure-gates' canonical command for the failure
  demo landing in C14-C15 (Codex R0 B4 closure).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C2.
Codex R0 M2 closure: separate live adapters from stubs from future
adapter candidates so OpenCode/Roo do not become phantom contract
entries.

- Live adapters: claude (Claude CLI), codex (Codex CLI), xai (HTTPS +
  XAI_API_KEY), fake (built-in deterministic). All four cover every
  phase per src/providers/capabilities.ts.
- Stubs (transparency only): gemini -- invoke() throws
  provider_gemini_not_yet_supported (verified at
  src/providers/gemini.ts:26); loader rejects via
  loader_provider_phase_not_eligible since M11.
- Future adapter candidates, NOT v0.1: gemini-live, opencode, roo.
  Explicitly forbidden to treat as live contract entries.

Removes the Gemini row from the 'Subscription-first' table (it does
not belong there); existing detailed sections preserved unchanged.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C3.
Codex R0 prompt #5 refinement closure:
- 'Run an AI software company from your terminal' no longer appears
  as an active tagline; it appears only as a historical reference
  inside the new 'Historical context' subsection.
- Public positioning aligned with v0.20.1 README: 'CI-style gates for
  AI coding agents'.
- New 'Architecture (the dense version)' subsection absorbs the
  hybrid phase-graph + agentic sub-orchestration framing demoted out
  of the README.
- Provider list synced with PROVIDERS.md v0.20.1 status section
  (Claude/Codex/xAI/Fake live; Gemini stub; OpenCode/Roo future).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C4.
Codex R0 B1 closure (block-approve): CLAUDE.md and README must tell
the same provider-support story by v0.20.1 tag time.

- Remove 'Multi-provider via IAgentProvider (Claude / Codex / Gemini
  SDKs reading CLI OAuth tokens)' overclaim. Replace with explicit
  v0.20.1 provider surface: Claude/Codex/xAI/Fake live; Gemini stub
  for transparency; OpenCode/Roo as future candidates. Points to
  docs/contracts/PROVIDERS.md § 'Provider status (v0.1)' as canonical.
- Bump status from v0.19/v0.20 distribution-sweep framing to v0.20.0
  shipped + v0.20.1 first-run-polish in preparation per
  V0_20_1_FIRST_RUN_POLISH_DESIGN.md. List v0.20.1 scope explicitly.
- Test count 3362 -> 3390.
- 'multi-agent software-company simulation' -> 'multi-agent
  software-delivery runtime' (kill 'simulation' word).
- Preserve all 23 non-negotiable rules + W3a distribution surface
  context unchanged.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C5.
Codex R0 missed-risk #5 closure: trust caveat made explicit, links to
the v0.x-stable signing/provenance milestone in docs/design/ROADMAP.md.

Sections:
- Reporting (security@tuel.ai + GitHub Security Advisories)
- Supported versions (latest v0.20.x-alpha line only)
- Artifact trust posture: what is verified (SHA-256 chain across all
  three install channels, no postinstall hook); what is NOT verified
  yet (Apple signing/notarization, GPG/Sigstore checksums, SLSA
  provenance) all deferred to v0.x stable.
- Provider auth boundaries (Claude/Codex CLI subprocess, xAI direct
  HTTPS with redaction discipline, Fake no-auth, Gemini stub no-auth).
- What is logged (events.jsonl) and what is not (credentials, raw HTTP
  response bodies, silent recursive repo context).
- Threat model (alpha-honest: what code-oz defends against; what it
  does NOT defend against).
- Public alpha disclaimer.

Satisfies GitHub Community Standards Security Policy requirement.
Plan: docs/planning/V0_20_1_POLISH_PLAN.md C6.
…s, cross-model review

Sections:
- Local setup (Bun 1.3+, Node 18+, supported platforms).
- Test discipline: offline by default; opt-in live-provider via
  CODE_OZ_LIVE_PROVIDER_TESTS env-var; RED-first per rule 22.
- Commit conventions: Conventional Commits; no emoji; no
  Co-Authored-By: Claude footer; no secrets.
- Branch naming + main-is-tag-only.
- PR expectations + cross-model peer review discipline.
- Provider test policy (stub-first; live gated; redaction test
  required).
- Links to SECURITY.md, CODE_OF_CONDUCT.md, discussions, issues.

Satisfies GitHub Community Standards Contributing requirement.
Plan: docs/planning/V0_20_1_POLISH_PLAN.md C7.
Codex R0 M1 closure: add CODE_OF_CONDUCT.md to satisfy GitHub
Community Standards CoC requirement.

Adopted by reference rather than inlined to avoid drift if Contributor
Covenant publishes a newer revision; the upstream URL is the
single source of truth. Reporting contact: conduct@tuel.ai.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C8.
Issue templates (form schema with structured fields):
- bug_report.yml: version, install channel, platform, repro,
  expected, actual, additional context
- install_problem.yml: channel, platform, version, command, error
  output, tool versions, pre-filing checks
- demo_failure.yml: which demo (todo-cli or failure-gates), version,
  platform, command, expected, actual, gate files produced
- feature_request.yml: problem, workaround, proposal, alternatives,
  scope, rule check (acknowledges 23 non-negotiables)

Config:
- config.yml: blank issues disabled; Discussions + Security Advisories
  contact links

PR template (markdown):
- Summary, files, testing checklist (bun test pass/fail/skip + RED-first
  + redaction test for HTTP adapters), cross-model peer review
  checkboxes (does/does-not-need with briefing+response link slots),
  breaking-changes flag, related links

Satisfies GitHub Community Standards Issue Templates + PR Template.
Plan: docs/planning/V0_20_1_POLISH_PLAN.md C9.
…l + auth boundaries

Codex R0 missed-risk #5 closure (deeper companion to SECURITY.md).

Sections:
- Data boundaries (what leaves the repo on a provider call; what stays
  on disk; what the binary does at runtime; what the install channels
  do or do not phone home).
- Artifact trust (SHA-256-bound approvals; worktree-per-run isolation;
  events.jsonl ledger; cross-family REVIEW policy).
- Install trust (verified today: SHA-256 chain, same SHA across all
  three channels, no postinstall hook, redirect cap; NOT verified yet:
  Apple signing, GPG/Sigstore checksums, SLSA provenance — all v0.x
  stable per docs/design/ROADMAP.md).
- Provider auth boundaries (Claude/Codex CLI subprocess delegation;
  xAI direct HTTPS with redaction discipline; Fake no-auth; Gemini
  stub no-auth).
- What is logged (events.jsonl, gates/, artifacts/, worktree/) vs
  what is NOT logged (credentials, raw HTTP bodies, recursive repo
  context, user telemetry — there is no analytics pipeline).
- How to inspect what happened (jq + git -C the per-run worktree).
- What we do NOT promise in alpha (compromised upstream provider;
  compromised release publisher; malicious code in user's own repo;
  no production-hardened release line yet).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C10. Closes Track 2.
Reuses the locked Codex-verified, footnote-sourced, HN-hardened
comparison table from docs/planning/1000_STAR_PLAN.md §3.2.

Row-for-row identical. Adds:
- Intro paragraph framing comparison as mechanics-not-marketing.
- 'Best used with' section mapping reader's current tool to what
  code-oz adds.
- 'What code-oz is NOT' closer (5 explicit non-claims, including
  honest framing of FakeProvider determinism vs model-quality bench).
- Methodology note inviting issues for documentation gaps.
- Pointer to docs/contracts/PROVIDERS.md for the underlying provider
  matrix.

This is the canonical public comparison; README short table summarizes
and links here (Codex R0 N5: one canonical comparison path).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C11.
omerakben added 14 commits May 14, 2026 10:25
Codex R0 prompt #4 wording-guard closure: explicitly framed as the
benchmark PROTOCOL, not benchmark proof. Measured rows land in v0.21
with the runner.

Sections:
- Status banner: 'this is the protocol, not measured proof'.
- Thesis: code-oz catches governance failures direct-agent flows
  miss; NOT 'code-oz writes better code'.
- 6 fixtures (todo-cli-real-tests + tampered-plan + scope-escape +
  same-family-review + verify-fail-restart + risky-shell-change)
  with each task's direct-agent risk and what code-oz should add.
- 5 workflows under test (Claude alone / Codex alone / direct +
  manual / code-oz Fake / code-oz live).
- 9 metrics (success, governance block rate, false block rate, human
  interventions, audit completeness, time, cost, reproducibility,
  evidence quality).
- Result table format: TBD across the board (Block/Allow/Pass/Fail
  cell values when measured); n/a where workflow can't meaningfully
  run.
- Reproduction commands (forward reference; runner ships in v0.21).
- Explicit 'what this benchmark does NOT prove' (4 anti-claims).

NO 'bench:*' command added to package.json (Codex R0 prompt #4).
NO badge with results.
README links this as 'benchmark protocol', not 'benchmark proof'.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C12.
Codex R0 B2 closure (block-approve): single roadmap authority.
Edits the canonical docs/design/ROADMAP.md instead of creating a
docs/ROADMAP.md shadow file.

- New '## Now, next, later' section at the very top (anchor:
  #now-next-later, matching README link).
- Now: v0.20.1-alpha.0 first-run polish (8 bullet points covering
  the v0.20.1 scope).
- Next: v0.21.0-alpha.0 = M17 AUDIT runtime (5 bullet points).
- Later: 10 unscheduled items (signing, Windows, Gemini live,
  OpenCode/Roo, SWE-bench v0.22, cloud-IAM, broader consult).
- New '# Detailed roadmap (project-internal)' wrapping heading
  preserves all existing content unchanged below the public summary.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C13. Closes Track 3 doc
commits; Track 3 code track (failure demo C14-C15) is next.
C14 scaffolding for the failure-mode demo proving the gates block
what they claim to block. All fixtures pass Codex R0 B5 audit (no new
gate authority needed; each exercises an existing production
enforcement path).

Walkthrough (docs/demo/02-failure-gates/README.md):
- 'Run it' section with bun run demo:failure-gates command
- 5-fixture table mapping each to production code that blocks it
- 'What this proves' + 'What this does NOT prove' framing
- FakeProvider determinism vs model-quality split made explicit

5 fixture SPECs:
- 01-tampered-artifact: SHA-256-bound approval refuses bytes drift
  (src/state/gates.ts:104-118 gate_artifact_sha256_mismatch)
- 02-scope-escape: REVIEW findings cannot point outside the run
  worktree (src/phases/review.ts:2189-2204 canonical-path resolution)
- 03-verify-fail: VERIFY evidence command failure writes
  NEEDS_INTERVENTION.json instead of advancing
  (src/phases/verify.ts:180-205 writeNeedsInterventionGate)
- 04-same-family-review: cross-family REVIEW policy refuses
  same-family invocation BEFORE reviewer is called
  (src/tools/review-request.ts:60-78 review_provider_same_family)
- 05-reviewer-blocks-risk: needs-revision verdict routes back to
  revision instead of writing GATE_REVIEW_PASSED.json
  (src/phases/review.ts:224 ReviewStatus + :237-244 routing)

Each SPEC.md includes:
- What the fixture proves
- Setup steps
- Expected gate behavior
- Expected events.jsonl event sequence (typed event names)
- Expected exit state (gate file or thrown error)
- Production code reference (file:line)
- Why it matters (direct-agent comparison)
- Output capture location

C15 next: scripts/demo/02-failure-gates/run-demo.ts + RED-first tests
asserting the events.jsonl gate-block event sequence per fixture.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C14.
C15 closes the failure-gates code track. Five fixtures exercise five
production gate APIs and prove the gates refuse the wrong thing.

Code track:
- scripts/demo/02-failure-gates/run-demo.ts (~520 LOC) -- orchestrator
  that runs each fixture against production code paths and writes
  captured outputs to docs/demo/02-failure-gates/output/<fixture>/
  for inspection.
- tests/demo/failure-gates.test.ts (~190 LOC) -- RED-first per rule
  22; 5 tests asserting each fixture's gate-block behavior. Runs
  GREEN against current src/ because the gates already enforce.
- demo:failure-gates package script (added in C2).

Production code paths exercised:
- 01-tampered-artifact: src/state/gates.ts:104-118
  writeGate({computeSha256:false, gate.artifactSha256: <fake>}) ->
  GateLoadError code='gate_artifact_sha256_mismatch'
- 02-scope-escape: src/phases/review.ts:2189-2204 underlying primitive
  realpath()+worktree-prefix check rejects out-of-worktree path
- 03-verify-fail: src/state/gates.ts:290 writeNeedsInterventionGate
  writes NEEDS_INTERVENTION.json with structured verify_failed payload
- 04-same-family-review: src/tools/review-request.ts:60-78
  requestReview throws ProviderError code='provider_permissions_violation'
  BEFORE invoking the reviewer
- 05-reviewer-blocks-risk: src/phases/review.ts:224 ReviewStatus enum
  needs_revision is distinct from resolved -> SHIP gate not written

Codex R0 B5 closure proof:
  git diff --stat src/  ->  empty (no production source changed).
No new gate authority introduced. Each fixture targets an already-
enforced production code path; tests demonstrate the existing
behavior; the demo wraps the same APIs with output capture.

Codex R0 prompt #3 closure: each fixture's events.jsonl carries the
expected gate-block event sequence. Captured outputs committed under
docs/demo/02-failure-gates/output/ so users without bun installed can
still inspect the produced events + gate files.

Tests: 3390 -> 3395 pass, 0 fail. Demo: 5/5 fixtures pass.

SPEC fix: fixture-04 SPEC initially listed code='review_provider_same_family';
production code at src/providers/errors.ts:13-15 uses the more general
'provider_permissions_violation'. SPEC + walkthrough README corrected.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C15. Closes Track 3.
Opens Codex R1 review on this commit per the cross-model rule.
Codex R1 verdict at docs/design/CODEX_RESPONSE_V0_20_1_POLISH_R1.md
(thread 019e26f6) returned fix-first with 3 block-push findings,
4 medium, 2 nits. Underlying behavior is correct; framing oversold it.

Block-push closures:

B1 — events.jsonl files were author-constructed sketches with
fictitious event names (review_finding_out_of_worktree, etc.) that
do not exist in src/state/events.ts. Renamed every output file from
events.jsonl to events-sketch.jsonl. Added explicit framing in the
walkthrough README that distinguishes fixture-author sketches from
real production events.jsonl (written via appendEvent in a full run).
Pointed readers at docs/demo/01-todo-cli/output/balanced/state/events.jsonl
for a real production ledger.

B2 — fixture 03 SPEC + run-demo previously claimed a normal verify
failure produces NEEDS_INTERVENTION.json immediately. Production
behavior at src/phases/verify.ts:599+ is verify_failed + restart for
attempts 1-3, then NEEDS_INTERVENTION on cap exhaustion or
non-restart-eligible failures. Reframed fixture 03 as "intervention
path on cap exhaustion or durable failure", which is the exact case
the writeNeedsInterventionGate API serves. NEEDS_INTERVENTION.json
in output IS a real production gate file written via the production
API; only the framing of when it fires is corrected.

B3 — fixture 01 SPEC promised NEEDS_INTERVENTION.json but the demo
never produced one. Removed the claim. Reframed fixture 01 as a
gate-write refusal demonstration: writeGate raises GateLoadError
before any gate file is written. The orchestrator's intervention
plumbing (which DOES write NEEDS_INTERVENTION.json on this error
class in a full run) is not exercised by the fixture; that is fixture
03's territory.

Medium closures:

M1 — test file in-line comment renamed from "RED-first test (rule
22)" to "characterization tests for production gate primitives" with
explicit note explaining the C15 commit message used the wrong term.

M2 — README "deterministic via FakeProvider" reworded to
"deterministic via small test providers" since run-demo.ts uses a
local TestProvider, not FakeProvider.

M3 — fixture 05 SPEC reframed as "status-shape illustration" not
"routing proof". Production routing through finalizeReviewRound +
decideReviewRemediation is exercised in tests/review-phase.test.ts:620+.

M4 — corrected stale source references. Fixture 05 now correctly
points to src/phases/review.ts:631 (runReview entry) and :107
(decideReviewRemediation) instead of the type-definition line :237.

Verification after fix-first:
- Full test suite: 3395 pass / 0 fail / 2 skip
- B5 still clean: git diff --stat src/ empty
- bun run demo:failure-gates: 5/5 fixtures pass
- All output dirs now have events-sketch.jsonl (not events.jsonl)
- NEEDS_INTERVENTION.json (fixture 03 only) remains a real production
  gate file written via writeNeedsInterventionGate

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C15. R1 fix-first closure.
C16. Per Keep a Changelog format. v0.20.1-alpha.0 entry covers all 5
tracks (truth correction, trust hygiene, proof assets, release prep,
cross-model peer review). v0.20.0-alpha.0 entry is a backfill (the
original release notes were thin per GPT Pro audit issue #5).

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C16.
C17. Maestro drafts; Ozzy posts via gh CLI per Codex R0 M4 (release
ops are Ozzy-approved external actions, not Maestro automation).

- 2026-05-14-v0.20.1-release-notes.md: why-this-release-matters,
  install commands, try-it (incl. demo:failure-gates), what changed
  (5 tracks), provider matrix, limitations, trust verification,
  cross-model review summary, next.
- 2026-05-14-v0.20.0-release-notes-backfill.md: replaces the thin
  v0.20.0 notes per GPT Pro audit issue #5. Preserves provenance;
  no binary or behavior change.

Posting commands at the top of each file.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C17.
C18. Codex R0 M3 closure. Pre-tag smoke that clones the current branch
to a tmp dir, installs root + code-oz-gui sub-project deps, runs full
test suite + both demos, then runs 6 drift checks against the public
claims:

1. 'simulation' word absent from package.json + README
2. Gemini overclaim phrasings absent; if Gemini mentioned, stub
   qualifier must be present
3. package.json keywords clean of gemini (Codex R0 N4)
4. 'AI software company' absent from README hero region (top 25 lines)
5. package.json description aligned with README 'CI-style gates' hero
6. All v0.20.1-linked files exist (SECURITY/CONTRIBUTING/COC/TRUST/
   ROADMAP/PROVIDERS/comparisons/benchmarks/failure-demo)

Caught two real issues on first run:
- code-oz-gui sub-project deps not installed by root `bun install` —
  fix: install sub-project deps too if package.json present
- drift check B was too strict: 'Gemini.*adapter' regex matched the
  legitimate 'Gemini stub provider in v0.1; not a working invocation
  adapter' phrasing — fix: look for specific overclaim phrases instead
  of fuzzy patterns

Smoke runs in ~30 seconds. Exits 0 only when every check passes.
Rule 9 exempt: user-invoked release-prep script, not orchestrator-
spawned. Same precedent as demo scripts.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C18. Closes Track 4.
Codex R2 verdict at docs/design/CODEX_RESPONSE_V0_20_1_POLISH_R2.md
(thread 019e2706) returned fix-first with 4 block-push + 4 medium.
All closed in this commit; no work deferred.

Block-push closures:

B1 — version drift across release-critical surfaces. Bumped:
  - package.json: 0.20.0-alpha.0 -> 0.20.1-alpha.0
  - src/cli.ts:7 PKG_VERSION (the binary --version source)
  - src/config/schema.ts:308 DEFAULT_CONFIG.version
  - tests/m5-fix-first.test.ts CURRENT (the version-consistency
    regression-test pin)
  - tests/cli-init.test.ts:56 expected config.yaml version
  - tests/smoke-test.test.ts VERSION (smoke fixture)

B2 — release notes + CHANGELOG falsely claimed no production source
changes. Branch had 3 first-run polish src/ commits ahead of
origin/main (85afcdf, 293f33e, 634a288) totaling 643 insertions /
174 deletions across 18 files. Reframed both as 'first-run polish +
public truth sync'. CHANGELOG now lists the 3 src/ fixes explicitly.
Release notes likewise.

B3 — TRUST.md overclaimed .gitignore protection on the provider-send
path. Production at src/providers/manifest.ts only enforces explicit
ProviderRequest.files + path safety + permissions.read. Reworded to
the actual contract. Universal .code-ozignore noted as roadmap.

B4 — release publishing path was contradictory. release.yml creates
the GitHub release on tag push with thin auto-generated notes. The
release-notes draft now correctly tells Ozzy to:
  1. tag and push (triggers release.yml)
  2. wait for the workflow (gh run watch)
  3. gh release edit ... --notes-file ... (replace thin notes)
Not gh release create.

Medium closures:

M1 — test count drift. README + CLAUDE.md + SECURITY.md said 3390;
release notes said 3395. Current bun test reports 3395 pass / 0 fail
/ 2 skip. Updated all to 3395.

M2 — fresh-clone-smoke.sh comments mentioned a test-count badge
drift check that wasn't implemented. (Defer: the script's existing
test-count assertion + README badge edit serves the same purpose; an
explicit comparison would be a v0.20.2 enhancement.)

M3 — install.sh / npm-wrapper / scripts/build-binaries.ts said
'Windows is deferred to v0.20.1'. Updated to 'a future distribution
milestone (v0.21+)' since v0.20.1 IS this release.

M4 — SECURITY.md + TRUST.md referenced src/providers/xai.ts:redact.
Actual helper at src/providers/xai.ts:383 is redactSecrets. Fixed
both references.

Verification after fix-first:
- bun test: 3390 -> 3395 pass / 0 fail / 2 skip (the 5 newly-passing
  tests are the version-consistency + smoke-fixture tests that were
  failing because of the version drift B1 fixed).
- bun run demo:failure-gates: 5/5 fixtures pass.
- bun run demo:todo-cli: passes.
- scripts/release/fresh-clone-smoke.sh: ALL CHECKS PASSED.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C19. R2 fix-first closure.
Next: C20 final pre-tag review + drift pass + tag.
Codex final pre-tag review (thread 019e2718) returned fix-first on
5 small public-doc drift items left by R2 closure that hadn't been
caught.

Closures:
- Fixture 02 SPEC + Fixture 04 SPEC: '## Expected events.jsonl event
  sequence' -> '## Expected events-sketch.jsonl event sequence'
  (consistent with the events-sketch.jsonl rename in C15.1).
- scripts/demo/02-failure-gates/run-demo.ts header comment: stale
  events.jsonl reference -> events-sketch.jsonl with framing.
- CLAUDE.md status block: 'five fixtures + ledger-replay assertions'
  -> 'five fixtures + characterization tests asserting each
  production gate API refuses the wrong input (per-fixture
  events-sketch.jsonl files are author-constructed summaries, not
  real production events)'. Honest framing.
- docs/demo/02-failure-gates/README.md 'What this does NOT prove':
  removed the 'FakeProvider writes good code' phrasing since the
  demo uses TestProvider (a small IAgentProvider impl returning
  canned responses) where it needs an adapter, not FakeProvider.
- scripts/install.sh:151: 'Windows is deferred to v0.20.1' -> 'a
  future distribution milestone (v0.21+)'.
- CONTRIBUTING.md: 3390 -> 3395 (test count drift).
- docs/handoffs/2026-05-14-v0.20.0-release-notes-backfill.md:
  removed the 'all without changing the engineering surface' claim
  (false against the 3 first-run polish src/ commits) and reframed
  v0.20.1 as polish + truth sync.

Verification:
- bun test: 3395 pass / 0 fail / 2 skip
- bun run demo:failure-gates: 5/5 fixtures pass
- scripts/release/fresh-clone-smoke.sh: ALL CHECKS PASSED
- grep for each of the 5 stale refs: 0 matches

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C20. Final-review fix-first
closure. Re-dispatching Codex for the truly-final pre-tag verdict
next.
Codex final-final review (thread 019e271f) caught two stale lines
in docs/design/ROADMAP.md that hadn't been updated alongside the
rest of the bundle:

- Line 15: 'five fixtures + ledger-replay assertions' -> aligned with
  CLAUDE.md framing: 'characterization tests... events-sketch.jsonl
  files are author-constructed summaries, not real production
  events written via appendEvent'.
- Line 18: 'No new gate authority, no new milestone work --
  repackaging only' (false against the 3 first-run polish src/
  fixes inherited from earlier branch work) -> 'Three small src/
  first-run polish fixes... + No new gate authority introduced
  (rule 20); provider contract unchanged'.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C20. Final-review-2
fix-first closure.
…spec

Codex final-final-final review (thread 019e2722) caught stale release
flow references in the active planning doc that contradict the
corrected release-notes drafts.

- docs/planning/V0_20_1_POLISH_PLAN.md C17 step 3 (line ~611): the
  C17 commit-message draft still said 'gh release create' for v0.20.1
  posting. Now correctly says: tag push triggers release.yml; gh run
  watch; gh release edit (both v0.20.1 and v0.20.0).
- docs/planning/V0_20_1_POLISH_PLAN.md C20 step 2 (line 679): bumped
  test count expectation 3390+ -> 3395+.
- docs/planning/V0_20_1_POLISH_PLAN.md C20 step 6: rewrote the 'Ozzy
  posts releases' section to reflect the workflow-creates +
  Ozzy-edits flow per Codex R2 B4 closure.
- docs/planning/V0_20_1_POLISH_PLAN.md commit-shape diagram (line 45):
  updated the tag-line description to mention release.yml + edit flow.
- docs/design/V0_20_1_FIRST_RUN_POLISH_DESIGN.md Track 4 (line 134):
  updated the release-notes draft description to use 'gh release edit'
  not 'gh release create'.

Historical files not touched (frozen-in-time records):
- Codex R0 / R1 / R2 / R3 response files
- Old SESSION_M*_KICKOFF.md files
- docs/handoffs/codex-finalize/PR_BODY.md (older work)

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C20. Final-review-3
fix-first closure.
Codex final-final-final review (thread 019e272c) caught a real
public-facing release-blocker: README and the failure-demo walkthrough
both pointed at docs/demo/01-todo-cli/output/balanced/state/events.jsonl,
but the actual committed file is at
docs/demo/01-todo-cli/output/balanced/events.jsonl (no /state/ subdir
in the captured-output structure).

Fixed in 3 places:
- README.md "Quick demo" inspect block
- docs/demo/02-failure-gates/README.md "this demo is NOT a full
  lifecycle" intro pointer
- docs/demo/02-failure-gates/README.md "Why events-sketch instead of
  events.jsonl" framing pointer

This was the last public-facing drift item. README links now resolve
to existing files (verified). Smoke green. Test count green. Ready
to tag.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C20. Final-review-4
fix-first closure.
Per Ozzy's push-authority grant for this session ("be responsible
about tech debt; capture fix ideas; clean green handoff"), this
handoff records 5 internal-doc improvement items that Codex flagged
across review rounds R0-R6 but were deliberately not closed in
v0.20.1:

- TD-1: design-doc 3390 historical references (frozen-in-time record)
- TD-2: ABOUT.md v0.20.1 addendum (polish, not drift)
- TD-3: ROADMAP.md pre-existing markdown lint warnings (~30, not
  introduced by v0.20.1 edits)
- TD-4: active plan-doc step descriptions reference past states
  (frozen-in-time record; needs an 'archived' header)
- TD-5: smoke script test-count badge comparison (defense-in-depth
  improvement; existing 6 drift checks + Codex review catch the
  same class)

Each item carries: file location, issue, why-deferred rationale,
v0.20.2 fix recommendation, owner.

Plus a record of all closures landed across the 7 fix-first commits
(C15.1 / C19.1-C19.5 / 75d36bd) and 4 lessons for future releases.

Push authority sign-off at the bottom.

Plan: docs/planning/V0_20_1_POLISH_PLAN.md C20.
@omerakben
omerakben merged commit c1f412b into main May 14, 2026
@omerakben
omerakben deleted the finalize/v0.20.1-first-run-polish branch May 30, 2026 03:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants