Skip to content

Roadmap item 11: Add inactive deploy and rollback gates - #252

Merged
yihanzhu merged 13 commits into
mainfrom
ystack/roadmap-11/deploy-rollback-v1
Sep 7, 2026
Merged

Roadmap item 11: Add inactive deploy and rollback gates#252
yihanzhu merged 13 commits into
mainfrom
ystack/roadmap-11/deploy-rollback-v1

Conversation

@yihanzhu

@yihanzhu yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Roadmap step 11 asks for environment tiers, a named production gate, rehearsed rollback, and delivery evidence, exposing deploy, status, and rollback as environment-scoped capabilities that require the risk tier's named authorization and bind a release to verified source and evidence. This adds those contracts and their gate evaluator as an inactive unit. Nothing deploys, rolls back, or reads an environment.

deploy/v1/: environment-tiers.json (dev, staging, production; each names its gate and the actor roles allowed per request kind), deploy_contracts.jq (release record, deploy/status/rollback requests, authorization record, rollback rehearsal record, and the two control evaluations consumed), validate-deploy-document.sh, deploy-gates.jq + evaluate-deploy.sh (six inputs → one canonical deploy_gate_evaluation with admissible or refused and a reason id: tier-unknown, authorization-missing/stale/wrong-tier, release-unverified, rollback-unrehearsed, kill-switch, duty-violation, malformed), and deploy-decision.json pinning the driver, program, module, validator, and tiers digests. Production carries an independent final guard: refused unless a named, same-tier, authorized operator record backs it, even when everything else passes. Time comes from the request, never a clock. The rehearsal record binds a rollback request from a non-operator actor. Every output carries authority none and qualification unavailable; admissible means "may be handed to a deployment adapter after the transition", never a grant.

adapter-tests/v1/fakes/deploy-dormant.sh is a fake deployment adapter in the dormant-publisher pattern: it accepts an admissible evaluation and returns a refusal receipt ("deployment disabled in construction mode"). It is exercised from the focused test rather than the adapter-tests runner, whose pinned producer→forge pipeline has no deployment phase; adding one is its own unit.

Notes for reviewers: the risk-gates evaluation is bound by policy-set identity and refused on any duty.* reason rather than required to be satisfied, because that evaluator has no satisfied result by design. deploy.malformed also covers a bundle whose control references disagree.

Review-size note: two stacked commits (contracts + validator, 282 lines; evaluator + decision record + fake adapter, 383 lines). The focused suite takes about 8 minutes because each evaluator run stages the pinned jq, as the control suites do.

Proof run on b87e43b:

  • bash scripts/test/deploy-rollback-gates.test.sh → 72 focused checks passed
  • shellcheck 0.11.0 -x -S style clean; bash scripts/check-rename.sh clean
  • bash scripts/test/portable-core-schema.test.sh → failures: 0

Construction mode: inactive output, no human gate.

🤖 Generated with Claude Code

ci and others added 2 commits September 6, 2026 13:28
Roadmap step 11 needs environment tiers, a named production gate, a rehearsed
rollback, and delivery evidence. This first commit lands the paper: the three
environment tiers and the shape of every document the gates will read. The gate
evaluator and the fake dormant deployment adapter follow in the next commit on
this branch, because the whole unit is about 660 lines of product code and the
size budget is 300-400.

deploy/v1/environment-tiers.json names dev, staging, and production, the gate
each one requires (production takes only a named operator authorization; the
lower tiers take the routine gate), and which actor roles may ask for deploy,
status, or rollback. deploy/v1/deploy_contracts.jq holds the shapes: the release
record binding a release id to a verified source commit and tree plus its
verifier, review, CI, and optional packaging-manifest evidence; the deploy,
status, and rollback requests; the operator authorization record; and the
rollback rehearsal record. The packaging release manifest is referred to by shape
only, so this unit does not depend on step 10's code.

deploy/v1/validate-deploy-document.sh checks one canonical document of a named
kind and prints nothing when it is well formed. It is fail closed: one error id
on stderr, no output, for a wrong or unknown kind, a non-canonical, multi-root,
oversized, too-deep, or symlinked file, or a body that does not match its
contract.

Nothing here deploys, rolls back, or reads any environment. There is no
deployment adapter in this repository and adding one is a post-transition,
operator-gated change. No credential, network, model call, authority, or
qualification is involved.

Proof on this commit:
- /opt/homebrew/bin/shellcheck -x -S style over the new scripts: clean
- bash scripts/test/deploy-rollback-gates.test.sh:
  deploy and rollback contracts: 24 focused checks passed
- bash scripts/check-rename.sh: clean

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This is the second half of Roadmap step 11, stacked on the contracts commit. It
adds the evaluator that reads a capability request together with everything that
would have to be true before a deployment adapter could ever see it, and the fake
dormant deployment adapter that refuses to be one.

deploy/v1/evaluate-deploy.sh takes six documents - the deploy, status, or
rollback request, the release record, the operator authorization, the rollback
rehearsal, the risk-gates evaluation, and the kill-switch evaluation - and
returns one canonical deploy_gate_evaluation whose decision is admissible or
refused. Admissible means only that the request may be handed to a deployment
adapter after the operating-mode transition; the output always records
authority "none" and qualification "unavailable" and never carries a grant,
qualification reference, or activation.

A refusal names why: tier-unknown, authorization-missing, authorization-stale,
authorization-wrong-tier, release-unverified, rollback-unrehearsed, kill-switch,
duty-violation, or malformed. Production carries an independent final guard, so a
production request is refused unless a named, authorized, same-tier operator
record backs it, whatever else passes. A rollback from a non-operator actor is
admissible only when the rehearsal it names covers the same environment and the
same release pair and succeeded; an operator is not blocked on one, because the
rehearsal requirement gates autonomous maintenance rather than the human. The
risk-gates evaluator has no satisfied result by design, so this unit records its
reference and refuses on its duty reasons rather than demanding a verdict it
cannot produce.

Time comes from the request, never a clock, so the same six inputs always produce
the same bytes. Inputs must be regular non-symlink files holding exactly one
canonical JSON document within fixed bounds; the tier policy is re-checked
through the shipped validator inside a private runtime before the gates run, and
every file is compared again afterwards, so a file moved mid-run is refused. A
recognizable deploy envelope with a bad body is refused as malformed rather than
crashing.

adapter-tests/v1/fakes/deploy-dormant.sh follows the dormant publisher: given an
admissible evaluation it returns a refusal receipt saying deployment is disabled
in construction mode, and given anything else it returns nothing. It is not
registered in the fake adapter contract matrix, whose inventory is a fixed
producer-then-forge pipeline with pinned package digests; adding a deployment
phase there is its own change.

Nothing here deploys, rolls back, or reads any environment. There is no real
deployment adapter in this repository and adding one is a post-transition,
operator-gated change.

Proof on this commit:
- /opt/homebrew/bin/shellcheck -x -S style over the new scripts: clean
- bash scripts/test/deploy-rollback-gates.test.sh:
  deploy and rollback gates: 72 focused checks passed
- bash scripts/check-rename.sh: clean

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 6, 2026

Copy link
Copy Markdown

Deploying ystack with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6901e3c
Status: ✅  Deploy successful!
Preview URL: https://f1e8791e.fabrica-6yx.pages.dev
Branch Preview URL: https://ystack-roadmap-11-deploy-rol.fabrica-6yx.pages.dev

View logs

@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: b87e43b
Reviewed-base: fae1848
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-22337/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The deploy gate can mark a request admissible even when an input risk-gate evaluation explicitly reports a violation. That undermines the core decision the new evaluator is meant to make.

Review comment:

  • [P1] Refuse violated risk-gate evaluations — /var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.G36XhscQuK/deploy/v1/deploy-gates.jq:91-94
    When the supplied risk_gate_evaluation has verdict: "violated" for a non-duty reason produced by the risk gate, such as decision.rejected or decision.role-denied, this only checks for duty.* reason ids and adds no refusal. With the other inputs valid, that request becomes deploy.admissible even though the component defines admissible as all gates being satisfied [docs/components.md:724-727]. Please gate on the risk verdict, not only duty-prefixed reasons.

The gates refused a risk-gate evaluation only when its reasons were duty
violations, so a verdict of violated for a rejected or role-denied decision
added no refusal and a request could still be admissible. A violated verdict
for any non-duty reason now refuses with deploy.risk-gate-violated; the
program digest in the decision record is re-pinned.

Proof: deploy-rollback-gates 73/73 (new case: violated verdict with
decision.rejected is refused), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 9fbb9f9
Reviewed-base: fae1848
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-88809/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The gate can produce an incomplete refusal reason set for mixed duty and non-duty risk violations. The decision still refuses, but the contract's reason semantics are wrong for that scenario.

Review comment:

  • [P2] Emit risk-gate failures for mixed reasons — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.ZkfYc1BHr0/deploy/v1/deploy-gates.jq:93-95
    When a risk-gate result contains both a duty.* reason and a non-duty violation such as decision.rejected, this any duty == false check suppresses deploy.risk-gate-violated and reports only deploy.duty-violation. The documented contract says deploy.risk-gate-violated applies to any non-duty risk violation, so mixed reason sets lose the reason downstream consumers need to distinguish the rejected risk gate from a duty-only failure.

A violated risk-gate verdict whose reasons mixed a duty violation with another
violation reported only deploy.duty-violation, hiding the second refusal. Any
violated verdict now carries deploy.risk-gate-violated as well; the program
digest in the decision record is re-pinned.

Proof: deploy-rollback-gates 73/73, shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 405c429
Reviewed-base: fae1848
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-10546/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The deploy gate can accept fabricated minimal control-evaluation documents and produce an admissible result without evidence that the risk/kill-switch evaluators actually ran. This undermines the new gate's core safety contract.

Review comment:

  • [P2] Require full control-evaluation contracts — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.n85BCUdz5J/deploy/v1/deploy_contracts.jq:164-172
    When all other deploy inputs pass, this predicate accepts a canonical kill_switch_evaluation or risk_gate_evaluation stub with only policy_set, verdict, and reason_ids; for example a fabricated kill-switch document with verdict: "satisfied" has no state_ref, decision_ref, or duty_evaluation_ref binding it to a real kill-switch run, but deploy-gates.jq can still return admissible. The component docs say the deploy gate consumes the control evaluations and that an active kill switch must refuse the request, so this should validate the actual control output shape rather than a minimal claim.

ci and others added 2 commits September 6, 2026 15:11
The deploy gate accepted a risk-gate or kill-switch evaluation that carried
only a policy set, a verdict and reason ids, so a fabricated kill-switch
document saying "satisfied" - with no state, attempt, decision or duty
references at all - passed the gate as if a real evaluator had produced it.

deploy_contracts.jq now checks each of the two control evaluations against the
exact document its own evaluator emits, field for field: the risk-gate body must
carry exactly the fourteen fields control/v1/risk-gates.jq writes, and the
kill-switch body exactly the thirteen fields control/v1/kill-switch.jq writes,
with every content reference bound to its media type and every document
reference to its schema version and kind. A missing field, an extra field, or a
reference of the wrong sort is now refused as deploy.malformed. Nothing else
was loosened or tightened.

The focused test's two control fixtures become full, internally consistent
documents rather than three-field stubs, and four negative cases cover a
kill-switch document with no state reference, a risk document with no stage,
and an unknown body key on either. deploy-decision.json re-pins the contract
module digest the driver checks.

Proof: /opt/homebrew/bin/shellcheck -x -S style clean;
bash scripts/test/deploy-rollback-gates.test.sh -> 77 focused checks passed;
bash scripts/check-rename.sh clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…loy-rollback-v1

# Conflicts:
#	ci/required-files.txt
@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 64f9f6e
Reviewed-base: ce249ec
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-43946/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The deploy document contract accepts invalid timestamps, which can let malformed requests or authorizations pass validation and affect gate decisions. This should be fixed before the patch is considered correct.

Review comment:

  • [P2] Validate timestamp ranges, not just the string shape — /var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.lEzY2VRHmI/deploy/v1/deploy_contracts.jq:9-11
    When a request or authorization contains an impossible but correctly formatted time such as 2026-99-99T99:99:99Z, timestamp_ok returns true, so the validator accepts the document and the evaluator can make stale/expiry decisions using invalid timestamps. The control gate code already uses calendar/time bounds for this kind of check; this deploy contract should reject out-of-range months, days, hours, minutes, and seconds as malformed.

A correctly formatted but impossible time such as 2026-99-99T99:99:99Z passed
the timestamp check, so stale and expiry decisions could run on invalid
values. Month, day (leap years included), hour, minute, and second are now
range-checked; the contracts digest in the decision record is re-pinned.

Proof: deploy-rollback-gates 79/79 (new cases: an impossible date and a
non-leap-year 29 February are refused), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: c8ffb70
Reviewed-base: ce249ec
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-64081/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The rollback gate can admit an autonomous rollback using after-the-fact rehearsal evidence. That violates the documented deployment transition invariant, so the patch is not correct as-is.

Review comment:

  • [P2] Reject rollback rehearsals dated after the request — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.bvK237AgFA/deploy/v1/deploy-gates.jq:83-89
    For a non-operator rollback, this only checks that the supplied rehearsal succeeded and matches the tier/release pair; it never checks that .body.rehearsed_at is at or before the request's .body.requested_at. A publisher can therefore get an autonomous rollback marked admissible using a rehearsal record dated after the rollback request, even though the transition doc requires the rehearsal to be recorded before any autonomous rollback is possible (docs/transition.md:206-209).

ci and others added 2 commits September 6, 2026 18:06
The rehearsal gate checked outcome, tier, and release pair but not time, so a
rehearsal dated after the request could make an autonomous rollback
admissible. A rehearsal recorded after requested_at now refuses with
deploy.rollback-unrehearsed; the program digest in the decision record is
re-pinned.

Proof: deploy-rollback-gates 80/80 (new case: a rehearsal dated after the
request is refused), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…loy-rollback-v1

# Conflicts:
#	README.md
#	ci/required-files.txt
#	docs/components.md
@yihanzhu

yihanzhu commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 18291ee
Reviewed-base: dd9f8aa
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-14172/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The deploy gate accepts malformed control-evaluation states that the upstream evaluators would not emit, which can make otherwise valid deployment requests admissible despite unresolved risk or kill-switch signals. These are blocking correctness issues for the new gate logic.

Full review comments:

  • [P2] Reject inconsistent risk-gate verdicts — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.RjHJdvQlDb/deploy/v1/deploy_contracts.jq:199-203
    When the supplied risk-gate evaluation has classification.minimum_tier: "unknown" but verdict: "inconclusive", this contract still accepts it as well formed. Because deploy-gates.jq only refuses risk inputs with verdict: "violated" or duty reasons, an otherwise valid deploy can be marked admissible even though the real risk evaluator rejects that combination; the deploy docs say these inputs are checked against the full evaluator output (docs/components.md:739-742).

  • [P2] Reject inconsistent kill-switch evaluations — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.RjHJdvQlDb/deploy/v1/deploy_contracts.jq:222-229
    When the supplied kill-switch evaluation says verdict: "satisfied" but carries a stop/inconclusive reason such as kill.stop-active, this validator accepts it and deploy-gates.jq then admits the request because it trusts only the verdict. The real kill-switch evaluator only emits satisfied with reason_ids == ["kill.cleared-current"], and the deploy docs say an active kill switch must refuse the request (docs/components.md:743-744).

…n tier

A kill-switch evaluation could say satisfied while carrying a stop reason, and
a risk-gate evaluation could say inconclusive with an unknown minimum tier or a
tier-unknown reason that disagreed with its classification; the gates trusted
the verdict alone. Both evaluations must now be consistent the way their
evaluators emit them, an unknown minimum tier refuses with
deploy.risk-gate-violated, and the contracts and program digests are re-pinned.

Proof: deploy-rollback-gates 84/84 (four new cases), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yihanzhu

yihanzhu commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: b5e40f1
Reviewed-base: dd9f8aa
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-61718/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The new deploy gate can admit inconsistent risk-gate inputs and same-timestamp rollback rehearsals that should fail closed. These are safety gate correctness issues in the added component.

Full review comments:

  • [P2] Refuse mismatched risk-gate verdicts — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.P2jWghawav/deploy/v1/deploy_contracts.jq:199-199
    When a supplied risk evaluation has verdict: "inconclusive" but carries a violation reason such as decision.rejected or risk.tier-downgrade, this contract still accepts it because it only checks that reason IDs are sorted IDs. deploy-gates.jq then admits the request unless the verdict is exactly violated or the tier is unknown, so a shape-exact but non-evaluator risk document can bypass the deploy risk refusal despite the documented “own evaluator emits” contract in docs/components.md#inactive-deploy-and-rollback-gates.

  • [P2] Reject same-time rollback rehearsals — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.P2jWghawav/deploy/v1/deploy-gates.jq:91-91
    For a non-operator rollback where rehearsed_at is exactly equal to requested_at, this check treats the rehearsal as valid because it only rejects timestamps after the request. The comment says the rehearsal must precede the request, and with second-level timestamps equality cannot prove the rehearsal was already recorded, so this should fail closed with a >= comparison.

ci and others added 2 commits September 6, 2026 20:24
… precedence

A risk-gate evaluation saying inconclusive while carrying a violation reason
passed the contract and the gate admitted it; the verdict must now be exactly
what the evaluator derives from its reasons (violated iff any violation,
inconclusive iff only the two unknowns). The previous consistency rule named a
reason id the evaluator never emits and is replaced. A rehearsal recorded in
the same second as the request could not prove it came first and is now
refused. Contracts and program digests are re-pinned.

Proof: deploy-rollback-gates 86/86 (new cases: inconclusive with a violation
reason and violated without one are malformed; a same-second rehearsal is
refused), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…loy-rollback-v1

# Conflicts:
#	README.md
#	ci/required-files.txt
#	docs/components.md
@yihanzhu

yihanzhu commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 89cb11e
Reviewed-base: 3cbd236
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-44308/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

The deploy evaluator can admit a high-risk request into a routine tier, which breaks the intended deployment gate semantics. The dormant fake adapter also accepts malformed admissible-looking evaluations, so the contract proof is too weak.

Full review comments:

  • [P1] Enforce tier risk before admitting requests — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.krb2Z4mvml/deploy/v1/deploy-gates.jq:96-98
    When a dev or staging request is paired with a valid risk_gate_evaluation whose classification.minimum_tier is high but whose verdict remains inconclusive, this branch adds no refusal because it only rejects violated or unknown. That makes a routine-tier environment admissible for a high-risk request, even though the tier policy says dev/staging are routine and production is high risk (docs/components.md:696-700). Compare minimum_tier to the matched tier's risk_tier before returning admissible.

  • [P2] Reject malformed dormant-adapter evaluations — /private/var/folders/rm/bkx4f4b91vqfn79vk02pcd140000gn/T/tmp.krb2Z4mvml/adapter-tests/v1/fakes/deploy-dormant.sh:11-16
    When the fake adapter receives a malformed deploy_gate_evaluation whose top level has the four expected keys and whose body looks admissible, such as one with an extra .body.grant, this predicate still passes and emits a receipt. That contradicts the documented contract that only an admissible evaluation gets a refusal receipt and anything else returns nothing (docs/components.md:768-774); reuse the exact body/canonical checks from the evaluator output validation before accepting.

…pter

A request classified high could be admitted to a routine-gated tier because
the gate refused only violated or unknown classifications; the minimum tier
must now be no higher than the environment tier's own risk tier, and a
bootstrap classification is never admissible. The dormant deployment adapter
accepted an admissible-looking evaluation with extra body keys; it now checks
the evaluator's complete output shape. The program digest is re-pinned.

Proof: deploy-rollback-gates 89/89 (new cases: high floor admissible in
production but refused in staging, bootstrap refused, forged evaluation refused
by the dormant adapter), shellcheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yihanzhu

yihanzhu commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer (cross-vendor, read-only)

Reviewed-head: 6901e3c
Reviewed-base: 3cbd236
reviewer: gpt-5.5 @ high

Posted verbatim by codex-review.sh (codex exec review --json --base refs/codex-review/252-91980/base in an isolated temp worktree, sandbox forced read-only). Comments only — Codex never pushes, approves, or merges.

No discrete, actionable correctness issues were identified in the changed deploy/rollback gate implementation, tests, or documentation updates.

@yihanzhu
yihanzhu merged commit fe2f400 into main Sep 7, 2026
2 checks passed
@yihanzhu
yihanzhu deleted the ystack/roadmap-11/deploy-rollback-v1 branch September 7, 2026 03:19
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.

1 participant