ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ci(fork): run Fork CI for fork/dev PRs and merges - #343

Merged
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci
Aug 6, 2026
Merged

ci(fork): run Fork CI for fork/dev PRs and merges#343
patroza merged 1 commit into
fork/devfrom
fork-dev/enable-fork-ci

Conversation

@omegent-app

@omegent-appomegent-appBot commented Aug 6, 2026

Copy link
Copy Markdown

First PR against the new fork/dev branch. Unblocks step 2 of
#342.

Why

fork/dev had no CI path at all. fork-ci.yml listed only the rebased stack layers and the
registered overlays as pull_request bases, and had no push: trigger anywhere. That blocks both
halves of the cutover:

  • PRs into fork/dev could never satisfy a required check.
  • No run would ever exist for a merge SHA, so the deploy poller — which keys on a successful
    fork-ci.yml run for the exact SHA — would wait forever.

What changed

  • fork/dev added as a pull_request base.
  • New push: trigger for fork/dev. fork/dev is never rebased, so its merge commits are the
    release candidates
    ; a green PR tip is not enough when the merge SHA differs.
  • Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded
    would leave that merge SHA permanently unapprovable for deployment — a state the poller cannot
    recover from on its own.

The quiet one: mobile releases

deployment_scope and dispatch_mobile_releases were gated on
workflow_dispatch && refs/heads/fork/integration. After the cutover mobile releases would have
stopped silently
— nothing errors, EAS simply never gets dispatched again. Both now also accept a
fork/dev push.

Two follow-on corrections that gate implies:

  • The previous-successful-CI lookup feeding classification hardcoded fork/integration +
    workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and
    classify every component as changed. It now follows the running branch and event.
  • The mobile dispatch targeted a fixed --ref fork/integration. It now targets the branch being
    validated, so the mobile workflows come from the same tip that passed.

Both fork/integration paths are retained unchanged — the two tips coexist until fork/integration
is retired.

Validation

  • Workflow YAML parses; triggers, job conditions, and the new env wiring verified programmatically.
  • vp fmt --check clean.
  • Fork CI dispatched at this head via workflow_dispatch + checkout_ref (the same pattern the
    stack uses for tips that do not carry the workflow):
    run 31073762042success.
    CheckTestMobile Native Static AnalysisRelease Smoke ✅.
    Classify Deployment Scope and Dispatch Mobile Releases correctly skipped, confirming the
    widened if conditions do not fire on a non-release tip.
  • The fork/dev push path is still unexercised. It cannot be until this merges and something
    lands on fork/dev; the first merge is the real proof. Worth watching that it produces a run and
    that Classify Deployment Scope picks a sane previous SHA.

Merge order

This must merge before required status checks are configured on fork/dev. Requiring a check
that cannot yet run would block the very PR that makes it runnable.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack
layers and the registered overlays as pull_request bases, and had no push
trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev
could not satisfy a required check, and no run would ever exist for a merge
SHA.
Add fork/dev as a pull_request base, and add a push trigger for it. The push
run matters because fork/dev is never rebased, so its merge commits are the
release candidates: deployment promotes an exact green SHA and keys on a
successful run of this workflow for that SHA. A green PR tip is not enough when
the merge SHA differs.
Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one
as superseded would leave that merge SHA permanently unapprovable for
deployment, which is not a state the poller can recover from on its own.
Extend the release-tip jobs the same way. deployment_scope and
dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so
after the cutover mobile releases would have stopped silently — nothing errors,
EAS simply never gets dispatched again. Both now accept a fork/dev push as well.
The previous-successful-CI lookup that feeds classification hardcoded
fork/integration + workflow_dispatch. Left alone it would diff every fork/dev
push against an unrelated tip and classify every component as changed, so it
now follows the running branch and event. The mobile dispatch likewise targets
the branch being validated instead of a fixed fork/integration ref, so the
mobile workflows come from the same tip that passed.
Both fork/integration paths are retained unchanged; the two tips coexist until
fork/integration is retired.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-appBot marked this pull request as ready for review August 6, 2026 05:24
@patroza
patroza merged commit e4bd4b1 into fork/devAug 6, 2026
8 checks passed
patroza added a commit that referenced this pull request Aug 6, 2026
Found by the first real `fork/dev` merge.
[#343](#343) merged,
the push run went green, `Dispatch Mobile Releases` fired — and then EAS
production
[failed](https://github.com/patroza/t3code/actions/runs/31074271669) at
**Resolve approved
integration source**.
## Cause
Both mobile workflows check out a hardcoded ref and then assert
containment:
```yaml
ref: fork/integration # <- hardcoded
...
git merge-base --is-ancestor "$target_sha" "$integration_sha"
```
A `fork/dev` SHA is not contained by `fork/integration`, so the
assertion rejected it. Same class of
hardcoding #343 fixed on the dispatch side — just one workflow further
along, and only observable
once a real `fork/dev` merge dispatched a release.
## Change
- `release_branch` input on both mobile workflows, **defaulting to
`fork/integration`** so any manual
dispatch that omits it behaves exactly as before.
- Checkout uses `${{ inputs.release_branch }}`; the "overlay deploy
tooling" condition compares
against it instead of the literal.
- `fork-ci.yml` passes `release_branch` alongside the `--ref` it already
passed.
Passing one without the other is the trap worth naming: the workflow
file would come from `fork/dev`
while the product checkout stayed on `fork/integration` — precisely the
failure above.
## Validation
- All three workflow files parse; `release_branch` default confirmed as
`fork/integration`.
- `vp fmt --check` clean across `.github/workflows/`.
- **The EAS path itself is not re-run by this PR.** Proof is the next
`fork/dev` merge that
classifies `mobile=true` — this PR's own merge should do it, since it
touches
`.github/workflows/**`. Worth watching that run rather than assuming.
## Note on the first dispatch
That run classified every component as changed because no previous
successful `fork/dev` push run
existed to diff against — the documented conservative fallback.
Subsequent merges diff against the
prior green `fork/dev` SHA and should scope normally.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto 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

@patroza