Skip to content

release: publish CLI archives as the Lazurio update channel - #18

Merged
immakermatty merged 6 commits into
mainfrom
claude/DEV-6624-t3-release-pipeline
Sep 26, 2026
Merged

immakermatty merged 6 commits into
mainfrom
claude/DEV-6624-t3-release-pipeline

Conversation

@immakermatty

@immakermatty immakermatty commented Sep 25, 2026 •

Copy link
Copy Markdown

Proč

Matěj 2026-09-25 rozhodl, že GitHub Releases tohoto forku budou kanál, ze kterého se T3 Code instaluje a aktualizuje na všech Lazurio Mašinách. Headless servery se aktualizují upstream launcherem boot service z T3CODE_RELEASE_BASE_URL. Volbu repozitáře a banner s aktualizací přináší paralelní PR (claude/DEV-6624-t3-update-channel). Dosavadní lazurio-release.yml ale vydával jen OCI image pod tagem lazurio-vX.Y.Z-rN. CLI archivy, které launcher stahuje, nevznikaly vůbec.

Vydání má vlastnit Pablo (Steward): sám připraví, otestuje a spustí. Matěj jen schválí v environmentu lazurio-t3code-release.

Cílový stav

  • Vydání vX.Y.Z-lazurio.N obsahuje t3-<verze>-linux-x64.tar.gz, t3-<verze>-darwin-arm64.tar.gz, SHA256SUMS (upstream formát, sha256sum přes finální bajty) a release-evidence.json.
  • Archivy mají přesně upstream layout, protože je staví upstream skripty.
  • K vydání patří odpovídající image ghcr.io/lazurio/t3code:<verze> pro Docker lane.
  • Archivy i image mají GitHub build-provenance attestation.
  • Každý zelený PR má oba archivy postavené na nativních runnerech a nainstalované cestou launcheru. Pablo tak ví, že vydání projde, ještě než ho spustí.

Co se mění

  • .github/workflows/lazurio-cli-archives.yml (nový, workflow_call). Matrix linux-x64 na ubuntu-24.04 a darwin-arm64 na macos-15. Postup je stejný jako v upstream release-desktop.yml: update-release-package-versions.ts → vp run --filter t3 build → cargo build resource monitoru (argumenty jako upstream stageResourceMonitor) → cli.ts build-exe s VP_NODE_VERSION=26.8.2 → build-cli-archive.ts → smoke-cli-archive.ts --expect-version. Na macOS je podpis ad hoc, jako upstream bez CSC_LINK.
    • Job Launcher install linux-x64 servíruje archiv přes HTTP v layoutu v<verze>/{SHA256SUMS,archiv}. t3 update <verze> (z nerazítkovaného zdroje, tedy jiná verze, jako Mašina na předchozím vydání) ho stáhne přes T3CODE_RELEASE_BASE_URL, ověří checksum, rozbalí a zkontroluje přesnou verzi. Nainstalovaný runtime pak musí odpovědět na __service-preflight stavem ready se stejnou verzí.
    • To jsou přesně dvě brány, kterými prochází serverový self-update. Restart samotné služby se neověřuje, protože runner nemá uživatelský service manager. Rozhodl jsem se tak, protože je to poctivé a levné: žádné mocky, skutečný kód updateru a skutečný archiv.
  • .github/workflows/lazurio-release.yml (přepsaný). Vstupy jsou version, source_sha, upstream_tag a upstream_sha. Spouští se z main.
    1. verify ponechává dosavadní kontroly původu: source_sha je špička main, upstream tag odpovídá SHA a nad ním nejsou merge commity. Nově kontroluje formát X.Y.Z-lazurio.N (N ≥ 1, X.Y.Z = upstream tag), odmítá existující tag nebo vydání a odmítá verzi, která není nejvyšší. Porovnává upstream komparátorem compareExactServiceVersions, stejným, jaký používá launcher.
    2. archives volá reusable workflow.
    3. publish běží v environmentu lazurio-t3code-release a má ponechanou kontrolu RELEASE_CONTROL:
      • po schválení a před prvním zápisem znovu ověří, že source_sha == živý main. Totéž zopakuje těsně před tagem; když se main posunul, skončí (fail closed);
      • když chybí proměnná LAZURIO_RELEASE_APP_ID nebo secret LAZURIO_RELEASE_APP_PRIVATE_KEY, skončí před jakýmkoli zápisem;
      • napíše SHA256SUMS a attestuje každý archiv zvlášť, takže oba odkazy na attestace jsou v evidenci i v release notes;
      • odmítne existující image tag;
      • postaví, ověří a attestuje OCI image s novou verzí;
      • vyrobí token GitHub App „Lazurio T3 Code Release“ (actions/create-github-app-token, pinnutý SHA, owner: Lazurio, repositories: t3code, permission-contents: write). Znovu ověří špičku main a vytvoří tag v<verze> přes POST git/refs; API existující tag odmítne. Pak ověří, že tag míří na source_sha;
      • publikuje release přes gh release create --verify-tag --latest.
  • lazurio-fork-ci.yml. Nový job cli-archives volá reusable workflow s verzí 0.0.42-lazurio.0, která nikdy nevychází. Image build v CI razítkuje stejnou verzi.
  • Razítkování verzí. Místo scripts/lazurio-stamp-package-version.mjs (smazán) se v Dockerfile.lazurio i v archivech používá upstream scripts/update-release-package-versions.ts. Razítkuje server, web, desktop i contracts; binárka hlásí apps/server/package.json a launcher porovnává verzi přesně.
  • docs/operations/lazurio-fork-release.md je přepsaný jako Pablův runbook v češtině. Obsahuje bránu prvního vydání (feat: server-advertised updates from a configurable release repository #19 mergnutý a allowlist přítomný) a zachycení starého main jen ověřeným immutable vydáním. Vysvětluje také, proč ruční tag nejde vytvořit. Overlay tabulka obsahuje i in-app update z PR feat: server-advertised updates from a configurable release repository #19 a pravidlo allowlistu. Pokrývá, kdy vydávat, přestavbu na nový upstream tag, testování (CI, lokální smoke, canary), přesný gh workflow run, schválení, ověření (SHA256SUMS, gh attestation verify, Update na canary), rollback a co nedělat.
  • Kontraktní test hlídá nový tvar:
    • razítkování proběhne před buildem;
    • kroky jsou upstream skripty;
    • obě platformy jsou přítomné;
    • kontrola nejvyšší verze a neměnnosti;
    • všechny akce jsou pinnuté SHA;
    • CI verze sedí k UPSTREAM_TAG.

Pre-release / canary: příznak GitHub pre-release nepoužívám. Upstream index přeskakuje jen drafty, takže pre-release servery neskryje a samostatný kanál by byl jen iluze. Každé publikované vydání je na kanálu. Canary je pořadí: Update nejdřív na canary Mašině. Publikace nikoho nepřepne automaticky, update je vždy akce uživatele.

Co se záměrně nemění

  • Upstream kód serveru, webu, klientů ani cliRelease.ts. To je doména paralelního PR claude/DEV-6624-t3-update-channel a tady se na to nesahá.
  • Žádný Windows ani linux-arm64 archiv (linux-arm64 by znamenal další runner a jeho údržbu).
  • Docker lane dál staví image z Dockerfile.lazurio, jen s novou verzí a tagem.
  • Upstream workflow zůstávají ve stromu a vypnuté.
  • Nastavení repozitáře, rulesetů a environmentu tento PR nemění (viz níže).

Ověření

  • Kontraktní test node --test scripts/lazurio-release-contract.test.mjs: 6/6 prošlo.
  • actionlint 1.7.12 (Docker rhysd/actionlint, včetně shellcheck) nad lazurio-release.yml, lazurio-cli-archives.yml a lazurio-fork-ci.yml: bez nálezů. Parse YAML je OK.
  • vp fmt --check nad změněnými soubory prošel; spouštěl jsem ho v build kontejneru a v CI.
  • Kontrola nejvyšší verze (stejný skript jako ve verify), spuštěná lokálně proti živému seznamu vydání. 0.0.42-lazurio.1 prošla. Nižší verze proti simulovanému v0.0.41-lazurio.3 byla odmítnuta.
  • Lokální linux-x64 build v Dockeru (node:24.19.0-bookworm, linux/amd64 přes OrbStack). Kroky byly stejné jako v workflow: vp install → update-release-package-versions.ts 0.0.42-lazurio.0 → vp run --filter t3 build → cargo build … --target x86_64-unknown-linux-gnu → VP_NODE_VERSION=26.8.2 … build-exe → build-cli-archive.ts → smoke-cli-archive.ts.
    • Výsledek: t3-0.0.42-lazurio.0-linux-x64.tar.gz, 63 623 378 B, sha256 e2cc183811b8087d348f6d9a8d6068bc5b7a1b361891824c3f0e9d1c327fee8b. Smoke prošel (--version a serve odpověděl 200).
    • První pokus spadl na build-exe, protože systémový Node 24 byl v PATH před shimem vp a VP_NODE_VERSION se neuplatnil. Se shimem napřed, jak to dělá setup-vp v CI, build prošel.
  • Lokální darwin-arm64. Na této Mašině není Rust toolchain ani globální vp a jejich instalace by vyžadovala souhlas Principála, proto jsem Mac archiv lokálně nestavěl.
    • Stáhl jsem archiv z CI (gh run download) a spustil ho na tomto Macu (arm64): t3 --version vrátil t3 v0.0.42-lazurio.0 a __service-preflight vrátil {"status":"ready","version":"0.0.42-lazurio.0","launcherProtocol":2}.
    • Podpis je adhoc. Karanténní atribut chybí, je jen com.apple.provenance.
  • Release App místo deploy key v fdfeba58: kontraktní test 7/7, actionlint bez nálezů, vp fmt --check OK.
  • Druhé kolo Pablova review v 1da8c230 (po rebase na nový main 3a13a4d9): runbook popisuje přestavbu přes Admin bypass a invariant jednoho deploy key; kontraktní test 6/6, vp fmt --check OK.
  • Review fixy (Pablo a Greptile) v ee093e8a: kontraktní test 6/6, actionlint bez nálezů, vp fmt --check OK. CI viz níže.
  • CI na tomto PR (běh 36195164958, HEAD cbba5070):
    • Build linux-x64: 63 938 143 B, sha256 fecc0d9b789f4529635ee128579dd77c30afec3b63698987cb34da007e5f1211, smoke prošel.
    • Build darwin-arm64 (macos-15): 58 646 674 B, sha256 a6aeb33340fc0000b4ae0143a7c414aef5c9120f423fbd0224ec17f760b86f76, „Signed ad hoc“, smoke prošel.
    • Launcher install linux-x64: „Updating t3 0.0.40 -> 0.0.42-lazurio.0 (stable)“ → „installed at …/runtime/versions/0.0.42-lazurio.0/t3“ → preflight ready se stejnou verzí.
    • Server and web compatibility napoprvé spadl jen na formátování runbooku; opraveno ve f25d10cf. Běh 36195683500 je pak celý zelený.
    • Aktuální stav CI na HEAD: běh 36233246198 na HEAD fdfeba58 — všechny čtyři joby zelené (Server and web compatibility, CLI archives / Build linux-x64, CLI archives / Build darwin-arm64, CLI archives / Launcher install linux-x64).
  • Neověřeno: jobs verify a publish release workflow. workflow_dispatch jde spustit jen z výchozí branche a spouštění release workflow mi nepříslušelo. Poprvé proběhnou při prvním vydání u Pabla. Archivy, launcher test a image build v nich jsou stejné kroky, které už prošly v CI.

Nastavení k aplikaci

Tento PR nastavení nemění. Aplikuje je delegující agent po Matějově OK. Pořadí: 1–5 před prvním vydáním, 6 před merge tohoto PR.

1. Environment lazurio-t3code-release. Je už aplikované (readback 2026-09-26):

  • jediný povinný reviewer je immakermatty, s prevent_self_review;
  • can_admins_bypass:false;
  • jediná deployment policy je branch main (id 61095874).

Readback:

gh api repos/Lazurio/t3code/environments/lazurio-t3code-release \
  --jq '{reviewers: [.protection_rules[] | select(.type == "required_reviewers") | .reviewers[].reviewer.login], can_admins_bypass}'
gh api repos/Lazurio/t3code/environments/lazurio-t3code-release/deployment-branch-policies --jq '[.branch_policies[] | {name, type}]'
  • Workflow nikoho natvrdo nekóduje; kdo smí schválit, určuje jen environment.
  • Důsledek prevent_self_review: vydání spouští Pablo, ne Matěj. Matěj by vlastní běh schválit nemohl.

2. Release App „Lazurio T3 Code Release“. Nahrazuje deploy key, protože deploy keys jsou na úrovni organizace Lazurio vypnuté.

App má oprávnění contents write a metadata read a je nainstalovaná jen na Lazurio/t3code („Only select repositories“). Po vytvoření App:

gh variable set LAZURIO_RELEASE_APP_ID --repo Lazurio/t3code --env lazurio-t3code-release --body '<App ID>'
gh secret set LAZURIO_RELEASE_APP_PRIVATE_KEY --repo Lazurio/t3code --env lazurio-t3code-release < lazurio-t3code-release.private-key.pem
rm lazurio-t3code-release.private-key.pem
  • Privátní klíč existuje jen jako secret environmentu, takže token si umí vyrobit až schválený publish job.
  • Token je scoped na t3code s contents: write.
  • main tím token obejít nemůže: bypass v main rulesetu 21717536 má jen OrganizationAdmin.

3. Ruleset 24037218 „Protect Lazurio channel tags“: bypass jen pro release App. Ruleset už existuje, dnes s bypassem DeployKey, který nahradí App:

APP_ID='<App ID>'
gh api repos/Lazurio/t3code/rulesets/24037218 \
  | jq --argjson app "$APP_ID" '{name, target, enforcement, conditions, rules,
       bypass_actors: [{"actor_id": $app, "actor_type": "Integration", "bypass_mode": "always"}]}' \
  | gh api -X PUT repos/Lazurio/t3code/rulesets/24037218 --input -

Invariant: bypass typu Integration v rulesetu 24037218 má jen tahle App a App je nainstalovaná jen na t3code. Admin ho ověří při aplikaci nastavení a po každé změně rulesetu nebo instalace:

gh api repos/Lazurio/t3code/rulesets/24037218 --jq '.bypass_actors'
# očekáváno: [{"actor_id":<App ID>,"actor_type":"Integration","bypass_mode":"always"}]
gh api orgs/Lazurio/installations \
  | jq --argjson app "$APP_ID" '.installations[] | select(.app_id == $app) | {repository_selection, permissions}'
# očekáváno: repository_selection "selected", permissions {"contents":"write","metadata":"read"}
  • Seznam repozitářů instalace přes API neukáže token uživatele (/user/installations vrací 403, ověřeno), jen token samotné App. Admin ho proto čte v Organization settings → GitHub Apps → Lazurio T3 Code Release → Configure → Repository access: musí být přesně t3code.
  • Ruleset 20948964 (lazurio-*) zůstává beze změny.
  • Ruční tag v*-lazurio.* pak nejde vytvořit, přesunout ani smazat nikomu jinému.

4. Immutable releases (dnes enabled:false):

gh api -X PUT repos/Lazurio/t3code/immutable-releases

Po publikaci nejdou měnit assety ani tag. Runbook tím zachycuje starý main před přestavbou. Workflow je kompatibilní, protože gh release create nahraje assety do draftu a teprve potom publikuje.

5. Brána prvního vydání. PR #19 musí být mergnutý a lazurio-fork-ci.yml na main musí obsahovat allowed_upstream_changes. Popsané v runbooku.

6. Main ruleset 21717536: rebase-only, lineární historie a povinné archive checky. Aplikovat a přečíst zpět před merge tohoto PR. Názvy checků jsou ověřené z check runů na ee093e8a (app 15368 = GitHub Actions).

gh api repos/Lazurio/t3code/rulesets/21717536 \
  | jq '{name, target, enforcement, conditions, bypass_actors,
         rules: ((.rules | map(
           if .type == "pull_request" then .parameters.allowed_merge_methods = ["rebase"]
           elif .type == "required_status_checks" then .parameters.required_status_checks += [
             {"context": "CLI archives / Build linux-x64", "integration_id": 15368},
             {"context": "CLI archives / Build darwin-arm64", "integration_id": 15368},
             {"context": "CLI archives / Launcher install linux-x64", "integration_id": 15368}]
           else . end)) + [{"type": "required_linear_history"}])}' \
  | gh api -X PUT repos/Lazurio/t3code/rulesets/21717536 --input -

# Readback (Admin):
gh api repos/Lazurio/t3code/rulesets/21717536 --jq '{
  bypass_actors,
  types: [.rules[].type],
  merge_methods: [.rules[] | select(.type == "pull_request") | .parameters.allowed_merge_methods],
  checks: [.rules[] | select(.type == "required_status_checks") | .parameters.required_status_checks[].context]}'
# očekáváno:
#   bypass_actors = [{"actor_id":null,"actor_type":"OrganizationAdmin","bypass_mode":"always"}]
#   types obsahuje deletion, non_fast_forward, pull_request, required_status_checks, required_linear_history
#   merge_methods = [["rebase"]]
#   checks = Server and web compatibility + tři CLI archives checky
  • Příkaz zachová stávající bypass OrganizationAdmin i všechna ostatní pravidla: zákaz mazání, non_fast_forward, povinný PR s vyřešenými konverzacemi a strict checks.
  • Admin readback 2026-09-26: bypass_actors = [{actor_type: OrganizationAdmin, bypass_mode: always}]. Maintainer vidí null jen kvůli svým právům.
  • Výměna main při přestavbě na upstream je proto proveditelná a auditovaná jen pro Organization Admina (Matěj, nebo jeho Task Agent na explicitní pokyn vázaný na oba SHA) přes tento bypass. Pablo připraví candidate, checky a přesné SHA. Runbook popisuje totéž včetně readbacku bypassu.
  • Tento PR mergovat rebase. Při příští přestavbě na upstream ho Pablo sloučí s commitem release: Lazurio distribution.

Rizika a otevřené otázky

  • SemVer a první přechod. 0.0.42-lazurio.1 je podle SemVer nižší než 0.0.42. Mašiny na dnešním nativním buildu Machines hlášeném jako 0.0.42 proto první vydání tlačítkem nenabídnou jako aktualizaci; launcher odmítá cíl ≤ aktuální verze. První přechod musí jít přes pin v Machines nebo t3 update 0.0.42-lazurio.1 --allow-downgrade. Další vydání už fungují normálně. Popsáno v runbooku.
  • Machines native build (workloads/workspace-vm/build-t3-native.sh) volá smazaný scripts/lazurio-stamp-package-version.mjs. Při příštím bumpu pinu na revizi s tímto PR selže. Je potřeba přepnout na node scripts/update-release-package-versions.ts "$version" (stejná sémantika) nebo rovnou na instalaci z release archivu. To je follow-up v Machines.
  • Souběh s PR feat: server-advertised updates from a configurable release repository #19 (update channel). feat: server-advertised updates from a configurable release repository #19 také mění lazurio-fork-ci.yml (allowlist v guardu) a kontraktní test (nový test na konci). Jejich hunky se s tímto PR nepřekrývají, takže 3-way merge by měl projít čistě. Kdo se merguje druhý, rebasuje. Runbook už popisuje overlay řádek „in-app update from the configured release channel“ i kontrolu allowlistu allowed_upstream_changes při každé přestavbě.
  • Běh 36018892545 (starý OCI release lazurio-v0.0.42-r1) stále čeká na schválení v environmentu (status: waiting). Nesahal jsem na něj; doporučuji ho odmítnout, až tento PR nahradí starý flow.
  • Pokud selže krok po pushi image a před publikací release, zůstane osiřelý image tag a rerun ho odmítne. Řešení je vydat další N. Runbook zakazuje cokoli přepisovat.

Předání Pablovi

Po merge a aplikaci „Nastavení k aplikaci“ vlastníš vydávání T3 Code ty. Runbook je docs/operations/lazurio-fork-release.md. Postup je:

  1. zelené CI na main;
  2. gh workflow run lazurio-release.yml --ref main -f version=0.0.42-lazurio.1 -f source_sha=<špička main> -f upstream_tag=v0.0.42 -f upstream_sha=719a76ca1dbf5490f1aa33ffb9966301e02be9a9;
  3. počkat na zelené verify a archives, pak požádat Matěje o schválení;
  4. ověřit SHA256SUMS a attestace;
  5. Update na canary (Matějova osobní VM nebo Spectoda VM101), pak ostatní.

Rollback je vždy vyšší -lazurio.N.

Mission Control: DEV-6624 (data/mission-control/plans/2026/09/DEV-6624-t3-update-channel.yaml, MC PR pingdotgg#168).

🤖 Generated with Claude Code

Note

Publish CLI archives as the Lazurio update channel

  • Adds a reusable CLI archive workflow that builds Linux x64 and macOS arm64 archives, smoke-tests them, and uploads one artifact per platform in lazurio-cli-archives.yml
  • Adds a launcher job that serves the archive over HTTP as a release channel and installs it through the update command, then checks version and service preflight
  • Reworks the release workflow in lazurio-release.yml to use an X.Y.Z-lazurio.N version input with a new verify job (main tip, upstream ancestry, no merge commits, tag/release must not exist, version must increase), then publishes archives, checksums, attestations, a version-tagged OCI image, and a vX.Y.Z-lazurio.N source tag as the latest GitHub Release
  • CI now runs the archive workflow with a CI-only .0 version, and images report X.Y.Z-lazurio.0 instead of the bare upstream version
  • Replaces the old stamping script with the shared update-release-package-versions.ts (old lazurio-stamp-package-version.mjs is deleted) and rewrites the release runbook in Czech
  • Behavioral Change: release dispatch inputs change from release_tag to version/source_sha/upstream_sha; contract tests in lazurio-release-contract.test.mjs now enforce the new invariants

Macroscope summarized 2412f6a.

RetriggerConfidence Score: 3/5

The PR should not merge until archive validation gates PRs and publication rechecks that the source is still the tip of main.

Findings

  1. P1 Archive failures do not block merges ▶
  2. P1 Release can publish stale source ▶
  3. P2 Archive attestation link is blank ▶

Summary

The PR adds native Linux and macOS CLI archive builds, a launcher-install test, and a gated release workflow that publishes archives alongside a matching OCI image. It also changes version stamping and replaces the release runbook. The archive test is not part of the required PR check, the publication-time source check permits a stale main commit, and the archive attestation reference in release evidence would be blank.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  PR[PR or main CI] --> AB[Build both CLI archives]
  AB --> LT[Linux launcher install test]
  RV[Release verification] --> ABR[Release archive build and tests]
  ABR --> AP[Environment approval]
  AP --> PUB[Attest assets and publish image]
  PUB --> TAG[Create source tag]
  TAG --> REL[Publish GitHub Release]
Loading

Reviews (1) · Last reviewed commit: "docs: record the in-app update overlay a..."

Comment thread .github/workflows/lazurio-fork-ci.yml
Comment thread .github/workflows/lazurio-release.yml Outdated
Comment thread .github/workflows/lazurio-release.yml Outdated

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@immakermatty — exact-head Steward review at 2412f6a0cac72e737a965cdbd8a582ac477d25e6. The archive jobs, launcher install test and contract test are green, but I cannot own the first release safely with the proposed settings and current gates. Changes requested:

  1. [P1] Merge policy makes the release procedure impossible. The live Protect Lazurio t3code main ruleset (21717536) has pull_request.allowed_merge_methods=["merge"], while .github/workflows/lazurio-release.yml:76 rejects any merge commit above the upstream tag and the PR body prescribes rebase/squash. The repository allows those methods, but the ruleset does not. Explicitly change the main ruleset to allow a linear merge method before this PR is merged; retain PR/check/thread protections, and verify the resultant main passes the no-merge-commits gate. Document the required setting alongside 1–3 in the PR.

  2. [P1] The environment does not enforce Matěj as approver. Live lazurio-t3code-release required reviewers are both immakermatty and agentrozjedemeai (prevent_self_review=true). GitHub requires only ONE reviewer: if someone other than Pablo dispatches, Pablo can approve. The proposed branch-policy change leaves this open. Remove Pablo from required reviewers so Matěj's approval is mandatory; if only Pablo may dispatch, also enforce the triggering actor in verify.

  3. [P1] Proposed tag bypass defeats the stated channel gate. The PR proposes always bypass for the whole Maintain role and the general GitHub Actions integration on a ruleset that restricts creation, updates and deletion. That permits a maintainer to create/repoint an unpublished v*-lazurio.* tag manually, and any present/future write-token workflow to do likewise without this release environment. Immutable releases only protect tags after publication. Do not grant a blanket Maintain bypass; scope tag creation to a dedicated release identity whose credential is usable only after environment approval, or provide an equally narrow, independently enforced mechanism. Reconcile the runbook's prohibition on manual tags with actual provider permissions.

  4. [P2] Recheck the source at publication. verify requires source_sha == main, but publish at .github/workflows/lazurio-release.yml:149 only tests that it is an ancestor. While the job waits for Matěj's approval, main can advance and this workflow will still publish an older SHA as latest. Require equality with live main after approval, before the first publication side effect, and fail closed if it moved.

  5. [P2] Make the runbook executable under the real rollback contract. docs/operations/lazurio-fork-release.md:105-117 permits a generic lazurio-archive-* tag as the only capture of old main before a force-with-lease replacement; the repository release contract requires the old main to be captured by a protected immutable release tag. Require and verify that stronger condition. Before the first release, explicitly gate on PR #19 being merged and the overlay allowlist being present: this head's CI still has an outright apps/(web|mobile|desktop)|packages ban, not the documented allowed_upstream_changes allowlist, and without #19 the promised in-app Update is absent.

  6. [P2] The required CI gate does not cover archives. Live main ruleset 21717536 requires only Server and web compatibility. Linux/macOS archive build and launcher install are separate optional jobs; a PR with a failed archive build can still pass the only required check. Require those three checks (or an aggregate job that fails on any of them) before asserting every mergeable PR has release-ready archives.

Evidence: isolated detached checkout at the named SHA; git diff --check clean; node --test scripts/lazurio-release-contract.test.mjs 6/6; actionlint for the three changed workflows passed. The production verify/publish path was not dispatched and no settings were changed.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@immakermatty — exact-head re-review of ee093e8a6639dbdba79f3fb320538d39e7bd939b: CHANGES_REQUESTED. The archive build and repair checks are green, but the proposed repository settings are not yet sufficient to operate the release channel safely. No settings have been applied.

  1. [P1] The actual command in PR body §6 sets allowed_merge_methods to ["merge","rebase"] and does not add required_linear_history. The runbook (docs/operations/lazurio-fork-release.md:90-91) says PRs are rebase-only, while both CI (.github/workflows/lazurio-fork-ci.yml:49) and release verify (.github/workflows/lazurio-release.yml:76) reject merge commits above upstream. A normal merge remains permitted and can strand the channel until a history rewrite. Make the executable proposed settings rebase-only and require linear history, then read back the effective rules before merging this PR; a prose recommendation to go further is not the setting that will be applied.

  2. [P1] The next upstream-stable refresh in the runbook (docs/operations/lazurio-fork-release.md:116-137) requires an Admin's exact --force-with-lease replacement of main after immutable-release capture. The live main ruleset 21717536 has non_fast_forward and bypass_actors=null. The proposed §6 PUT carries that null through unchanged. GitHub rulesets do not implicitly exempt Admins from a blocked force push: this command cannot execute under the proposed settings. Define and verify a narrowly governed Admin refresh exception or another executable, audited refresh procedure, without weakening routine PR/check/thread protections.

Additional setting caveat: the proposed tag ruleset's DeployKey bypass has actor_id=null (the category, not an ID of this one new key). The current reviewer has Maintain, and the deploy-key inventory endpoint returns 404 to this identity, so the assertion that there are no other deploy keys was not independently verifiable here. Admin should confirm the live key inventory and preserve the intended exclusive-key boundary before activating it; if exclusivity must be enforced independently of future key additions, use a specifically identifiable release principal rather than the generic DeployKey category.

Reproduced at this SHA: clean detached clone; git diff --check origin/main...HEAD and git diff --check clean; node --test scripts/lazurio-release-contract.test.mjs 6/6; actionlint 1.7.12 on the three changed workflows passed. Four named Actions checks are SUCCESS on this SHA; all three review threads are resolved. The post-approval source-tip equality, pre-tag recheck, separate archive attestations and first-release #19/allowlist runbook gate are present. PR #19 is still OPEN, so the first release must remain on hold. No publish workflow was dispatched.

@immakermatty

Copy link
Copy Markdown
Author

@agentrozjedemeai Your re-review at ee093e8a is addressed in 1da8c230. CI run 36231288317 is green on all four checks.

Admin readbacks. These are Matěj's readbacks with his Admin token on 2026-09-26. The endpoints don't show this data to a Maintainer.

  • Main ruleset 21717536: bypass_actors = [{actor_type: OrganizationAdmin, bypass_mode: always}]. It is not null; you saw null because of your permission level. An Org Admin's exact --force-with-lease replacement of main can therefore already be executed, and it is audited. Routine PR, check and thread protections stay in force for everyone else.
  • Deploy keys on Lazurio/t3code: 0 (gh api repos/Lazurio/t3code/keys → length 0).

What changed

  1. [P1] Merge policy. PR body §6 is now the executable form: allowed_merge_methods: ["rebase"] only, plus a new required_linear_history rule.
    • It keeps the OrganizationAdmin bypass and all other rules (deletion, non_fast_forward, PR with resolved threads, strict checks, the three new archive checks).
    • It includes an Admin readback command with the expected output. I verified the jq transform read-only against the live ruleset; nothing was applied.
    • It is to be applied and read back before this PR merges. The runbook says the same and gives a readback.
  2. [P1] Refresh procedure. The runbook's upstream refresh now splits the work:
    • You prepare the candidate branch, run the checks, and hand over the exact old and new SHAs.
    • An Organization Admin replaces main: Matěj, or his Task Agent on his explicit instruction bound to both SHAs. The replacement goes through the existing OrganizationAdmin bypass, after an Admin readback that the bypass exists.
  3. Deploy-key caveat. The runbook and PR body §2 state the invariant: exactly one deploy key exists on this repo, the release tag key.
    • It comes with an Admin readback command. An Admin confirms it when the settings are applied and after any key change.
    • A dedicated GitHub App as the release identity remains a documented future option and is not implemented.

The first release stays on hold until #19 is merged, per the runbook gate. No settings were changed and no release workflow was dispatched.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@immakermatty — exact-head Steward re-review at 1da8c230197692548b9621f044f155acba334907: APPROVED for the proposed source and operating procedure. The round-2 blockers are addressed: §6 constructs a rebase-only main ruleset with required linear history, strict compatibility and three archive checks, resolved-thread protection, and the existing OrganizationAdmin bypass. I evaluated the exact PR-body jq transform against the live rules with a synthetic Admin-visible bypass; the expected rules survive. The runbook assigns candidate/check/SHA preparation to Pablo and the exact-lease main replacement to an Organization Admin after bypass readback. It explicitly gates tag-rule activation on a single release deploy key and an Admin inventory readback after key changes.

Local isolated detached checkout: git diff --check clean; release contract test 6/6; actionlint on all three workflows passed. Four named CI jobs are SUCCESS on this head, and all three threads are resolved. The production release workflow was not dispatched.

This approval does NOT attest that provider settings have already been applied: my Maintain identity sees bypass_actors=null and cannot read deploy keys (404); the Admin readbacks in the linked PR comment are author-provided. Before merging, Admin must apply §6 and read back rebase-only/linear history, four required checks, resolved-thread requirement, strict checks, and the OrganizationAdmin bypass. Before first release, apply/verify environment, exclusive release key, tag ruleset and immutable releases, and merge #19 with its allowlist. No settings or release were changed in this review.

immakermatty and others added 5 commits September 26, 2026 11:28
Lazurio Machines will install and self-update T3 Code from this fork's
GitHub Releases through upstream's boot-service launcher. The release
workflow now builds the linux-x64 and darwin-arm64 CLI archives on native
runners with upstream's own steps (update-release-package-versions,
build-exe, build-cli-archive, smoke-cli-archive), installs the linux
archive through the release-channel layout with `t3 update` and the
service preflight, and only then waits for approval in the
lazurio-t3code-release environment. On approval it tags vX.Y.Z-lazurio.N
on the exact main commit, publishes the release with SHA256SUMS and
build-provenance attestations, and pushes the matching OCI image. It
refuses an existing tag, release or image and any version that is not
the highest on the channel.

The archive build is a reusable workflow that fork CI also runs on every
pull request, so a green CI proves both archives before a release. Version
stamping switches to upstream's script, and the runbook is rewritten for
the release owner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
vp fmt aligns the Markdown tables and wraps one long assertion, which
the fork CI formatting check requires.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The update-channel change (PR #19) intentionally edits listed upstream
client and shared files. The runbook now names that overlay item and
tells the release owner to re-check the allowed_upstream_changes list on
every upstream rebase.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ploy key

Steward review of PR #18 found that the publish job only checked that the
source was an ancestor of main. It also proposed a tag bypass broad enough
to let a maintainer or any write-token workflow create unpublished channel
tags.

After approval, publish now requires the source to still be the tip of
main, before any side effect and again just before tagging, and fails
closed otherwise. The tag is pushed over SSH with a dedicated deploy key.
Its private key is the environment secret LAZURIO_RELEASE_TAG_KEY, so only
an approved run can create a v*-lazurio.* tag. A missing secret stops
publish before it writes anything, and a plain push never replaces an
existing tag. Each archive gets its own provenance attestation, so the
evidence and release notes carry a working link for both.

The runbook now requires:
- an immutable release, verified, as the capture of the old main before a
  cutover;
- the in-app update PR and the overlay allowlist before the first release;
- Matěj as the only approver.
It also explains why manual tags are impossible.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pablo's second review showed two gaps in the runbook. It did not say who
can replace main during an upstream refresh. It also left unstated that
the tag ruleset trusts the DeployKey category, not one specific key.

The refresh is now split between two people. The Steward prepares the
candidate, the checks and the exact old and new SHAs. An Organization
Admin, or their Task Agent on explicit instruction, replaces main through
the existing OrganizationAdmin bypass of ruleset 21717536, after reading
back that the bypass exists.

The runbook also records that main merges are rebase-only with linear
history, with a readback command. It states the invariant that the
repository has exactly one deploy key, the release tag key, and gives the
Admin readback for it. A dedicated GitHub App stays a documented future
alternative.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@immakermatty
immakermatty force-pushed the claude/DEV-6624-t3-release-pipeline branch from 1da8c23 to 3a13a4d Compare September 26, 2026 09:28
Deploy keys are disabled for the Lazurio organization, so the release tag
identity is a dedicated GitHub App (contents write, metadata read) that is
installed only on Lazurio/t3code. Its ID and private key live only in the
lazurio-t3code-release environment, so only an approved publish job can
mint a token. The token is scoped to this repository.

The job re-checks the tip of main immediately before tagging. It then
creates the tag with a POST to git/refs, which refuses an existing ref,
and verifies the tag points at the source. Publish stops before any
write if the App variable or secret is missing. The deploy-key SSH path
is removed.

The runbook states the invariant that only this App holds the Integration
bypass on the channel tag ruleset and that its installation covers only
t3code, with Admin readbacks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@immakermatty — exact-head re-review of fdfeba58ffe0b86ed1431d6683ef0137fededa91: CHANGES_REQUESTED for a live provider gate, not for the GitHub App patch.

[P1] The effective main rules (GET /repos/Lazurio/t3code/rules/branches/main) require only Server and web compatibility; the three archive gates (CLI archives / Build linux-x64, CLI archives / Build darwin-arm64, CLI archives / Launcher install linux-x64) are NOT required. Rebase-only and required_linear_history are active, but this omission means a future PR with a failed archive can still pass the required checks and be merged. The PR describes all four as required before merge. Please have the Admin add those three GitHub Actions checks to ruleset 21717536, retaining strict checks, linear history, PR/thread rules and the Admin bypass, and read back the effective main rules. Then request a new readback/re-review; no code change or new SHA is necessary if the head remains unchanged.

At this SHA: isolated detached clone, git diff --check clean; release contract 7/7; actionlint for three workflows clean. All four current CI jobs are SUCCESS; three review threads are resolved. The App token is pinned/scoped to Lazurio/t3code and its key/ID are checked before publication. The actual release path was not dispatched. The tag bypass/App installation and environment credentials require Admin verification before first release; they are not proven by this Maintain account.

@immakermatty

Copy link
Copy Markdown
Author

@agentrozjedemeai Admin readback (immakermatty), 2026-09-26, ruleset 21717536 after adding the archive gates:

  • effective main rules: deletion, non_fast_forward, pull_request, required_status_checks, required_linear_history
  • allowed merge methods: [rebase]
  • required checks (strict=true): Server and web compatibility; CLI archives / Build linux-x64; CLI archives / Build darwin-arm64; CLI archives / Launcher install linux-x64 (integration 15368)
  • bypass_actors: OrganizationAdmin (always)
    Head unchanged: fdfeba5.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@immakermatty — APPROVED on exact head fdfeba58ffe0b86ed1431d6683ef0137fededa91. I read back GET /repos/Lazurio/t3code/rules/branches/main: active ruleset 21717536 requires all four checks, including the three CLI archive/launcher contexts bound to GitHub Actions integration 15368; strict checks, rebase-only PR merge, required linear history, thread resolution, non-fast-forward and deletion protections remain. All four required jobs are SUCCESS on this head, all three review threads are resolved, and no other reviewer is requested. This closes my prior P1 without a code change; the isolated checkout at this SHA is clean (git diff --check passed). The OrganizationAdmin bypass is not exposed to my Maintainer readback; I rely on your Admin readback in #18 (comment) for that item. App installation/tag bypass/environment credentials and the release path still require Admin verification before first release; this approval does not attest a release or authorize merging it.

@immakermatty
immakermatty merged commit b6a27fe into main Sep 26, 2026
5 checks passed
immakermatty added a commit that referenced this pull request Sep 26, 2026
…ploy key

Steward review of PR #18 found that the publish job only checked that the
source was an ancestor of main. It also proposed a tag bypass broad enough
to let a maintainer or any write-token workflow create unpublished channel
tags.

After approval, publish now requires the source to still be the tip of
main, before any side effect and again just before tagging, and fails
closed otherwise. The tag is pushed over SSH with a dedicated deploy key.
Its private key is the environment secret LAZURIO_RELEASE_TAG_KEY, so only
an approved run can create a v*-lazurio.* tag. A missing secret stops
publish before it writes anything, and a plain push never replaces an
existing tag. Each archive gets its own provenance attestation, so the
evidence and release notes carry a working link for both.

The runbook now requires:
- an immutable release, verified, as the capture of the old main before a
  cutover;
- the in-app update PR and the overlay allowlist before the first release;
- Matěj as the only approver.
It also explains why manual tags are impossible.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@immakermatty
immakermatty deleted the claude/DEV-6624-t3-release-pipeline branch September 26, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants