finalize(v0.20.1): first-run polish + a11y + distribution audit - #30
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (58)
📝 WalkthroughWalkthroughThis 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. ChangesFirst-run polish
Estimated code review effort 🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
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.eventPointeris now required and validated, and all writers compute the event line before writing the gate; non-emptyactionableSuggestionsare enforced. - Distribution: npm wrapper enforces HTTPS, verifies cached binaries against a
.sha256sidecar, 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 resumealias,--effort low|medium|highdeprecation aliases, baredoctoraggregate 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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| return parsed | ||
| } | ||
|
|
||
| async function download(url, destination) { |
There was a problem hiding this comment.
| export async function writeInterruptStopGate( | ||
| runPaths: RunPaths, | ||
| runId: string, | ||
| signal: 'SIGINT' | 'SIGTERM' = 'SIGINT', | ||
| now: () => string = () => new Date().toISOString(), | ||
| ): Promise<void> { |
There was a problem hiding this comment.
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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
💡 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".
| const health = await registry.get(provider).health() | ||
| if (health.authStatus !== 'ok') return 'fake' | ||
| } |
There was a problem hiding this comment.
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 👍 / 👎.
| if (value in aliases) { | ||
| return { kind: 'ok', value: aliases[value]!, alias: value } |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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 winUpdate 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 liftAvoid rescanning
events.jsonlon every append.
appendEvent()is now doing a fullreadFile()+ 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 winAdd coverage for the new live approve execution branches.
This test only locks fixture-mode refusal. Please add cases for live
approvesuccess, liveapprovefailure (approve-failed), and unknownrunId(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 valueConsider 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 winPrefer
process.execPathfor consistency.Line 93 was updated to use
process.execPathinstead 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
⛔ Files ignored due to path filters (5)
code-oz-gui/bun.lockis excluded by!**/*.lockcode-oz-gui/docs/screenshots/decisions-task.pngis excluded by!**/*.pngcode-oz-gui/docs/screenshots/events-errors.pngis excluded by!**/*.pngcode-oz-gui/docs/screenshots/hero.pngis excluded by!**/*.pngcode-oz-gui/docs/screenshots/workspace-form.pngis excluded by!**/*.png
📒 Files selected for processing (86)
.github/workflows/release.ymlREADME.mdcode-oz-gui/README.mdcode-oz-gui/app/api/helper/ask/route.tscode-oz-gui/app/api/run/[runId]/approve/route.tscode-oz-gui/app/globals.csscode-oz-gui/app/page.tsxcode-oz-gui/components/Board.tsxcode-oz-gui/components/Card.tsxcode-oz-gui/components/Composer.tsxcode-oz-gui/components/DecisionsView.tsxcode-oz-gui/components/Drawer.tsxcode-oz-gui/components/EventsView.tsxcode-oz-gui/components/Footer.tsxcode-oz-gui/components/PhaseColumn.tsxcode-oz-gui/eslint.config.mjscode-oz-gui/fixtures/sample-run/README.mdcode-oz-gui/fixtures/sample-run/current.jsoncode-oz-gui/fixtures/sample-run/events.jsonlcode-oz-gui/lib/code-oz-spawn.tscode-oz-gui/lib/repo-root.tscode-oz-gui/lib/run-registry.tscode-oz-gui/lib/run-store.tscode-oz-gui/lib/types.tscode-oz-gui/next.config.tscode-oz-gui/package.jsoncode-oz-gui/playwright.config.tscode-oz-gui/scripts/capture-screenshots.tscode-oz-gui/tests/e2e/a11y.e2e.tscode-oz-gui/tests/e2e/happy-path.e2e.tscode-oz-gui/tests/unit/approve-route.test.tscode-oz-gui/tests/unit/code-oz-spawn.test.tscode-oz-gui/tests/unit/helper-ask.test.tscode-oz-gui/tests/unit/run-store-approval.test.tsdocs/ABOUT.mddocs/PROVIDER_SETUP.mddocs/design/RELEASE_NOTES_v0.20.0-alpha.0.mddocs/design/RELEASE_NOTES_v0.20.1-alpha.0.mddocs/handoffs/codex-finalize/A1-cli-first-run.mddocs/handoffs/codex-finalize/A10-hygiene.mddocs/handoffs/codex-finalize/A2-gui-first-run.mddocs/handoffs/codex-finalize/A3-distribution.mddocs/handoffs/codex-finalize/A4-binaries.mddocs/handoffs/codex-finalize/A5-docs.mddocs/handoffs/codex-finalize/A6-errors.mddocs/handoffs/codex-finalize/A7-providers.mddocs/handoffs/codex-finalize/A8-visual.mddocs/handoffs/codex-finalize/A9-a11y.mddocs/handoffs/codex-finalize/CODEX_GOAL_R2.txtdocs/handoffs/codex-finalize/FIRST_RUN_AUDIT.mddocs/handoffs/codex-finalize/FIRST_RUN_FIXES.mddocs/handoffs/codex-finalize/PR_BODY.mddocs/homebrew/README.mdnpm-wrapper/index.cjsscripts/build-binaries.tsscripts/install.shscripts/smoke-test.tssrc/cli.tssrc/commands/doctor.tssrc/commands/run.tssrc/phases/build.tssrc/phases/define.tssrc/phases/plan.tssrc/phases/review.tssrc/phases/schedule-attempt.tssrc/phases/verify.tssrc/providers/claude.tssrc/providers/codex.tssrc/providers/first-run-fake-fixture.tssrc/providers/invoke.tssrc/state/events.tssrc/state/gates.tssrc/state/schemas.tssrc/worktree/load-or-create-run-worktree.tstests/build-binaries.test.tstests/build-phase.test.tstests/ci-workflows.test.tstests/cli-run-args.test.tstests/commands-doctor.test.tstests/commands-run.test.tstests/homebrew-formula.test.tstests/install-script.test.tstests/npm-wrapper.test.tstests/providers-claude.test.tstests/providers-codex.test.tstests/state-gates.test.ts
| 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" |
There was a problem hiding this comment.
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.
| 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')); |
There was a problem hiding this comment.
🧩 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 2Repository: 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 -20Repository: 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 -20Repository: 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.
| 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.
| "typecheck": "tsc --noEmit", | ||
| "test": "bun test tests/unit", | ||
| "lint": "eslint .", | ||
| "clean": "next clean", |
There was a problem hiding this comment.
🧩 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:
- 1: https://nextjs.im/docs/15/app/api-reference/cli/next/
- 2: https://nextjs.org/docs/app/api-reference/cli/next
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.
| - `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` |
There was a problem hiding this comment.
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.
| - `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. |
There was a problem hiding this comment.
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).
| | `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 | |
There was a problem hiding this comment.
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.
| 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. |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
| 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.
| 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 |
There was a problem hiding this comment.
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).
| 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', | ||
| ]) | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
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.
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.
DO NOT MERGE
Summary
Finalizes the v0.20.1 first-run polish and distribution audit fixes.
FIRST_RUN_AUDIT.md.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: produceddist/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, andbun run test:e2epassed.code-oz-gui:env DISABLE_HMR=true bun run test:a11ypassed outside the sandbox after sandboxed Chromium hit a macOS Mach-port permission failure.Notes
bun run test:e2e.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
UI / Accessibility