Skip to content

Add post-sync readiness endpoint and bounded ocx ready wait - #569

Open
diegocantarero wants to merge 6 commits into
lidge-jun:devfrom
diegocantarero:agent/macos-post-sync-readiness
Open

Add post-sync readiness endpoint and bounded ocx ready wait#569
diegocantarero wants to merge 6 commits into
lidge-jun:devfrom
diegocantarero:agent/macos-post-sync-readiness

Conversation

@diegocantarero

@diegocantarero diegocantarero commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve GET /healthz as immediate process liveness
  • add exact unauthenticated GET /readyz for post-sync readiness
  • add ocx ready [--json] [--wait [--timeout <seconds>]]
  • document the liveness/readiness contract and embedding invariants

Problem

The listener can bind before startup catalog/config synchronization finishes. A consumer that treats /healthz as readiness can therefore connect to a live proxy before model catalog convergence.

This closes that portable startup race. It was observed while macOS restored a Codex client during login, but the implementation is platform-neutral and does not include macOS process-management policy.

Behavior

  • /healthz is unchanged.
  • /readyz returns 200 only for ready.
  • pending and failed return 503 with Retry-After: 1.
  • POST /readyz and GET /readyz/ return a JSON 404 (the endpoint matches exact pathname + GET only, regardless of whether gui/dist exists).
  • readiness is owned by a private one-shot gate per server instance
  • startup sync becomes ready only for ok: true with no nonempty warning
  • ocx ready validates service identity and HTTP/status consistency
  • --wait uses one hard deadline across discovery, probes, polling, and sleep
  • older proxies without /readyz fail closed as readiness unavailable
  • downstream startup watchers can use ocx ready --wait instead of guessing from liveness

/readyz unauthenticated response contract

Exactly these six fields, nothing else:

{
  "service": "opencodex",
  "version": "<string>",
  "uptime": "<number>",
  "pid": "<integer>",
  "port": "<integer>",
  "status": "pending | ready | failed"
}

Why this is safe for an unauthenticated caller: every field is already exposed by the existing unauthenticated /healthz (service, version, uptime, pid, port) or is the readiness state itself (status) that the endpoint exists to report. No sync message, warning text, catalog path, provider output, account data, or diagnostic text is included, and the strict /readyz probe treats any foreign or malformed body as unreachable.

Compatibility and privacy

  • existing /healthz clients retain their behavior
  • existing startServer(port) callers remain source-compatible and fail closed as pending unless they own and transition a supplied gate
  • responses expose only fixed readiness state and the existing identity metadata
  • no provider output, account data, paths, sync warnings, or diagnostic text are exposed

Merge-ready work (rebase + review fixes)

  • rebased onto current dev (fa51fce54), keeping both sides of the previously conflicting hunks: startServer(port, { readinessGate }) with scheduleCatalogPrewarm(), the management-auth imports, and deadlineAt discovery budgeting with the source discriminator
  • adopted dev's existing deadlineAt discovery budget instead of the duplicate deadlineMs/probeBudget mechanism
  • re-targeted the ready docs to docs-site/.../reference/cli/lifecycle.md in all locales and documented the 1-300 second --timeout range
  • fixed CodeRabbit findings: deadline test off-by-one (vacuous assertion) and AbortSignal.timeout stub ordering
  • made the exact-method /readyz contract deterministic (JSON 404 for non-GET/trailing-slash paths)

Validation

  • bun run typecheck: pass
  • focused readiness/liveness/CLI suite: 185 pass, 0 fail
  • full local suite: only pre-existing environment-limited failures remain (Windows symlink EPERM tests and a codex-v2-gate assertion that depends on an npm-style node_modules/.bin/codex.cmd shim; both reproduce outside this PR); the maintained GitHub CI matrix is the full-suite acceptance gate
  • bun run privacy:scan: pass
  • docs-site build (bun run build): pass
  • GitHub CI: pending on the rebased head

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Wibias, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 35f379b2-8906-4448-8ca7-faab1f4fbae6

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1b94f and d54ceec.

📒 Files selected for processing (1)
  • README.md
📝 Walkthrough

Walkthrough

The proxy now tracks startup readiness per server instance, exposes a strict unauthenticated /readyz endpoint, and adds the ocx ready CLI command with JSON output, polling, deadlines, validation, and documented exit codes. Tests cover readiness transitions, endpoint contracts, CLI dispatch, deadlines, and compatibility behavior.

Changes

Proxy readiness flow

Layer / File(s) Summary
Startup readiness gate
src/server/readiness.ts, src/cli/index.ts, structure/03_catalog-and-subagents.md, tests/update-notify.test.ts
Each startServer invocation receives an isolated one-shot gate, and model synchronization transitions it to ready or failed.
Readiness endpoint and strict probe
src/server/index.ts, src/server/proxy-liveness.ts
GET /readyz returns sanitized identity and readiness data; discovery and probing enforce absolute deadlines and validate HTTP/body consistency.
Ready CLI parsing and polling
src/cli/ready.ts, src/cli/index.ts, src/cli/help.ts, tests/cli-restart-health.test.ts
ocx ready validates arguments before preflight, performs single or bounded polling probes, supports JSON output, and returns codes 0, 1, or 64.
Readiness integration coverage
tests/cli-ready.test.ts, tests/cli-ready-subprocess.test.ts, tests/proxy-liveness.test.ts, tests/server-live.test.ts
Tests cover parser validation, deadline precedence, strict readiness schemas, subprocess behavior, endpoint isolation, startup transitions, and CLI routing.
Readiness documentation
README.md, docs-site/src/content/docs/*/reference/cli.md
Documentation describes /readyz, ocx ready, polling and timeout semantics, JSON fields, exit codes, fail-closed behavior, and separation from /healthz.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: enhancement

Suggested reviewers: lidge-j, ingwannu

Sequence Diagram(s)

sequenceDiagram
  participant CLI as ocx ready
  participant Discovery as findLiveProxy
  participant Probe as probeReadiness
  participant Proxy as Proxy /readyz
  CLI->>Discovery: Discover live proxy within deadline
  Discovery-->>CLI: Return pid and port
  CLI->>Probe: Probe readiness
  Probe->>Proxy: GET /readyz
  Proxy-->>Probe: Return status and identity
  Probe-->>CLI: Return validated readiness
  CLI-->>CLI: Poll, report JSON, and exit
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: a post-sync readiness endpoint and a bounded ocx ready wait workflow.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@diegocantarero
diegocantarero marked this pull request as ready for review July 27, 2026 22:17
coderabbitai[bot]

This comment was marked as outdated.

@Wibias
Wibias marked this pull request as draft July 28, 2026 01:50
@Wibias

Wibias commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Please change the PR to ready for review once you are finished @diegocantarero

@lidge-jun

Copy link
Copy Markdown
Owner

NEEDS-SECURITY-REVIEW — the design is sound, the boundary is what needs a reviewer.

The separation is the right one. src/server/readiness.ts:23-93 keeps a one-shot per-server gate, and src/cli/index.ts:208-216 flips it only after syncModelsToCodex returns ok: true with no warning. That means the listener can be up while the gate is still pending, which is exactly the state the old /healthz-only contract could not express. Keeping /healthz as immediate liveness and adding /readyz on top is cleaner than overloading one endpoint.

The tests are unusually strong for a PR this size. tests/server-live.test.ts:823-1040 exercises the real endpoint — exact path and method, unauthenticated behaviour, response sanitization, per-server isolation. tests/cli-ready.test.ts covers the parser, terminal failure, malformed identity, and both sides of the deadline boundary, and tests/cli-ready-subprocess.test.ts:71-181 dispatches a real subprocess rather than mocking it. These bind the public contract instead of searching source.

Why it still needs security review. This deliberately adds a new unauthenticated endpoint. The response is narrowly sanitized to fixed identity and status fields, and 503 with Retry-After: 1 is a reasonable pending signal — but "we added an unauthenticated route and constrained its body carefully" is precisely the change MAINTAINERS.md:33-34 wants a second pair of eyes on. A reviewer should confirm the identity fields cannot become a fingerprinting surface and that the gate cannot be observed to leak startup timing about a private deployment.

The three conflicts all want both sides kept, not a choice:

  • src/cli/index.tsstartServer(port, { readinessGate }) versus current startServer(port) plus scheduleCatalogPrewarm(). Keep the gate and the prewarm.
  • src/server/index.ts — the import hunk, where dev adds management-auth session initialization. Both are needed.
  • src/server/proxy-liveness.ts — your deadline budgeting versus current dev's source: "config" discriminator on the configured-port fallback. Both.

What happens next: rebase keeping both sides of those three, mark it ready when you are satisfied, run fresh CI, then request security review. dev CI is fully green now, so checks will reflect this branch. I found no functional blocker in the readiness design.

@lidge-jun

Copy link
Copy Markdown
Owner

Thanks for putting this together. The separation between process liveness and post-sync readiness is the right design, and it addresses a real startup race.

Before maintainer review, please:

  1. Rebase this branch onto the current dev; the branch has drifted since the last CI run.
  2. Mark the PR ready for review so the full CI matrix and CodeRabbit run against the rebased head.
  3. Arrange an independent security review for the unauthenticated /readyz endpoint. Please document in the PR body exactly every field and state the endpoint exposes, and explain why none of that information is sensitive to an unauthenticated caller. If that cannot be established clearly, gate the endpoint instead.

The design direction is right and the startup race is real — we want this.

diegocantarero and others added 5 commits August 3, 2026 06:04
Document the sanitized HTTP identity separately from the CLI --json shape.
Clarify failed-immediate wait behavior and the default 45s timeout.
Keep the readiness contract aligned across supported locales.
Add real subprocess coverage for terminal failed readiness.
- Rebase the readiness PR onto current dev: adopt the existing deadlineAt
  discovery budget in proxy-liveness instead of the duplicate deadlineMs/
  probeBudget mechanism, keeping dev's attempts/source discriminator.
- Answer POST /readyz and GET /readyz/ with a deterministic JSON 404 so the
  exact-GET contract never depends on whether gui/dist exists (the GUI SPA
  fallback previously served index.html with 200 for those paths).
- Re-target the ready docs to reference/cli/lifecycle.md across all locales
  and document the 1-300 second --timeout range.
- Fix the deadline test off-by-one and AbortSignal stub ordering; adapt the
  catalog-prewarm source-order test to startServer(port, { readinessGate }).
Extract the 750ms per-probe ceiling as DEFAULT_PROBE_TIMEOUT_MS in
proxy-liveness and import it from the ready CLI instead of redeclaring
IO_TIMEOUT_CAP_MS, so liveness and readiness defaults cannot diverge.
@Wibias
Wibias force-pushed the agent/macos-post-sync-readiness branch from 0a1b94f to 55f4937 Compare August 3, 2026 04:49
@Wibias
Wibias marked this pull request as ready for review August 3, 2026 04:49

@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: 55f493777a

ℹ️ 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/cli/ready.ts
…tion

verifyPidIdentity spawns WMIC/PowerShell (up to seconds on Windows) and is
only needed for kill targets. runReady's production discovery now passes
verifyPidFn: () => null so no OS command-line check runs outside the wait
deadline; the /healthz identity marker and strict /readyz contract
validation are unchanged.
@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

[shipping-github] Addressed feedback

feedback: issue_comment:5154177408
commit: d54ceec

@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

[shipping-github] Security review requested

@lidge-jun — requesting the deliberate security review you asked for on the unauthenticated GET /readyz endpoint before merge.

Head: d54ceec8b (rebased on dev, required CI green, ready for review). The exact response contract — exactly {service, version, uptime, pid, port, status}, no diagnostics — and the sensitivity rationale are documented in the PR description. The endpoint is stricter than /healthz: non-GET and trailing-slash requests return JSON 404 regardless of gui/dist, and ocx ready probes fail closed as unreachable on foreign or malformed bodies.

This is the last open gate before merge; please review the boundary.

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.

3 participants