From 3e37c98381f239518c6e0730a1fed9583e4295af Mon Sep 17 00:00:00 2001 From: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 06:32:50 +0200 Subject: [PATCH 1/9] docs: propose stable development and release branch --- docs/stable-dev-release-branch-handover.md | 549 +++++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 docs/stable-dev-release-branch-handover.md diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md new file mode 100644 index 00000000000..bd807160201 --- /dev/null +++ b/docs/stable-dev-release-branch-handover.md @@ -0,0 +1,549 @@ +# Stable Development and Release Branch Handover + +## Status + +Design proposal. This document describes a migration away from using the continuously rebased fork +stack as the daily contributor and release path. + +The intended outcome is: + +- Contributors work against a stable branch whose history is never rewritten. +- A green merge can be released immediately or according to a configurable cadence. +- The useful upstream, base, Tim, and candidate provenance layers remain clean and rebased. +- Routine work does not require contributors to label desktop, mobile, server, or other affected + clients manually. +- Clean downstream history remains available as generated output rather than an authoring surface. + +## Current Problem + +The current runnable branch is produced through a fully rewritten stack: + +```text +upstream/main + -> fork/base + -> fork/tim + -> fork/candidates + -> fork/changes + -> identity/Discord/VS Code/desktop overlays + -> fork/integration +``` + +This gives the fork good provenance, but it couples ordinary development and releases to expensive +history maintenance: + +- Rebases change commit identities and invalidate contributor bases. +- Every upstream update can require the entire stack and all overlays to be rewritten. +- Small bot, server, desktop, or mobile fixes cannot ship until unrelated layers are green again. +- Conflicts in an early provenance layer block downstream releases. +- Permanent overlays add composition and synchronization work to otherwise ordinary features. + +Git cannot provide stable commit identities and continuously rebased history on the same branch. +The solution is to give stable development and clean projection different branches and different +responsibilities. + +## Proposed Topology + +Retain a clean provenance stack through `fork/candidates`, then feed its changes into a stable, +complete development branch: + +```text +upstream/main + -> fork/base + -> fork/tim + -> fork/candidates periodically rebased provenance stack + | + | reviewed tree-delta synchronization + v + fork/dev stable complete product + ^ | + | +-> CI -> immediate or scheduled releases + | + +-- ordinary downstream PRs + +-- identity work + +-- Discord work + +-- VS Code work + +-- web/mobile/desktop/server/bot work + +fork/dev checkpoint + -> fork/changes-clean + -> fork/integration-clean periodic generated projection, never a PR base +``` + +## Branch Responsibilities + +| Branch | Rewritten | Contributor target | Release source | Purpose | +| ------------------------ | ------------------- | ------------------ | -------------- | ------------------------------------------- | +| `main` | Yes, mirror-managed | No | No | Exact upstream mirror | +| `fork/base` | Yes | No | No | Fork repository and CI infrastructure | +| `fork/tim` | Yes | No | No | Selected Tim imports with provenance | +| `fork/candidates` | Yes | No | No | Selected unmerged upstream candidates | +| `fork/dev` | Never | Yes | Yes | Canonical complete downstream product | +| `fork/changes-clean` | Yes | No | Optional | Curated downstream projection | +| `fork/integration-clean` | Yes | No | Optional/audit | Clean composed output matching a checkpoint | + +The clean branch names are placeholders. Existing names may be retained if their generated nature +is made unmistakable and tooling prevents contributors from targeting them. + +## Core Invariants + +### Stable development + +- `fork/dev` is never rebased or force-pushed after cutover. +- Ordinary contributor PRs target `fork/dev`. +- Contributor branches never depend on generated projection branches. +- The complete application can be built, tested, and run directly from `fork/dev`. +- A clean-stack rebuild is not required for a routine release. + +### Exact releases + +- Every release refers to an exact, green `fork/dev` SHA. +- Deployment receives the same SHA that passed the required checks. +- Bot, server, desktop, and mobile release status can be recorded independently. +- Release timing is policy, not branch topology: it may be immediate, debounced, scheduled, or + manually promoted. + +### Clean provenance + +- `main`, `fork/base`, `fork/tim`, and `fork/candidates` retain their current provenance roles. +- Generated clean branches are output only and may be rewritten safely. +- Generated branches are never merged back into `fork/dev`. +- For a selected checkpoint, the final clean projection must reproduce the checkpoint's product + tree, apart from narrowly documented generated metadata. + +```text +tree(fork/integration-clean) == tree(tagged fork/dev checkpoint) +``` + +## Synchronizing the Rebased Stack into `fork/dev` + +Rewritten provenance branches must not be repeatedly merged into `fork/dev`. After a rebase, their +commits have new identities; merging the rewritten tip would duplicate history and produce avoidable +conflicts. + +Instead, synchronize the net tree change. + +Assume: + +- `C1` is the `fork/candidates` tree currently incorporated into `fork/dev`. +- `C2` is the latest rebuilt and verified `fork/candidates` tree. + +The synchronization process should: + +1. Create a sync branch from `fork/dev`. +2. Calculate the tree delta from `C1` to `C2`. +3. Apply that delta to the sync branch. +4. Resolve integration conflicts against the current `fork/dev` product tree. +5. Run the full required checks. +6. Open a normal PR into `fork/dev`. +7. Merge it without rewriting `fork/dev`. +8. Record `C2` as the newly imported provenance checkpoint. + +The PR should be recognizable without requiring daily contributor metadata, for example: + +```text +sync(provenance): import upstream stack C1..C2 +``` + +The imported checkpoint may be recorded in an immutable tag or a small machine-owned state file: + +```json +{ + "importedCandidatesCommit": "", + "importedCandidatesTree": "", + "importedUpstreamCommit": "" +} +``` + +This is stack synchronization state, not a manual product ledger. + +## Daily Contributor Workflow + +The normal path becomes: + +1. Create a feature branch from `fork/dev`. +2. Open a PR against `fork/dev`. +3. Run the mandatory local validation. +4. Run GitHub CI for the exact PR and merge tip. +5. Merge using the selected stable-history policy. +6. Release immediately or include the merge in the next release cadence. + +No restack or overlay composition is required for an ordinary feature or fix. + +Squash merges are a reasonable default because they give each GitHub PR one stable commit on +`fork/dev`. Merge commits can remain available where preserving a dependent series is valuable. + +## Identity, Discord, and VS Code + +Identity, Discord, and VS Code remain meaningful ownership areas, but they do not necessarily need +permanent composition overlays. + +### Preferred model + +Their feature PRs target `fork/dev` directly. Ownership and validation are inferred from paths and +dependency impact: + +- Discord's separate application/package directories naturally select Discord checks and owners. +- VS Code's separate extension directories naturally select VS Code checks and owners. +- Identity-owned paths and shared integration points select identity checks and owners. +- Shared package changes expand validation to affected consumers. + +Use path-based workflow filters and `CODEOWNERS` instead of requiring contributors to apply client +labels. + +### Optional subsystem staging branches + +If one of these areas genuinely needs independent staging, it may use a stable branch such as: + +```text +fork/discord-dev +fork/vscode-dev +fork/identity-dev +``` + +These branches must also never be rebased. Work is merged into `fork/dev`, and `fork/dev` is merged +back afterward so the subsystem branch stays current. + +This adds merge topology and administration, so it should only be introduced where separate staging +provides a concrete benefit. Clear folders alone are not sufficient justification. + +## Ordinary Cross-Cutting Features + +Features such as the desktop URL-handler enhancement should be ordinary commits or squash-merged +PRs on `fork/dev`, not permanent layers. + +For example: + +```text +feat(desktop): support remote URL handling +``` + +The changed files determine validation and release scope. A dedicated layer is justified only when +work has independent external provenance or must remain independently staged—not simply because it +is identifiable as a feature. + +## Inferring Validation and Release Scope + +Daily contributors should not manually classify their work as web, mobile, desktop, server, or bot. +Automation should derive affected surfaces from the diff and the workspace dependency graph. + +Illustrative path rules: + +```text +apps/mobile/** -> mobile checks and release scope +apps/desktop/** -> desktop checks and release scope +apps/server/** -> server and bot checks/release scope +apps/web/** -> web checks +apps/discord/** -> Discord checks +apps/vscode/** -> VS Code checks +packages/contracts/** -> all relevant producers and consumers +packages/client-runtime/** -> web and mobile +shared build/config paths -> conservative full validation +``` + +Path rules should be generated from workspace ownership/dependency data where practical. Unknown or +ambiguous shared paths should fail safely by selecting broader checks, not by requiring labels. + +Labels remain appropriate only for exceptional intent that cannot be inferred from code: + +- Explicitly excluding a change from a clean projection. +- Associating a repair with an earlier feature for history folding. +- Recording an unusual external import. +- Requesting a special release behavior. + +## GitHub PRs as the Development Ledger + +GitHub PR history is the primary daily ledger. Do not create a second manifest containing every +normal contributor PR. + +The projection process can use: + +- Merge order and timestamps. +- Stable squash or merge commits on `fork/dev`. +- Changed paths. +- PR relationships and referenced issues. +- External provenance recorded on import PRs. +- Checkpoint tags defining the projection interval. + +Explicit metadata is required only when the work cannot speak for itself. + +### External imports + +Tim and candidate import PRs should record immutable source information: + +```text +Source-Repository: +Source-PR: +Source-SHA: +Fork-Layer: tim|candidate +``` + +### Folding later repairs + +A repair that should be folded into an earlier clean-history feature may record: + +```text +Projection-Fold-Into: +``` + +This is exceptional projection metadata, not a label required on normal work. + +### Minimal manifest + +A small manifest remains useful for machine policy that GitHub cannot reliably infer: + +- Layer ordering. +- External import sources. +- Persistent conflict-resolution rules. +- Explicit exclusions. +- Required ordering constraints. +- Fix-to-feature folding overrides. +- Exceptional commits that did not originate in a PR. + +It should not duplicate the GitHub PR overview. + +## Release Workflow + +`fork/dev` supports multiple release policies without changing the branch model. + +### Immediate releases + +After a merge: + +1. Obtain or run CI for the exact resulting `fork/dev` SHA. +2. Infer affected release targets from the previous approved SHA and the new SHA. +3. Dispatch releases for affected targets. +4. Record the outcome independently for each target. + +This is suitable for urgent bot, server, or desktop fixes. + +### Lagged releases + +Merges may instead accumulate and be promoted: + +- After a debounce period. +- Every few hours. +- Daily. +- At a manually selected checkpoint. +- On different schedules for different products. + +For example, bot/server releases can be frequent while desktop or mobile uses a slower promotion +cadence. These are release-policy decisions and do not require additional integration branches. + +### Checkpoints + +Approved releases and clean projections should reference immutable `fork/dev` checkpoints, for +example: + +```text +fork-dev/2026-08-06.1 +fork-dev/2026-08-06.2 +``` + +Each deployment record should include: + +- `fork/dev` SHA. +- CI run and conclusion. +- Calculated change scope. +- Bot deployment status. +- Server deployment status. +- Desktop build/publication status. +- Mobile build/publication status. + +A partial multi-target release must not be represented as completely deployed. + +## Periodic Clean Downstream Projection + +Clean downstream history can be generated monthly, twice monthly, before major releases, or on +demand. It is not part of the routine release critical path. + +### Inputs + +- Latest selected upstream commit. +- Rebuilt and verified `fork/base`, `fork/tim`, and `fork/candidates`. +- A tagged, green `fork/dev` checkpoint. +- GitHub PRs merged between projection checkpoints. +- Exceptional projection metadata and conflict policies. + +### Process + +1. Select and tag a green `fork/dev` checkpoint. +2. Update the upstream mirror. +3. Rebuild and verify `fork/base`. +4. Rebuild and verify `fork/tim`. +5. Rebuild and verify `fork/candidates`. +6. Select downstream PRs represented in the checkpoint. +7. Exclude provenance-sync commits because their content is already represented below. +8. Replay downstream features into `fork/changes-clean`. +9. Fold explicitly linked repairs into their owning feature commits. +10. Generate any still-required client projections. +11. Compose `fork/integration-clean`. +12. Run the complete per-layer gate in stop-the-line order. +13. Verify tree equivalence with the selected `fork/dev` checkpoint. +14. Publish generated branches only after all checks pass. + +### Failure behavior + +A projection failure: + +- Blocks publication of the generated clean stack. +- Does not rewrite or block `fork/dev`. +- Does not block unrelated releases from an already-green `fork/dev` SHA. +- Produces an actionable report identifying the PR, layer, commit, and conflicting paths. + +## Tree-Equivalence Verification + +The clean projection must prove it did not drop downstream behavior. + +At minimum, compare the selected trees: + +```bash +git diff --exit-code +``` + +Any expected differences must be narrowly documented and machine-verifiable. Do not accept broad +path exclusions. + +The projection gate should additionally verify: + +- Package manifest and lockfile consistency. +- Migration namespace integrity. +- Web and mobile fork-surface existence tests. +- Server and bot behavioral tests. +- Desktop build and preload verification. +- Identity, Discord, and VS Code behavior. +- Release smoke tests. + +## CI Responsibilities + +### PRs targeting `fork/dev` + +Run the complete product gate appropriate to the inferred impact: + +- Formatting and lint. +- Full monorepo typecheck. +- Unit and behavior tests. +- Desktop build and preload checks when affected. +- Mobile native static analysis when affected. +- Release smoke checks when affected. +- Conservative full validation for ambiguous shared changes. + +### Merges into `fork/dev` + +Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if +the resulting merge SHA differs or the base moved. + +### Rebased provenance and clean projection layers + +Retain the existing full, stop-the-line per-layer gate: + +```text +main + -> fork/base + -> fork/tim + -> fork/candidates + -> fork/changes-clean + -> optional generated projections + -> fork/integration-clean +``` + +This expensive process is appropriate for periodic provenance maintenance. It should not sit in the +path of every routine product release. + +## Migration Plan + +### Phase 1: Establish `fork/dev` + +1. Create `fork/dev` from the current green `fork/integration` tip. +2. Prove the initial trees are identical. +3. Record the incorporated `fork/candidates` checkpoint. +4. Protect `fork/dev` against force-pushes and deletion. +5. Configure required checks and merge policy. +6. Make `fork/dev` the default base for ordinary contributor PRs. +7. Keep the old release path temporarily as a fallback. + +### Phase 2: Release from `fork/dev` + +1. Update release workflows to accept an exact green `fork/dev` SHA. +2. Implement path/dependency-based scope classification. +3. Track per-target release outcomes. +4. Add immutable release/checkpoint records. +5. Exercise bot, server, desktop, and mobile paths. +6. Stop requiring full-stack composition for routine releases after validation. + +### Phase 3: Simplify overlays + +1. Route new identity, Discord, and VS Code PRs directly to `fork/dev` unless independent staging is + demonstrably required. +2. Move ownership to `CODEOWNERS` and path-based CI. +3. Land current overlay content into `fork/dev` through reviewed migration PRs. +4. Retire permanent composition overlays once no open work depends on them. +5. Convert small cross-cutting overlays, including the desktop URL handler, into ordinary stable + feature history. + +### Phase 4: Automate provenance synchronization + +1. Persist the last imported candidates commit and tree. +2. Build the `C1..C2` tree-delta sync branch automatically. +3. Open a reviewed PR into `fork/dev`. +4. Run the complete product gate. +5. Update the checkpoint only after the PR merges. +6. Never merge a rewritten provenance branch directly into `fork/dev`. + +### Phase 5: Automate clean projection + +1. Query GitHub PRs between checkpoint tags. +2. Infer ordinary changes from their commits and paths. +3. Read explicit metadata only for imports and projection exceptions. +4. Replay changes into clean layers. +5. Fold explicitly related repairs. +6. Apply durable conflict policies. +7. Verify every rewritten layer. +8. Prove final tree equivalence. +9. Publish generated branches. + +### Phase 6: Retire the old critical path + +After the new release and projection workflows are proven: + +- Stop targeting `fork/changes` with contributor PRs. +- Stop composing permanent overlays after every product merge. +- Mark generated branches clearly as non-authoring surfaces. +- Keep clean reconstruction available without coupling it to normal releases. + +## Operational Rules + +1. Never force-push or rebase `fork/dev`. +2. Never merge generated projection branches into `fork/dev`. +3. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta. +4. Never require a clean projection rebuild to ship an unrelated urgent fix. +5. Never release an untested `fork/dev` SHA. +6. Every ordinary product change enters through a GitHub PR. +7. Daily contributors do not manually classify affected clients when paths and dependencies can do + so. +8. External imports record immutable source provenance. +9. Projection exceptions are explicit and rare. +10. A generated integration is not successful until it matches its selected `fork/dev` checkpoint. + +## Open Decisions + +These decisions can be made independently after the branch topology is accepted: + +- Immediate, debounced, scheduled, or manual release cadence for each target. +- Squash-only versus mixed merge policy on `fork/dev`. +- Frequency of upstream/provenance synchronization. +- Frequency of clean downstream projection. +- Whether identity, Discord, or VS Code needs a stable subsystem staging branch. +- Whether clean projection is mandatory for major releases or purely an audit/upstreaming artifact. +- Naming of the generated clean branches. +- Exact rules for mapping shared-package changes to downstream consumers. + +## Recommended Decision + +Adopt the following operating principle: + +> Rebase external provenance; merge stable product development; release exact green `fork/dev` +> checkpoints; generate clean downstream history separately. + +This retains the valuable structure of `main -> base -> Tim -> candidates`, removes daily rebases +from contributor and release workflows, avoids routine label administration, and preserves the +ability to produce a clean, auditable downstream history when it is actually needed. From cb0d26fc38ef4501e8d92b6f7d107e4ffd1f3945 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 05:00:55 +0000 Subject: [PATCH 2/9] docs: front-load the fork/dev cutover and defer clean projection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The proposal read as one large migration whose payoff arrived at the end. In practice the benefit is concentrated in two steps — cut fork/dev from the green fork/integration tip, and release exact green fork/dev SHAs — and everything downstream of that is optional. Restructure accordingly: - Add an adopt-now/defer table so the blocking work is visible without reading the whole document. - Promote the fork/dev cutover and release-from-fork/dev into named steps 1 and 2, each a concrete checklist. - Move clean downstream projection, tree-equivalence proofs, and periodic projection releases into a section marked deferred, and say plainly that nothing in steps 1-3 depends on them. - State that provenance synchronization is a manual procedure to begin with; automating it is a convenience, not a prerequisite. - Keep Source-* import trailers as immediate practice, since they are cheap at import time and hard to reconstruct later. Call out the one ordering hazard explicitly: fork/dev is cut from fork/integration, not fork/changes, so overlay content is present from the first commit and every registered overlay must be drained rather than left open against fork/changes. Add an Ops Repository Changes section describing the prerequisite made available in aaaomega/ops: the trusted deploy branch, CI workflow, and CI trigger event become configurable with today's values as defaults, so the cutover is a deploy.env edit and a poller restart, and reverting is the same edit. Note that component checkpoint state keeps its filenames so the cutover does not trigger a full fleet redeploy. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 566 ++++++++------------- 1 file changed, 225 insertions(+), 341 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index bd807160201..ddab20210d3 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -2,9 +2,14 @@ ## Status -Design proposal. This document describes a migration away from using the continuously rebased fork +Staged plan. This document describes a migration away from using the continuously rebased fork stack as the daily contributor and release path. +**Steps 1 and 2 deliver the entire daily benefit and are the only steps required to start.** +Everything after them is optional, incremental, and can be deferred indefinitely without losing +what steps 1 and 2 gained. In particular, generated clean downstream history and periodic +projection releases are **not** prerequisites and are **not** on the critical path. + The intended outcome is: - Contributors work against a stable branch whose history is never rewritten. @@ -12,7 +17,24 @@ The intended outcome is: - The useful upstream, base, Tim, and candidate provenance layers remain clean and rebased. - Routine work does not require contributors to label desktop, mobile, server, or other affected clients manually. -- Clean downstream history remains available as generated output rather than an authoring surface. +- Clean downstream history remains available as generated output rather than an authoring surface — + if and when it is actually wanted. + +## Adopt Now, Defer the Rest + +| Capability | When | Blocking? | Why | +| ------------------------------------------------ | ---------- | --------- | ------------------------------------------------------ | +| `fork/dev` stable branch, default PR base | **Step 1** | Yes | Removes rebase churn from every contributor | +| Deploy from an exact green `fork/dev` SHA | **Step 1** | Yes | Ops already deploys an exact SHA; only the ref changes | +| Path/dependency-inferred check and release scope | **Step 2** | No | Removes manual client labelling | +| Per-target release records and cadence policy | **Step 2** | No | Lets bot/server ship faster than desktop/mobile | +| Overlay drain into `fork/dev` | Step 3 | No | Happens naturally once overlays stop being rebased | +| Automated `fork/candidates` tree-delta sync | Later | No | Manual sync is fine at the current upstream cadence | +| `fork/changes-clean` / `fork/integration-clean` | If needed | No | Audit/upstreaming artifact, not a release input | +| Periodic clean projection and tree proofs | If needed | No | Only useful when upstreaming or auditing is due | + +The rebased provenance stack (`main → fork/base → fork/tim → fork/candidates`) keeps working exactly +as it does today throughout. Nothing regresses if the clean-projection work is never built. ## Current Problem @@ -64,9 +86,10 @@ upstream/main +-- VS Code work +-- web/mobile/desktop/server/bot work -fork/dev checkpoint - -> fork/changes-clean - -> fork/integration-clean periodic generated projection, never a PR base + (optional, later) + fork/dev checkpoint + -> fork/changes-clean + -> fork/integration-clean generated on demand, never a PR base ``` ## Branch Responsibilities @@ -78,11 +101,10 @@ fork/dev checkpoint | `fork/tim` | Yes | No | No | Selected Tim imports with provenance | | `fork/candidates` | Yes | No | No | Selected unmerged upstream candidates | | `fork/dev` | Never | Yes | Yes | Canonical complete downstream product | -| `fork/changes-clean` | Yes | No | Optional | Curated downstream projection | -| `fork/integration-clean` | Yes | No | Optional/audit | Clean composed output matching a checkpoint | +| `fork/changes-clean` | Yes | No | No (optional) | Curated downstream projection, if built | +| `fork/integration-clean` | Yes | No | No (audit) | Clean composed output matching a checkpoint | -The clean branch names are placeholders. Existing names may be retained if their generated nature -is made unmistakable and tooling prevents contributors from targeting them. +The clean branch names are placeholders and only matter once that work is actually scheduled. ## Core Invariants @@ -105,123 +127,49 @@ is made unmistakable and tooling prevents contributors from targeting them. ### Clean provenance - `main`, `fork/base`, `fork/tim`, and `fork/candidates` retain their current provenance roles. -- Generated clean branches are output only and may be rewritten safely. -- Generated branches are never merged back into `fork/dev`. -- For a selected checkpoint, the final clean projection must reproduce the checkpoint's product - tree, apart from narrowly documented generated metadata. - -```text -tree(fork/integration-clean) == tree(tagged fork/dev checkpoint) -``` - -## Synchronizing the Rebased Stack into `fork/dev` - -Rewritten provenance branches must not be repeatedly merged into `fork/dev`. After a rebase, their -commits have new identities; merging the rewritten tip would duplicate history and produce avoidable -conflicts. - -Instead, synchronize the net tree change. - -Assume: - -- `C1` is the `fork/candidates` tree currently incorporated into `fork/dev`. -- `C2` is the latest rebuilt and verified `fork/candidates` tree. - -The synchronization process should: - -1. Create a sync branch from `fork/dev`. -2. Calculate the tree delta from `C1` to `C2`. -3. Apply that delta to the sync branch. -4. Resolve integration conflicts against the current `fork/dev` product tree. -5. Run the full required checks. -6. Open a normal PR into `fork/dev`. -7. Merge it without rewriting `fork/dev`. -8. Record `C2` as the newly imported provenance checkpoint. - -The PR should be recognizable without requiring daily contributor metadata, for example: - -```text -sync(provenance): import upstream stack C1..C2 -``` - -The imported checkpoint may be recorded in an immutable tag or a small machine-owned state file: - -```json -{ - "importedCandidatesCommit": "", - "importedCandidatesTree": "", - "importedUpstreamCommit": "" -} -``` - -This is stack synchronization state, not a manual product ledger. - -## Daily Contributor Workflow +- Rewritten provenance tips are never merged into `fork/dev`; their tree delta is imported instead. +- Any generated clean branches are output only, may be rewritten safely, and are never merged back. -The normal path becomes: +## Step 1 — Cut Over to `fork/dev` -1. Create a feature branch from `fork/dev`. -2. Open a PR against `fork/dev`. -3. Run the mandatory local validation. -4. Run GitHub CI for the exact PR and merge tip. -5. Merge using the selected stable-history policy. -6. Release immediately or include the merge in the next release cadence. - -No restack or overlay composition is required for an ordinary feature or fix. - -Squash merges are a reasonable default because they give each GitHub PR one stable commit on -`fork/dev`. Merge commits can remain available where preserving a dependent series is valuable. - -## Identity, Discord, and VS Code - -Identity, Discord, and VS Code remain meaningful ownership areas, but they do not necessarily need -permanent composition overlays. - -### Preferred model - -Their feature PRs target `fork/dev` directly. Ownership and validation are inferred from paths and -dependency impact: - -- Discord's separate application/package directories naturally select Discord checks and owners. -- VS Code's separate extension directories naturally select VS Code checks and owners. -- Identity-owned paths and shared integration points select identity checks and owners. -- Shared package changes expand validation to affected consumers. +This is the whole of the immediate benefit. It is a ref change plus branch protection, not a +re-architecture. -Use path-based workflow filters and `CODEOWNERS` instead of requiring contributors to apply client -labels. - -### Optional subsystem staging branches - -If one of these areas genuinely needs independent staging, it may use a stable branch such as: - -```text -fork/discord-dev -fork/vscode-dev -fork/identity-dev -``` +1. Create `fork/dev` from the current green `fork/integration` tip. +2. Prove the initial trees are identical: `git diff --exit-code fork/integration fork/dev`. +3. Record the incorporated `fork/candidates` checkpoint (commit + tree). +4. Protect `fork/dev` against force-push and deletion; configure required checks. +5. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. +6. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). +7. Keep the old compose-and-deploy path installed as a temporary fallback. -These branches must also never be rebased. Work is merged into `fork/dev`, and `fork/dev` is merged -back afterward so the subsystem branch stays current. +### Cutover consequence: overlays are landed, not skipped -This adds merge topology and administration, so it should only be introduced where separate staging -provides a concrete benefit. Clear folders alone are not sufficient justification. +Because `fork/dev` is cut from `fork/integration` — not from `fork/changes` — **overlay content is +already present in `fork/dev` from its first commit**. This is deliberate: the runnable product must +not regress at cutover. -## Ordinary Cross-Cutting Features +The consequence is that every registered overlay must be drained rather than left open: -Features such as the desktop URL-handler enhancement should be ordinary commits or squash-merged -PRs on `fork/dev`, not permanent layers. +- An overlay whose content is fully contained in the cutover tip is **done**. Close its PR, remove + it from `.github/pr-stack.json`, and stop rebasing it. +- An overlay with work not yet in the cutover tip is rebased **onto `fork/dev`** once, then merged + as an ordinary PR. +- Leaving an overlay open against `fork/changes` after cutover will duplicate its commits the next + time anything composes. Drain first, then cut over — or cut over and drain the same day. -For example: +This is the one ordering hazard in the migration. Everything else is additive. -```text -feat(desktop): support remote URL handling -``` +## Step 2 — Release Directly from `fork/dev` -The changed files determine validation and release scope. A dedicated layer is justified only when -work has independent external provenance or must remain independently staged—not simply because it -is identifiable as a feature. +1. Update release workflows to accept an exact green `fork/dev` SHA. +2. Implement path/dependency-based scope classification (below). +3. Track per-target release outcomes independently. +4. Add immutable checkpoint tags for approved releases. +5. Exercise bot, server, desktop, and mobile paths once. +6. Stop requiring full-stack composition for routine releases. -## Inferring Validation and Release Scope +### Inferring validation and release scope Daily contributors should not manually classify their work as web, mobile, desktop, server, or bot. Automation should derive affected surfaces from the diff and the workspace dependency graph. @@ -240,300 +188,234 @@ packages/client-runtime/** -> web and mobile shared build/config paths -> conservative full validation ``` -Path rules should be generated from workspace ownership/dependency data where practical. Unknown or -ambiguous shared paths should fail safely by selecting broader checks, not by requiring labels. - -Labels remain appropriate only for exceptional intent that cannot be inferred from code: - -- Explicitly excluding a change from a clean projection. -- Associating a repair with an earlier feature for history folding. -- Recording an unusual external import. -- Requesting a special release behavior. +Unknown or ambiguous shared paths must fail safely by selecting broader checks, never by requiring a +label. `scripts/classify-deployment-diff.sh` already performs this classification for deployment; the +same classifier should drive PR check selection so the two cannot disagree. -## GitHub PRs as the Development Ledger - -GitHub PR history is the primary daily ledger. Do not create a second manifest containing every -normal contributor PR. +Labels remain appropriate only for exceptional intent that cannot be inferred from code — for +example requesting an unusual release behaviour, or recording an unusual external import. -The projection process can use: +### Release policies -- Merge order and timestamps. -- Stable squash or merge commits on `fork/dev`. -- Changed paths. -- PR relationships and referenced issues. -- External provenance recorded on import PRs. -- Checkpoint tags defining the projection interval. +`fork/dev` supports multiple release policies without changing the branch model. -Explicit metadata is required only when the work cannot speak for itself. +**Immediate:** after a merge, obtain CI for the exact resulting `fork/dev` SHA, infer affected +targets from the previous approved SHA, dispatch, and record each outcome. Suitable for urgent bot, +server, or desktop fixes. -### External imports +**Lagged:** merges accumulate and are promoted after a debounce period, every few hours, daily, or +at a manually selected checkpoint — and on different schedules per product. Bot and server can be +frequent while desktop and mobile use a slower cadence. These are policy decisions requiring no +additional integration branches. -Tim and candidate import PRs should record immutable source information: +Approved releases reference immutable checkpoints, for example `fork-dev/2026-08-06.1`. Each +deployment record should include the `fork/dev` SHA, CI run and conclusion, calculated change scope, +and independent bot / server / desktop / mobile status. **A partial multi-target release must not be +represented as completely deployed.** -```text -Source-Repository: -Source-PR: -Source-SHA: -Fork-Layer: tim|candidate -``` +## Ops Repository Changes -### Folding later repairs +Deployment already promotes an exact CI-approved SHA, so the cutover is a _ref_ change, not a +mechanism change. `aaaomega/ops` currently hardcodes `fork/integration` in the poller, deployer, +clone preparation, failure notifier, and laptop catch-up. -A repair that should be folded into an earlier clean-history feature may record: +The prerequisite ops change is to make the trusted branch, CI workflow, and CI trigger event +configurable, defaulting to today's values so nothing changes until the cutover: -```text -Projection-Fold-Into: +```sh +T3CODE_DEPLOY_BRANCH=fork/integration # -> fork/dev at cutover +T3CODE_DEPLOY_CI_WORKFLOW=fork-ci.yml +T3CODE_DEPLOY_CI_EVENT=workflow_dispatch # -> push at cutover ``` -This is exceptional projection metadata, not a label required on normal work. +Cutover is then a single `~/.config/t3code/deploy.env` edit on `smart` plus a poller restart, and +reverting is the same edit. -### Minimal manifest +Component checkpoint state files (`fork-integration--sha`) keep their names across the +cutover on purpose. Their value is the last deployed SHA used for tree-diff classification, and +because `fork/dev` starts at the `fork/integration` tip those SHAs remain valid ancestors — so the +cutover does not trigger a full fleet redeploy. -A small manifest remains useful for machine policy that GitHub cannot reliably infer: +## Daily Contributor Workflow -- Layer ordering. -- External import sources. -- Persistent conflict-resolution rules. -- Explicit exclusions. -- Required ordering constraints. -- Fix-to-feature folding overrides. -- Exceptional commits that did not originate in a PR. +After step 1 the normal path is: -It should not duplicate the GitHub PR overview. +1. Create a feature branch from `fork/dev`. +2. Open a PR against `fork/dev`. +3. Run the mandatory local validation. +4. Run GitHub CI for the exact PR and merge tip. +5. Merge using the selected stable-history policy. +6. Release immediately or in the next release cadence. -## Release Workflow +No restack or overlay composition is required for an ordinary feature or fix. -`fork/dev` supports multiple release policies without changing the branch model. +Squash merges are a reasonable default because they give each GitHub PR one stable commit on +`fork/dev`. Merge commits can remain available where preserving a dependent series is valuable. -### Immediate releases +Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if +the resulting merge SHA differs or the base moved. -After a merge: +## Identity, Discord, and VS Code -1. Obtain or run CI for the exact resulting `fork/dev` SHA. -2. Infer affected release targets from the previous approved SHA and the new SHA. -3. Dispatch releases for affected targets. -4. Record the outcome independently for each target. +Identity, Discord, and VS Code remain meaningful ownership areas, but they do not need permanent +composition overlays. -This is suitable for urgent bot, server, or desktop fixes. +Their feature PRs target `fork/dev` directly. Ownership and validation are inferred from paths and +dependency impact — Discord's and VS Code's separate directories naturally select their own checks +and owners; shared package changes expand validation to affected consumers. Use path-based workflow +filters and `CODEOWNERS` instead of requiring contributors to apply client labels. -### Lagged releases +If one of these areas genuinely needs independent staging, it may later use a stable, never-rebased +branch such as `fork/discord-dev`. Work merges into `fork/dev`, and `fork/dev` merges back +afterwards. This adds merge topology and administration, so introduce it only where separate staging +provides a concrete benefit. Clear folders alone are not sufficient justification. -Merges may instead accumulate and be promoted: +Likewise, ordinary cross-cutting features — the desktop URL-handler enhancement, for example — +become ordinary squash-merged PRs on `fork/dev`, not permanent layers. A dedicated layer is +justified only when work has independent external provenance or must remain independently staged. -- After a debounce period. -- Every few hours. -- Daily. -- At a manually selected checkpoint. -- On different schedules for different products. +## Synchronizing the Rebased Stack into `fork/dev` -For example, bot/server releases can be frequent while desktop or mobile uses a slower promotion -cadence. These are release-policy decisions and do not require additional integration branches. +Rewritten provenance branches must not be repeatedly merged into `fork/dev`. After a rebase their +commits have new identities; merging the rewritten tip would duplicate history and produce avoidable +conflicts. Synchronize the net tree change instead. -### Checkpoints +Assume `C1` is the `fork/candidates` tree currently incorporated into `fork/dev`, and `C2` is the +latest rebuilt and verified `fork/candidates` tree. Then: -Approved releases and clean projections should reference immutable `fork/dev` checkpoints, for -example: +1. Create a sync branch from `fork/dev`. +2. Calculate the tree delta from `C1` to `C2` and apply it to the sync branch. +3. Resolve integration conflicts against the current `fork/dev` product tree. +4. Run the full required checks. +5. Open a normal PR into `fork/dev`, titled for example + `sync(provenance): import upstream stack C1..C2`. +6. Merge it without rewriting `fork/dev`. +7. Record `C2` as the newly imported provenance checkpoint. + +**This is a manual procedure to begin with, and that is fine.** At the current upstream cadence it +runs rarely enough that automation is a convenience, not a prerequisite. The imported checkpoint may +be recorded in an immutable tag or a small machine-owned state file: -```text -fork-dev/2026-08-06.1 -fork-dev/2026-08-06.2 +```json +{ + "importedCandidatesCommit": "", + "importedCandidatesTree": "", + "importedUpstreamCommit": "" +} ``` -Each deployment record should include: - -- `fork/dev` SHA. -- CI run and conclusion. -- Calculated change scope. -- Bot deployment status. -- Server deployment status. -- Desktop build/publication status. -- Mobile build/publication status. +Automate it later by persisting the last imported commit and tree, building the `C1..C2` sync branch +automatically, opening a reviewed PR, and updating the checkpoint only after that PR merges. -A partial multi-target release must not be represented as completely deployed. - -## Periodic Clean Downstream Projection - -Clean downstream history can be generated monthly, twice monthly, before major releases, or on -demand. It is not part of the routine release critical path. - -### Inputs - -- Latest selected upstream commit. -- Rebuilt and verified `fork/base`, `fork/tim`, and `fork/candidates`. -- A tagged, green `fork/dev` checkpoint. -- GitHub PRs merged between projection checkpoints. -- Exceptional projection metadata and conflict policies. - -### Process - -1. Select and tag a green `fork/dev` checkpoint. -2. Update the upstream mirror. -3. Rebuild and verify `fork/base`. -4. Rebuild and verify `fork/tim`. -5. Rebuild and verify `fork/candidates`. -6. Select downstream PRs represented in the checkpoint. -7. Exclude provenance-sync commits because their content is already represented below. -8. Replay downstream features into `fork/changes-clean`. -9. Fold explicitly linked repairs into their owning feature commits. -10. Generate any still-required client projections. -11. Compose `fork/integration-clean`. -12. Run the complete per-layer gate in stop-the-line order. -13. Verify tree equivalence with the selected `fork/dev` checkpoint. -14. Publish generated branches only after all checks pass. - -### Failure behavior - -A projection failure: - -- Blocks publication of the generated clean stack. -- Does not rewrite or block `fork/dev`. -- Does not block unrelated releases from an already-green `fork/dev` SHA. -- Produces an actionable report identifying the PR, layer, commit, and conflicting paths. - -## Tree-Equivalence Verification +## GitHub PRs as the Development Ledger -The clean projection must prove it did not drop downstream behavior. +GitHub PR history is the primary daily ledger. Do not create a second manifest containing every +normal contributor PR. Merge order, stable squash commits, changed paths, PR relationships, and +checkpoint tags already carry the information. -At minimum, compare the selected trees: +Explicit metadata is required only when the work cannot speak for itself. Tim and candidate import +PRs should record immutable source information: -```bash -git diff --exit-code +```text +Source-Repository: +Source-PR: +Source-SHA: +Fork-Layer: tim|candidate ``` -Any expected differences must be narrowly documented and machine-verifiable. Do not accept broad -path exclusions. - -The projection gate should additionally verify: +This is worth adopting immediately even though the projection work is deferred — the trailers cost +nothing on an import PR and are hard to reconstruct afterwards. -- Package manifest and lockfile consistency. -- Migration namespace integrity. -- Web and mobile fork-surface existence tests. -- Server and bot behavioral tests. -- Desktop build and preload verification. -- Identity, Discord, and VS Code behavior. -- Release smoke tests. +## Deferred — Clean Downstream Projection -## CI Responsibilities +**Not required to reap the benefits above.** Skip this entire section until there is a concrete +need: upstreaming a series, an external audit, or a major release that wants a curated history. +Nothing in steps 1–3 depends on it. -### PRs targeting `fork/dev` +When it is scheduled, clean downstream history is generated on demand from a tagged, green +`fork/dev` checkpoint plus rebuilt `fork/base`, `fork/tim`, and `fork/candidates`: -Run the complete product gate appropriate to the inferred impact: - -- Formatting and lint. -- Full monorepo typecheck. -- Unit and behavior tests. -- Desktop build and preload checks when affected. -- Mobile native static analysis when affected. -- Release smoke checks when affected. -- Conservative full validation for ambiguous shared changes. +1. Select and tag a green `fork/dev` checkpoint. +2. Update the upstream mirror; rebuild and verify `fork/base`, `fork/tim`, and `fork/candidates`. +3. Select the downstream PRs represented in the checkpoint, excluding provenance-sync commits whose + content is already represented below. +4. Replay downstream features into `fork/changes-clean`, folding explicitly linked repairs + (`Projection-Fold-Into: `) into their owning feature commits. +5. Compose `fork/integration-clean` and run the per-layer gate in stop-the-line order. +6. Verify tree equivalence with the selected checkpoint: -### Merges into `fork/dev` + ```bash + git diff --exit-code + ``` -Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if -the resulting merge SHA differs or the base moved. +7. Publish generated branches only after all checks pass. -### Rebased provenance and clean projection layers +Expected differences must be narrowly documented and machine-verifiable; broad path exclusions are +not acceptable, because they are how a projection silently drops downstream behaviour. -Retain the existing full, stop-the-line per-layer gate: +A projection failure blocks publication of the generated clean stack only. It does not rewrite or +block `fork/dev`, does not block releases from an already-green `fork/dev` SHA, and produces an +actionable report identifying the PR, layer, commit, and conflicting paths. -```text -main - -> fork/base - -> fork/tim - -> fork/candidates - -> fork/changes-clean - -> optional generated projections - -> fork/integration-clean -``` +A small manifest remains useful for machine policy GitHub cannot infer — layer ordering, external +import sources, persistent conflict-resolution rules, explicit exclusions, ordering constraints, +fix-to-feature folding overrides, and exceptional commits that did not originate in a PR. It should +not duplicate the GitHub PR overview. -This expensive process is appropriate for periodic provenance maintenance. It should not sit in the -path of every routine product release. +The existing full stop-the-line per-layer gate is retained for this work. It is appropriate for +periodic provenance maintenance and must not sit in the path of a routine product release. ## Migration Plan -### Phase 1: Establish `fork/dev` +### Step 1 — Establish `fork/dev` (do now) -1. Create `fork/dev` from the current green `fork/integration` tip. -2. Prove the initial trees are identical. -3. Record the incorporated `fork/candidates` checkpoint. -4. Protect `fork/dev` against force-pushes and deletion. -5. Configure required checks and merge policy. -6. Make `fork/dev` the default base for ordinary contributor PRs. -7. Keep the old release path temporarily as a fallback. +See [Step 1](#step-1--cut-over-to-forkdev). Includes the ops deploy-branch parameterization and the +overlay drain. -### Phase 2: Release from `fork/dev` +### Step 2 — Release from `fork/dev` (do now) -1. Update release workflows to accept an exact green `fork/dev` SHA. -2. Implement path/dependency-based scope classification. -3. Track per-target release outcomes. -4. Add immutable release/checkpoint records. -5. Exercise bot, server, desktop, and mobile paths. -6. Stop requiring full-stack composition for routine releases after validation. +See [Step 2](#step-2--release-directly-from-forkdev). -### Phase 3: Simplify overlays +### Step 3 — Simplify overlays (as they drain) -1. Route new identity, Discord, and VS Code PRs directly to `fork/dev` unless independent staging is - demonstrably required. +1. Route new identity, Discord, and VS Code PRs directly to `fork/dev`. 2. Move ownership to `CODEOWNERS` and path-based CI. -3. Land current overlay content into `fork/dev` through reviewed migration PRs. -4. Retire permanent composition overlays once no open work depends on them. -5. Convert small cross-cutting overlays, including the desktop URL handler, into ordinary stable - feature history. - -### Phase 4: Automate provenance synchronization - -1. Persist the last imported candidates commit and tree. -2. Build the `C1..C2` tree-delta sync branch automatically. -3. Open a reviewed PR into `fork/dev`. -4. Run the complete product gate. -5. Update the checkpoint only after the PR merges. -6. Never merge a rewritten provenance branch directly into `fork/dev`. - -### Phase 5: Automate clean projection +3. Retire permanent composition overlays once no open work depends on them. +4. Stop targeting `fork/changes` with contributor PRs and stop composing overlays after every merge. -1. Query GitHub PRs between checkpoint tags. -2. Infer ordinary changes from their commits and paths. -3. Read explicit metadata only for imports and projection exceptions. -4. Replay changes into clean layers. -5. Fold explicitly related repairs. -6. Apply durable conflict policies. -7. Verify every rewritten layer. -8. Prove final tree equivalence. -9. Publish generated branches. +### Step 4 — Automate provenance synchronization (when manual becomes tedious) -### Phase 6: Retire the old critical path +Persist the last imported candidates commit and tree, build the `C1..C2` sync branch automatically, +open a reviewed PR, run the full gate, and update the checkpoint only after merge. Never merge a +rewritten provenance branch directly into `fork/dev`. -After the new release and projection workflows are proven: +### Step 5 — Automate clean projection (only if needed) -- Stop targeting `fork/changes` with contributor PRs. -- Stop composing permanent overlays after every product merge. -- Mark generated branches clearly as non-authoring surfaces. -- Keep clean reconstruction available without coupling it to normal releases. +Only once the deferred projection above is actually wanted: query PRs between checkpoint tags, infer +ordinary changes from commits and paths, replay into clean layers, apply durable conflict policies, +verify every rewritten layer, prove tree equivalence, and publish. ## Operational Rules 1. Never force-push or rebase `fork/dev`. -2. Never merge generated projection branches into `fork/dev`. -3. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta. -4. Never require a clean projection rebuild to ship an unrelated urgent fix. -5. Never release an untested `fork/dev` SHA. -6. Every ordinary product change enters through a GitHub PR. -7. Daily contributors do not manually classify affected clients when paths and dependencies can do - so. -8. External imports record immutable source provenance. -9. Projection exceptions are explicit and rare. -10. A generated integration is not successful until it matches its selected `fork/dev` checkpoint. +2. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta. +3. Never require a clean projection rebuild to ship an unrelated urgent fix. +4. Never release an untested `fork/dev` SHA; release the exact merge SHA, not the PR tip. +5. Every ordinary product change enters through a GitHub PR. +6. Daily contributors do not manually classify affected clients when paths and dependencies can. +7. External imports record immutable source provenance at import time. +8. If generated projection branches exist, never merge them into `fork/dev` and never use one as a + PR base. ## Open Decisions -These decisions can be made independently after the branch topology is accepted: +None of these block step 1 or step 2: - Immediate, debounced, scheduled, or manual release cadence for each target. - Squash-only versus mixed merge policy on `fork/dev`. - Frequency of upstream/provenance synchronization. -- Frequency of clean downstream projection. - Whether identity, Discord, or VS Code needs a stable subsystem staging branch. -- Whether clean projection is mandatory for major releases or purely an audit/upstreaming artifact. +- Whether clean downstream projection is ever built, and if so on what trigger. - Naming of the generated clean branches. - Exact rules for mapping shared-package changes to downstream consumers. @@ -542,8 +424,10 @@ These decisions can be made independently after the branch topology is accepted: Adopt the following operating principle: > Rebase external provenance; merge stable product development; release exact green `fork/dev` -> checkpoints; generate clean downstream history separately. +> checkpoints; generate clean downstream history separately — and only when it is actually needed. -This retains the valuable structure of `main -> base -> Tim -> candidates`, removes daily rebases -from contributor and release workflows, avoids routine label administration, and preserves the -ability to produce a clean, auditable downstream history when it is actually needed. +Steps 1 and 2 can land in a single day: cut `fork/dev` from the green `fork/integration` tip, drain +the overlays, flip the ops deploy branch, and release from exact green SHAs. That removes daily +rebases from contributor and release workflows and removes routine label administration immediately, +while preserving the ability to produce a clean, auditable downstream history later if it is ever +worth the cost. From e323b6fe8a60f6a1ddaf102f51538548f984c2d5 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 05:02:44 +0000 Subject: [PATCH 3/9] docs: keep host and repository names out of the public proposal The ops prerequisite section named the private ops repository and the deployment host directly. Neither is needed to explain the change, and this document lives in a public repository. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index ddab20210d3..7cafb480478 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -216,8 +216,8 @@ represented as completely deployed.** ## Ops Repository Changes Deployment already promotes an exact CI-approved SHA, so the cutover is a _ref_ change, not a -mechanism change. `aaaomega/ops` currently hardcodes `fork/integration` in the poller, deployer, -clone preparation, failure notifier, and laptop catch-up. +mechanism change. The private ops repository currently hardcodes `fork/integration` in the poller, +deployer, clone preparation, failure notifier, and laptop catch-up. The prerequisite ops change is to make the trusted branch, CI workflow, and CI trigger event configurable, defaulting to today's values so nothing changes until the cutover: @@ -228,7 +228,7 @@ T3CODE_DEPLOY_CI_WORKFLOW=fork-ci.yml T3CODE_DEPLOY_CI_EVENT=workflow_dispatch # -> push at cutover ``` -Cutover is then a single `~/.config/t3code/deploy.env` edit on `smart` plus a poller restart, and +Cutover is then a single `deploy.env` edit on the deployment host plus a poller restart, and reverting is the same edit. Component checkpoint state files (`fork-integration--sha`) keep their names across the From bfd23aaf6f5536e0d34f26cb050cfcbc607e9364 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 05:04:43 +0000 Subject: [PATCH 4/9] docs: settle squash as the merge policy for fork/dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squash was listed as an open decision. It is decided: every PR into fork/dev becomes exactly one commit. That is also what makes the rest of the document hold together — the PR ledger maps one-to-one onto fork/dev history, release scope is a diff between two commits, and a later clean projection has a single unit to replay per PR. Whether any exception is warranted is deliberately left open rather than pre-specified; it can be argued when a concrete case appears. Until then squash is the only merge method in practice, and step 1 now sets GitHub's default merge method rather than relying on contributors picking the right button. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 38 ++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index 7cafb480478..5d1ac45a708 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -139,9 +139,11 @@ re-architecture. 2. Prove the initial trees are identical: `git diff --exit-code fork/integration fork/dev`. 3. Record the incorporated `fork/candidates` checkpoint (commit + tree). 4. Protect `fork/dev` against force-push and deletion; configure required checks. -5. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. -6. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). -7. Keep the old compose-and-deploy path installed as a temporary fallback. +5. Set squash as the repository's default merge method (see + [Merge policy](#merge-policy-squash-decided)). +6. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. +7. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). +8. Keep the old compose-and-deploy path installed as a temporary fallback. ### Cutover consequence: overlays are landed, not skipped @@ -244,13 +246,23 @@ After step 1 the normal path is: 2. Open a PR against `fork/dev`. 3. Run the mandatory local validation. 4. Run GitHub CI for the exact PR and merge tip. -5. Merge using the selected stable-history policy. +5. Squash merge. 6. Release immediately or in the next release cadence. No restack or overlay composition is required for an ordinary feature or fix. -Squash merges are a reasonable default because they give each GitHub PR one stable commit on -`fork/dev`. Merge commits can remain available where preserving a dependent series is valuable. +### Merge policy: squash (decided) + +**Squash merge is the standard for PRs into `fork/dev`.** Every GitHub PR becomes exactly one stable +commit, which is what makes the rest of this document work: the PR ledger maps one-to-one onto +`fork/dev` history, release scope is a diff between two commits, and a later clean projection has a +single unit to replay per PR. + +Exceptions are not defined yet. If a case appears where preserving a dependent series on `fork/dev` +genuinely matters, it can be argued on its own merits then. Until that happens, treat squash as the +only merge method — and set GitHub's repository default accordingly rather than relying on +contributors picking the right button. Leaving the other merge methods enabled is fine while the +exception question is open; making one the default is not. Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if the resulting merge SHA differs or the base moved. @@ -401,18 +413,23 @@ verify every rewritten layer, prove tree equivalence, and publish. 2. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta. 3. Never require a clean projection rebuild to ship an unrelated urgent fix. 4. Never release an untested `fork/dev` SHA; release the exact merge SHA, not the PR tip. -5. Every ordinary product change enters through a GitHub PR. +5. Every ordinary product change enters through a GitHub PR, squash merged. 6. Daily contributors do not manually classify affected clients when paths and dependencies can. 7. External imports record immutable source provenance at import time. 8. If generated projection branches exist, never merge them into `fork/dev` and never use one as a PR base. +## Settled Decisions + +- **Merge policy: squash.** Every PR into `fork/dev` becomes one commit. Whether any exception is + warranted is left open until a concrete case appears. See + [Merge policy](#merge-policy-squash-decided). + ## Open Decisions None of these block step 1 or step 2: - Immediate, debounced, scheduled, or manual release cadence for each target. -- Squash-only versus mixed merge policy on `fork/dev`. - Frequency of upstream/provenance synchronization. - Whether identity, Discord, or VS Code needs a stable subsystem staging branch. - Whether clean downstream projection is ever built, and if so on what trigger. @@ -423,8 +440,9 @@ None of these block step 1 or step 2: Adopt the following operating principle: -> Rebase external provenance; merge stable product development; release exact green `fork/dev` -> checkpoints; generate clean downstream history separately — and only when it is actually needed. +> Rebase external provenance; squash stable product development onto `fork/dev`; release exact green +> `fork/dev` checkpoints; generate clean downstream history separately — and only when it is +> actually needed. Steps 1 and 2 can land in a single day: cut `fork/dev` from the green `fork/integration` tip, drain the overlays, flip the ops deploy branch, and release from exact green SHAs. That removes daily From c87b243fc9c09f497ad77b3e5afdd8fa6d8a7699 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 05:24:08 +0000 Subject: [PATCH 5/9] docs: correct how squash is actually enforced on GitHub The merge-policy section said to "set GitHub's repository default merge method" and that leaving the other methods enabled was fine. There is no such field. GitHub picks the merge button's primary action from whichever methods are enabled, in the order merge commit then squash then rebase, so leaving merge commits enabled would have left merge commit as the default and quietly contradicted the decision. Squash only becomes the default by disabling merge commits and rebase merging. That also gives exceptions the right shape: taking one requires deliberately re-enabling a method, rather than picking a different dropdown entry. Also flag an ordering trap in step 1: required checks must not be configured before CI is wired to run for fork/dev, or the PR that enables that CI can never satisfy them. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index 5d1ac45a708..182976362c2 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -138,8 +138,9 @@ re-architecture. 1. Create `fork/dev` from the current green `fork/integration` tip. 2. Prove the initial trees are identical: `git diff --exit-code fork/integration fork/dev`. 3. Record the incorporated `fork/candidates` checkpoint (commit + tree). -4. Protect `fork/dev` against force-push and deletion; configure required checks. -5. Set squash as the repository's default merge method (see +4. Protect `fork/dev` against force-push and deletion. Add required checks **after** CI is wired to + run for `fork/dev` — requiring a check that cannot yet run blocks the very PR that enables it. +5. Disable merge commits and rebase merging so squash is the only method (see [Merge policy](#merge-policy-squash-decided)). 6. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. 7. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). @@ -259,10 +260,21 @@ commit, which is what makes the rest of this document work: the PR ledger maps o single unit to replay per PR. Exceptions are not defined yet. If a case appears where preserving a dependent series on `fork/dev` -genuinely matters, it can be argued on its own merits then. Until that happens, treat squash as the -only merge method — and set GitHub's repository default accordingly rather than relying on -contributors picking the right button. Leaving the other merge methods enabled is fine while the -exception question is open; making one the default is not. +genuinely matters, it can be argued on its own merits then. + +Enforce this in repository settings rather than by asking contributors to pick the right button. +GitHub has no "default merge method" field: the merge button's primary action is whichever method is +enabled, in the order merge commit → squash → rebase. Squash only becomes the default by disabling +the other two: + +```sh +gh api -X PATCH repos// \ + -F allow_squash_merge=true -F allow_merge_commit=false -F allow_rebase_merge=false +``` + +That also makes an exception a deliberate act — someone has to re-enable a method to take one — which +is the right shape for a policy whose exceptions are undefined. Pair it with +`required_linear_history` on `fork/dev` so the invariant holds even if a setting is changed later. Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if the resulting merge SHA differs or the base moved. From 2d0e7dfaefba55eb346f4d0011e306de72f02f8f Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 06:24:56 +0000 Subject: [PATCH 6/9] docs: warn that fork/dev always reads as behind upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The branch page reports "5 commits behind pingdotgg/t3code:main" immediately after a successful sync, and the count grows with every import. That is a commit-graph artifact: the delta lands as one squashed commit, so fork/dev carries upstream's content without upstream's commit objects, and GitHub computes ahead/behind by reachability alone. Verified against the first real sync — all five upstream commits' content is present in fork/dev and none of them is an ancestor of it. The dangerous part is the "Sync fork" button sitting next to that banner. It merges upstream main into fork/dev, which is precisely the duplicate-history failure this section already warns about for provenance tips, and the banner invites it on every visit. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index 182976362c2..760624fded7 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -304,6 +304,19 @@ Rewritten provenance branches must not be repeatedly merged into `fork/dev`. Aft commits have new identities; merging the rewritten tip would duplicate history and produce avoidable conflicts. Synchronize the net tree change instead. +### GitHub will report `fork/dev` as behind upstream. Never "Sync fork" + +Because the delta lands as one squashed commit, `fork/dev` carries upstream's _content_ without +upstream's _commit objects_, and GitHub computes ahead/behind purely by reachability. The branch page +will therefore always read `N commits behind pingdotgg/t3code:main`, and `N` grows with every import. +That is expected, not drift: commit-level provenance lives on `main → fork/base → fork/tim → +fork/candidates`, which is where currency should be checked. + +**Do not press the "Sync fork" button.** It merges upstream `main` into `fork/dev`, which is exactly +the duplicate-history failure above — the content is already present, so the merge conflicts en masse +and leaves two representations of the same changes. Verify currency by confirming that the imported +upstream commit recorded in the latest checkpoint matches the upstream tip. + Assume `C1` is the `fork/candidates` tree currently incorporated into `fork/dev`, and `C2` is the latest rebuilt and verified `fork/candidates` tree. Then: From 8f5408d8ff96d6aec43b7e3d69ac40b93a55ab03 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 06:31:14 +0000 Subject: [PATCH 7/9] docs: record upstream ancestry so "behind" stays readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The document claimed fork/dev could not carry upstream commits and that the branch page would read N commits behind forever. That was wrong, and the reasoning behind it was over-applied. The no-merge rule protects against branches that are rebased: fork/base, fork/tim and fork/candidates get new commit identities each cycle, so merging their tips repeatedly duplicates history. upstream/main is append-only and its identities are permanent, so nothing stops fork/dev from having them. Importing only a tree delta brings upstream's content without its commit objects, and GitHub measures ahead/behind by reachability alone — so the one number worth reading was permanently wrong. Close the sync with `git merge -s ours `, which keeps the tree byte-for-byte and adds only the parent link. Verified against the current tip: tree unchanged at 5ce8e37845d5, upstream a2ca89aa1 becomes an ancestor, behind count 0. State the hazard plainly: -s ours asserts every upstream change is accounted for, so a resolution that silently dropped one makes that loss permanent. It belongs at the end of a verified sync, never run alone to green the banner. Configuration follows from wanting a merge commit without giving up squash. Re-enabling merge commits repo-wide would make Merge the primary button again — GitHub orders merge, squash, rebase — reversing the squash decision for every ordinary PR. So keep squash-only and give the sync automation a ruleset bypass actor to push the ancestry merge directly. The sync PR itself stays squashed; the ancestry merge lands on top. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 68 +++++++++++++++++----- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index 760624fded7..dd20bf39eea 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -141,7 +141,8 @@ re-architecture. 4. Protect `fork/dev` against force-push and deletion. Add required checks **after** CI is wired to run for `fork/dev` — requiring a check that cannot yet run blocks the very PR that enables it. 5. Disable merge commits and rebase merging so squash is the only method (see - [Merge policy](#merge-policy-squash-decided)). + [Merge policy](#merge-policy-squash-decided)), and add the sync automation as a ruleset bypass + actor so provenance ancestry merges can still be pushed. 6. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. 7. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). 8. Keep the old compose-and-deploy path installed as a temporary fallback. @@ -259,8 +260,12 @@ commit, which is what makes the rest of this document work: the PR ledger maps o `fork/dev` history, release scope is a diff between two commits, and a later clean projection has a single unit to replay per PR. -Exceptions are not defined yet. If a case appears where preserving a dependent series on `fork/dev` -genuinely matters, it can be argued on its own merits then. +One exception is known: **provenance sync PRs are merge-committed, not squashed**, because +squashing discards the upstream ancestry link that makes "commits behind upstream" readable. See +[Record upstream ancestry](#record-upstream-ancestry-so-behind-stays-readable). + +Beyond that, exceptions are not defined. If a case appears where preserving a dependent series on +`fork/dev` genuinely matters, it can be argued on its own merits then. Enforce this in repository settings rather than by asking contributors to pick the right button. GitHub has no "default merge method" field: the merge button's primary action is whichever method is @@ -276,6 +281,12 @@ That also makes an exception a deliberate act — someone has to re-enable a met is the right shape for a policy whose exceptions are undefined. Pair it with `required_linear_history` on `fork/dev` so the invariant holds even if a setting is changed later. +Leave merge commits disabled even though provenance syncs need one. Re-enabling them repo-wide makes +**Merge** the merge button's primary action again — GitHub picks it in the order merge → squash → +rebase — which quietly reverses this decision for every ordinary PR. Give the sync automation a +ruleset **bypass actor** instead, so it can push the ancestry merge directly while every human path +stays squash-only. + Release only the exact merge SHA after its required checks pass. A green PR tip is not sufficient if the resulting merge SHA differs or the base moved. @@ -304,18 +315,44 @@ Rewritten provenance branches must not be repeatedly merged into `fork/dev`. Aft commits have new identities; merging the rewritten tip would duplicate history and produce avoidable conflicts. Synchronize the net tree change instead. -### GitHub will report `fork/dev` as behind upstream. Never "Sync fork" +### Record upstream ancestry so "behind" stays readable + +The no-merge rule above is about branches that are **rebased**: `fork/base`, `fork/tim`, and +`fork/candidates` get new commit identities every cycle, so merging their tips repeatedly duplicates +history. `upstream/main` is not rebased. It is append-only and its commit identities are permanent, +so there is no reason for `fork/dev` to lack them. + +Importing only a tree delta gives `fork/dev` upstream's _content_ without upstream's _commit +objects_. GitHub computes ahead/behind purely by reachability, so the branch page reads +`N commits behind pingdotgg/t3code:main` and `N` grows with every import — which makes the one number +everyone actually wants to read permanently useless. + +Fix it by recording the ancestry the content already implies, as the final step of a sync: + +```sh +git merge -s ours -m "chore(provenance): record upstream as an ancestor" +``` + +`-s ours` keeps `fork/dev`'s tree byte-for-byte and adds only the parent link. After it, the upstream +commits are genuine ancestors and the branch page reads 0 behind, then counts up honestly as upstream +moves. + +**This step asserts that every upstream change is accounted for.** If a sync resolution silently +dropped one, the merge makes that loss permanent — later merges start from the new merge base and +never re-offer those hunks. So run it only as the last step of a sync whose checks passed, never on +its own to turn the banner green. + +One consequence for repository configuration: this is a merge commit, so it cannot go through the +squash-only PR path. Do **not** re-enable merge commits repo-wide to allow it — that makes Merge the +default button for every PR. Add the sync automation as a **bypass actor** on the `fork/dev` ruleset +and let it push the ancestry merge directly, leaving `required_linear_history` and squash-only intact +for every human path. -Because the delta lands as one squashed commit, `fork/dev` carries upstream's _content_ without -upstream's _commit objects_, and GitHub computes ahead/behind purely by reachability. The branch page -will therefore always read `N commits behind pingdotgg/t3code:main`, and `N` grows with every import. -That is expected, not drift: commit-level provenance lives on `main → fork/base → fork/tim → -fork/candidates`, which is where currency should be checked. +Sequence per sync: merge the content PR normally (squashed), then push the `-s ours` ancestry merge +on top. Squashing a sync branch that already contains the ancestry merge would discard it. -**Do not press the "Sync fork" button.** It merges upstream `main` into `fork/dev`, which is exactly -the duplicate-history failure above — the content is already present, so the merge conflicts en masse -and leaves two representations of the same changes. Verify currency by confirming that the imported -upstream commit recorded in the latest checkpoint matches the upstream tip. +The "Sync fork" button remains the wrong tool — it merges upstream into `fork/dev` for content, which +re-applies changes the delta already brought in. The ancestry merge above is the supported path. Assume `C1` is the `fork/candidates` tree currently incorporated into `fork/dev`, and `C2` is the latest rebuilt and verified `fork/candidates` tree. Then: @@ -326,8 +363,9 @@ latest rebuilt and verified `fork/candidates` tree. Then: 4. Run the full required checks. 5. Open a normal PR into `fork/dev`, titled for example `sync(provenance): import upstream stack C1..C2`. -6. Merge it without rewriting `fork/dev`. -7. Record `C2` as the newly imported provenance checkpoint. +6. Merge it without rewriting `fork/dev` (squashed, like any other PR). +7. Push the upstream ancestry merge on top: `git merge -s ours ` (see above). +8. Record `C2` and the imported upstream commit as the newly imported provenance checkpoint. **This is a manual procedure to begin with, and that is fine.** At the current upstream cadence it runs rarely enough that automation is a convenience, not a prerequisite. The imported checkpoint may From 0216a7bb3242bedcb5779a1e5df77963d2cdb88f Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 07:17:47 +0000 Subject: [PATCH 8/9] docs: record the adopted fork/dev model instead of proposing it The document still read as a plan with "do now" steps while the migration had already been executed and, in places, executed differently. Rewrite it as the record. Corrected to match reality: - fork/dev is the default branch, the contributor target and the release source, live since 2026-08-06, with the actual ruleset, checkpoint tags and SHAs named. - Release now spans two workflows. fork-ci decides whether a SHA is valid and fork-release acts on that verdict via workflow_run, because a release action must never be able to veto a validation verdict. - Ops parameterization and the deploy.env cutover are done, not pending. - fork/changes and fork/integration are frozen, not fallbacks. - Check selection is not path-inferred and the document no longer implies it is. 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. Adds what the cutover surfaced, because each item cost a round trip: fork/dev had no CI path at all, mobile releases would have stopped silently, both mobile workflows hardcoded fork/integration and rejected every fork/dev SHA, a release failure could strand the fleet, and every PR based on fork/changes was already broken by earlier rebases and needed its one real commit cherry-picked rather than its branch replayed. Open decisions are now the ones that are genuinely still open, and the migration steps are the ones genuinely still outstanding. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- docs/stable-dev-release-branch-handover.md | 351 +++++++++++++-------- 1 file changed, 211 insertions(+), 140 deletions(-) diff --git a/docs/stable-dev-release-branch-handover.md b/docs/stable-dev-release-branch-handover.md index dd20bf39eea..d0adcf2b7c1 100644 --- a/docs/stable-dev-release-branch-handover.md +++ b/docs/stable-dev-release-branch-handover.md @@ -2,15 +2,32 @@ ## Status -Staged plan. This document describes a migration away from using the continuously rebased fork -stack as the daily contributor and release path. +**Adopted and live since 2026-08-06.** `fork/dev` is the default branch, the contributor target and +the release source. This document is the record of the model and of what the cutover surfaced, not a +proposal. -**Steps 1 and 2 deliver the entire daily benefit and are the only steps required to start.** -Everything after them is optional, incremental, and can be deferred indefinitely without losing -what steps 1 and 2 gained. In particular, generated clean downstream history and periodic -projection releases are **not** prerequisites and are **not** on the critical path. +What is in place: -The intended outcome is: +| | | +| ---------------------------------------------------------------------------------------- | ----------------------------------- | +| `fork/dev` cut from the green `fork/integration` tip `21badd04e`, trees proven identical | tag `fork-dev/2026-08-06.1` | +| Default branch, ruleset, squash-only merges, required checks | live | +| CI wired for `fork/dev` pull requests and merges | #343 | +| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` on the deploy host | +| Validation and release split into separate workflows | #347, #349 | +| First provenance sync, upstream `2a04db134..a2ca89aa1` | #345, tag `fork-dev/2026-08-06.2` | +| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` | +| Integration overlays drained and deregistered | #348 | + +What is deliberately not done: clean downstream projection (see +[Deferred](#deferred--clean-downstream-projection)), automated provenance sync, and retirement of the +overlay machinery itself. + +`fork/changes` is frozen at `271c4b228` and `fork/integration` at `21badd04e`. Neither is a +contributor target or a release source any longer; they are kept only until the remaining PRs still +based on `fork/changes` are drained. + +The intended outcome, all of which now holds: - Contributors work against a stable branch whose history is never rewritten. - A green merge can be released immediately or according to a configurable cadence. @@ -20,25 +37,23 @@ The intended outcome is: - Clean downstream history remains available as generated output rather than an authoring surface — if and when it is actually wanted. -## Adopt Now, Defer the Rest +## What Remains -| Capability | When | Blocking? | Why | -| ------------------------------------------------ | ---------- | --------- | ------------------------------------------------------ | -| `fork/dev` stable branch, default PR base | **Step 1** | Yes | Removes rebase churn from every contributor | -| Deploy from an exact green `fork/dev` SHA | **Step 1** | Yes | Ops already deploys an exact SHA; only the ref changes | -| Path/dependency-inferred check and release scope | **Step 2** | No | Removes manual client labelling | -| Per-target release records and cadence policy | **Step 2** | No | Lets bot/server ship faster than desktop/mobile | -| Overlay drain into `fork/dev` | Step 3 | No | Happens naturally once overlays stop being rebased | -| Automated `fork/candidates` tree-delta sync | Later | No | Manual sync is fine at the current upstream cadence | -| `fork/changes-clean` / `fork/integration-clean` | If needed | No | Audit/upstreaming artifact, not a release input | -| Periodic clean projection and tree proofs | If needed | No | Only useful when upstreaming or auditing is due | +| Work | State | +| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| PRs still based on `fork/changes` | #317, #226, #185 conflict on rebase; #237 and #238 live in an external fork and need their author | +| Retire `fork/changes` and `fork/integration` | blocked on the above | +| Overlay machinery (`compose-integration`, `rebase-integration-overlays`, `force-update-overlay-tip`, `client-overlay-ownership`) | still present and passing its tests with an empty manifest; removal is ~20 files and a separate decision | +| Automated provenance synchronization | manual, and fine at the current upstream cadence | +| Clean downstream projection | deferred indefinitely; nothing depends on it | +| Per-target release cadence | still immediate for everything | -The rebased provenance stack (`main → fork/base → fork/tim → fork/candidates`) keeps working exactly -as it does today throughout. Nothing regresses if the clean-projection work is never built. +The rebased provenance stack (`main → fork/base → fork/tim → fork/candidates`) works exactly as it +did before and is unaffected by any of the above. -## Current Problem +## The Problem This Solved -The current runnable branch is produced through a fully rewritten stack: +The runnable branch used to be produced through a fully rewritten stack: ```text upstream/main @@ -50,7 +65,7 @@ upstream/main -> fork/integration ``` -This gives the fork good provenance, but it couples ordinary development and releases to expensive +That gave the fork good provenance, but coupled ordinary development and releases to expensive history maintenance: - Rebases change commit identities and invalidate contributor bases. @@ -63,10 +78,10 @@ Git cannot provide stable commit identities and continuously rebased history on The solution is to give stable development and clean projection different branches and different responsibilities. -## Proposed Topology +## Topology -Retain a clean provenance stack through `fork/candidates`, then feed its changes into a stable, -complete development branch: +A clean provenance stack is retained through `fork/candidates`, and its changes are fed into a +stable, complete development branch: ```text upstream/main @@ -94,15 +109,17 @@ upstream/main ## Branch Responsibilities -| Branch | Rewritten | Contributor target | Release source | Purpose | -| ------------------------ | ------------------- | ------------------ | -------------- | ------------------------------------------- | -| `main` | Yes, mirror-managed | No | No | Exact upstream mirror | -| `fork/base` | Yes | No | No | Fork repository and CI infrastructure | -| `fork/tim` | Yes | No | No | Selected Tim imports with provenance | -| `fork/candidates` | Yes | No | No | Selected unmerged upstream candidates | -| `fork/dev` | Never | Yes | Yes | Canonical complete downstream product | -| `fork/changes-clean` | Yes | No | No (optional) | Curated downstream projection, if built | -| `fork/integration-clean` | Yes | No | No (audit) | Clean composed output matching a checkpoint | +| Branch | Rewritten | Contributor target | Release source | Purpose | +| ------------------------ | ------------------- | ------------------ | -------------- | --------------------------------------------- | +| `main` | Yes, mirror-managed | No | No | Exact upstream mirror | +| `fork/base` | Yes | No | No | Fork repository and CI infrastructure | +| `fork/tim` | Yes | No | No | Selected Tim imports with provenance | +| `fork/candidates` | Yes | No | No | Selected unmerged upstream candidates | +| `fork/dev` | Never | Yes | Yes | Canonical complete downstream product | +| `fork/changes` | Was | No, frozen | No | Superseded by `fork/dev`; retire once drained | +| `fork/integration` | Was | No, frozen | No | Superseded by `fork/dev`; retire once drained | +| `fork/changes-clean` | Yes | No | No (optional) | Curated downstream projection, if built | +| `fork/integration-clean` | Yes | No | No (audit) | Clean composed output matching a checkpoint | The clean branch names are placeholders and only matter once that work is actually scheduled. @@ -130,53 +147,102 @@ The clean branch names are placeholders and only matter once that work is actual - Rewritten provenance tips are never merged into `fork/dev`; their tree delta is imported instead. - Any generated clean branches are output only, may be rewritten safely, and are never merged back. -## Step 1 — Cut Over to `fork/dev` - -This is the whole of the immediate benefit. It is a ref change plus branch protection, not a -re-architecture. - -1. Create `fork/dev` from the current green `fork/integration` tip. -2. Prove the initial trees are identical: `git diff --exit-code fork/integration fork/dev`. -3. Record the incorporated `fork/candidates` checkpoint (commit + tree). -4. Protect `fork/dev` against force-push and deletion. Add required checks **after** CI is wired to - run for `fork/dev` — requiring a check that cannot yet run blocks the very PR that enables it. -5. Disable merge commits and rebase merging so squash is the only method (see - [Merge policy](#merge-policy-squash-decided)), and add the sync automation as a ruleset bypass - actor so provenance ancestry merges can still be pushed. -6. Make `fork/dev` the GitHub default branch and the base for ordinary contributor PRs. -7. Point deployment at `fork/dev` (see [Ops Repository Changes](#ops-repository-changes)). -8. Keep the old compose-and-deploy path installed as a temporary fallback. - -### Cutover consequence: overlays are landed, not skipped - -Because `fork/dev` is cut from `fork/integration` — not from `fork/changes` — **overlay content is -already present in `fork/dev` from its first commit**. This is deliberate: the runnable product must -not regress at cutover. - -The consequence is that every registered overlay must be drained rather than left open: - -- An overlay whose content is fully contained in the cutover tip is **done**. Close its PR, remove - it from `.github/pr-stack.json`, and stop rebasing it. -- An overlay with work not yet in the cutover tip is rebased **onto `fork/dev`** once, then merged - as an ordinary PR. -- Leaving an overlay open against `fork/changes` after cutover will duplicate its commits the next - time anything composes. Drain first, then cut over — or cut over and drain the same day. - -This is the one ordering hazard in the migration. Everything else is additive. - -## Step 2 — Release Directly from `fork/dev` - -1. Update release workflows to accept an exact green `fork/dev` SHA. -2. Implement path/dependency-based scope classification (below). -3. Track per-target release outcomes independently. -4. Add immutable checkpoint tags for approved releases. -5. Exercise bot, server, desktop, and mobile paths once. -6. Stop requiring full-stack composition for routine releases. - -### Inferring validation and release scope - -Daily contributors should not manually classify their work as web, mobile, desktop, server, or bot. -Automation should derive affected surfaces from the diff and the workspace dependency graph. +## How the Cutover Was Done + +A ref change plus branch protection, not a re-architecture. + +1. `fork/dev` created from the green `fork/integration` tip `21badd04e`. +2. Trees proven identical — `git diff --exit-code fork/integration fork/dev`, both `a8a2b757a`. +3. Incorporated `fork/candidates` checkpoint recorded in tag `fork-dev/2026-08-06.1`. +4. `fork/dev` protected by the ruleset _Protect fork/dev (PR + CI)_: `deletion`, + `required_linear_history`, `non_fast_forward`, `pull_request` restricted to squash, and required + checks `Check` / `Test` / `Mobile Native Static Analysis` / `Release Smoke`. +5. Merge commits and rebase merging disabled repository-wide, so squash is the only method; the sync + automation holds a ruleset bypass actor so provenance ancestry merges can still be pushed. +6. `fork/dev` made the GitHub default branch and the base for contributor PRs. +7. Deployment pointed at `fork/dev` (see [Ops](#ops)). + +Required checks were added **after** CI was wired to run for `fork/dev`. Requiring a check that +cannot yet run blocks the very PR that makes it runnable — the ordering matters. + +`strict_required_status_checks_policy` is deliberately `false`. Strict would invalidate every open +PR's checks on each merge; it is safe to relax because every merge SHA is validated by its own push +run, and deployment only promotes green SHAs. + +### Overlays were landed, not skipped + +Because `fork/dev` was cut from `fork/integration` — not from `fork/changes` — overlay content was +present in `fork/dev` from its first commit. That was deliberate: the runnable product must not +regress at cutover. + +The consequence was that every registered overlay had to be drained rather than left open. Leaving +one open against `fork/changes` would duplicate its commits the next time anything composed. All +four were verified contained **by path**, not by subject — the only commits not present touched +`pnpm-lock.yaml` alone, regeneration artifacts that compose discards by its own rule — then closed +and deregistered in #348. + +### What the cutover surfaced + +The old path hid several faults that only a real cutover could expose. They are recorded because +each cost a round trip: + +- **`fork/dev` had no CI path at all.** `fork-ci.yml` listed only the rebased stack layers and the + overlays as `pull_request` bases and had no `push` trigger, so required checks were impossible and + no run would ever exist for a merge SHA (#343). +- **Mobile releases would have stopped silently.** The release jobs were gated on + `workflow_dispatch` + `fork/integration`; nothing errors when they simply never fire again (#343). +- **Both mobile workflows hardcoded `ref: fork/integration`** and then asserted the requested SHA was + contained by that checkout, so every `fork/dev` SHA was rejected (#344, #346). +- **A release failure could veto a validation verdict.** A failed EAS dispatch marked a valid SHA + unapprovable and stranded the whole fleet, which is why validation and release are now separate + workflows (#347). +- **Every PR based on `fork/changes` was already broken.** Because `fork/changes` had been rebased, + GitHub reported them as 60–100 commits and 629–741 changed files. Each turned out to be one commit + of real work on stale history; the fix was to cherry-pick that commit onto `fork/dev`, not to + replay the branch. + +## Releasing from `fork/dev` + +Every release refers to an exact green `fork/dev` SHA. Two workflows, deliberately separate: + +| Workflow | Answers | Consumed by | +| ------------------ | ---------------------------- | ---------------------------------------- | +| `fork-ci.yml` | "is this SHA valid?" | the deploy poller, and branch protection | +| `fork-release.yml` | "release this validated SHA" | nothing — terminal | + +`fork-release` is chained on `workflow_run` and starts only after `fork-ci` **concludes success** for +a `push` on `fork/dev`. Its manual `workflow_dispatch` path re-checks that green verdict against the +API rather than trusting the operator. + +This split exists because a release action must never be able to veto a validation verdict. When +mobile dispatch lived inside `fork-ci`, a failed EAS call marked a valid SHA unapprovable and +stranded server, Discord, desktop and VS Code promotion of a perfectly good commit. + +Consequences worth knowing: + +- Release failures are recorded against the release run. The poller queries `fork-ci.yml` and never + sees them. +- `workflow_run` only fires for workflow files on the **default branch**. This works because + `fork/dev` is the default branch; it would silently do nothing otherwise. +- Server, Discord, desktop and VS Code are promoted by the poller pulling from the green `fork-ci` + run. Only mobile is dispatched by `fork-release`. + +The release summary reports what the SHA would move, classified by +`scripts/classify-deployment-diff.sh` — the same script the poller runs, so the report and the fleet +cannot disagree about what a diff means. It compares against the previous _released_ SHA, while the +poller compares against the last SHA it actually _deployed_; when those diverge the poller selects a +superset of the reported targets, never a subset. + +### Inferring release scope + +Daily contributors do not manually classify their work as web, mobile, desktop, server, or bot. +`scripts/classify-deployment-diff.sh` derives the affected surfaces from the diff, and both the +poller and `fork-release` run it, so no label is required to ship. + +**Check selection is not inferred and probably should not be.** Every PR into `fork/dev` runs all +four required checks regardless of the paths it touches. That is the conservative choice, and until +the suite is slow enough to hurt it is also the correct one: a path filter that is wrong in the +narrow direction silently skips a check on a protected branch. Illustrative path rules: @@ -192,9 +258,9 @@ packages/client-runtime/** -> web and mobile shared build/config paths -> conservative full validation ``` -Unknown or ambiguous shared paths must fail safely by selecting broader checks, never by requiring a -label. `scripts/classify-deployment-diff.sh` already performs this classification for deployment; the -same classifier should drive PR check selection so the two cannot disagree. +Unknown or ambiguous shared paths fail safely by selecting broader scope, never by requiring a +label. If check selection is ever narrowed, it must be driven by this same classifier so the two +cannot disagree about what a diff means. Labels remain appropriate only for exceptional intent that cannot be inferred from code — for example requesting an unusual release behaviour, or recording an unusual external import. @@ -203,42 +269,46 @@ example requesting an unusual release behaviour, or recording an unusual externa `fork/dev` supports multiple release policies without changing the branch model. -**Immediate:** after a merge, obtain CI for the exact resulting `fork/dev` SHA, infer affected -targets from the previous approved SHA, dispatch, and record each outcome. Suitable for urgent bot, -server, or desktop fixes. +**Immediate — the current policy for every target.** After a merge, `fork-ci` validates the exact +resulting SHA, `fork-release` infers affected targets from the previous released SHA and dispatches, +and the poller promotes the rest within its 30-second cycle. -**Lagged:** merges accumulate and are promoted after a debounce period, every few hours, daily, or -at a manually selected checkpoint — and on different schedules per product. Bot and server can be -frequent while desktop and mobile use a slower cadence. These are policy decisions requiring no -additional integration branches. +**Lagged — available, not adopted.** Merges could instead accumulate and be promoted after a +debounce period, every few hours, daily, or at a manually selected checkpoint, on different schedules +per product. That is a policy change in `fork-release` and the poller cadence; it needs no additional +integration branches. Approved releases reference immutable checkpoints, for example `fork-dev/2026-08-06.1`. Each deployment record should include the `fork/dev` SHA, CI run and conclusion, calculated change scope, and independent bot / server / desktop / mobile status. **A partial multi-target release must not be represented as completely deployed.** -## Ops Repository Changes - -Deployment already promotes an exact CI-approved SHA, so the cutover is a _ref_ change, not a -mechanism change. The private ops repository currently hardcodes `fork/integration` in the poller, -deployer, clone preparation, failure notifier, and laptop catch-up. +## Ops -The prerequisite ops change is to make the trusted branch, CI workflow, and CI trigger event -configurable, defaulting to today's values so nothing changes until the cutover: +Deployment already promoted an exact CI-approved SHA, so the cutover was a _ref_ change, not a +mechanism change. The private ops repository had hardcoded `fork/integration` in the poller, +deployer, clone preparation, failure notifier and laptop catch-up; those now resolve through one +shared library: ```sh -T3CODE_DEPLOY_BRANCH=fork/integration # -> fork/dev at cutover +T3CODE_DEPLOY_BRANCH=fork/dev # default fork/integration +T3CODE_DEPLOY_CI_EVENT=push # default workflow_dispatch +T3CODE_CONTRIBUTOR_BRANCH=fork/dev # default fork/changes T3CODE_DEPLOY_CI_WORKFLOW=fork-ci.yml -T3CODE_DEPLOY_CI_EVENT=workflow_dispatch # -> push at cutover ``` -Cutover is then a single `deploy.env` edit on the deployment host plus a poller restart, and -reverting is the same edit. +Set in `deploy.env` on the deployment host, which the poller unit reads via `EnvironmentFile=-` and +the library parses directly as plain `KEY=VALUE` — never sourced as shell, so a config file cannot +execute code. Defaults reproduce the pre-cutover behaviour exactly, so reverting is deleting that +file. Component checkpoint state files (`fork-integration--sha`) keep their names across the cutover on purpose. Their value is the last deployed SHA used for tree-diff classification, and -because `fork/dev` starts at the `fork/integration` tip those SHAs remain valid ancestors — so the -cutover does not trigger a full fleet redeploy. +because `fork/dev` started at the `fork/integration` tip those SHAs remained valid ancestors — so the +cutover did not trigger a full fleet redeploy. + +The poller keys on a successful `fork-ci` **push** run for the exact SHA. Because release actions +were moved out of that workflow, a release failure can no longer withhold a fleet promotion. ## Daily Contributor Workflow @@ -440,75 +510,76 @@ not duplicate the GitHub PR overview. The existing full stop-the-line per-layer gate is retained for this work. It is appropriate for periodic provenance maintenance and must not sit in the path of a routine product release. -## Migration Plan - -### Step 1 — Establish `fork/dev` (do now) - -See [Step 1](#step-1--cut-over-to-forkdev). Includes the ops deploy-branch parameterization and the -overlay drain. +## Remaining Migration Steps -### Step 2 — Release from `fork/dev` (do now) +Steps 1 and 2 — establishing `fork/dev` and releasing from it — are done; see +[How the Cutover Was Done](#how-the-cutover-was-done) and +[Releasing from `fork/dev`](#releasing-from-forkdev). -See [Step 2](#step-2--release-directly-from-forkdev). +### Finish draining `fork/changes` -### Step 3 — Simplify overlays (as they drain) +The registered overlays are drained and deregistered. What is left are the ordinary PRs still based +on `fork/changes`: #317, #226 and #185 conflict when their real commit is cherry-picked onto +`fork/dev`, and #237 and #238 live in an external fork and need their author. Once those are +resolved, `fork/changes` and `fork/integration` can be deleted and the composition workflows removed. -1. Route new identity, Discord, and VS Code PRs directly to `fork/dev`. -2. Move ownership to `CODEOWNERS` and path-based CI. -3. Retire permanent composition overlays once no open work depends on them. -4. Stop targeting `fork/changes` with contributor PRs and stop composing overlays after every merge. - -### Step 4 — Automate provenance synchronization (when manual becomes tedious) +### Automate provenance synchronization (when manual becomes tedious) Persist the last imported candidates commit and tree, build the `C1..C2` sync branch automatically, -open a reviewed PR, run the full gate, and update the checkpoint only after merge. Never merge a -rewritten provenance branch directly into `fork/dev`. +open a reviewed PR, run the full gate, push the ancestry merge, and update the checkpoint only after +merge. Never merge a rewritten provenance branch directly into `fork/dev`. -### Step 5 — Automate clean projection (only if needed) +### Automate clean projection (only if needed) -Only once the deferred projection above is actually wanted: query PRs between checkpoint tags, infer +Only once the deferred projection below is actually wanted: query PRs between checkpoint tags, infer ordinary changes from commits and paths, replay into clean layers, apply durable conflict policies, verify every rewritten layer, prove tree equivalence, and publish. ## Operational Rules 1. Never force-push or rebase `fork/dev`. -2. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta. +2. Never merge a rewritten provenance tip directly into `fork/dev`; import its tree delta, then + record upstream ancestry with `merge -s ours`. 3. Never require a clean projection rebuild to ship an unrelated urgent fix. 4. Never release an untested `fork/dev` SHA; release the exact merge SHA, not the PR tip. 5. Every ordinary product change enters through a GitHub PR, squash merged. -6. Daily contributors do not manually classify affected clients when paths and dependencies can. -7. External imports record immutable source provenance at import time. -8. If generated projection branches exist, never merge them into `fork/dev` and never use one as a +6. Never let a release action decide whether a SHA is valid. +7. Daily contributors do not manually classify affected clients when paths and dependencies can. +8. External imports record immutable source provenance at import time. +9. If generated projection branches exist, never merge them into `fork/dev` and never use one as a PR base. ## Settled Decisions -- **Merge policy: squash.** Every PR into `fork/dev` becomes one commit. Whether any exception is - warranted is left open until a concrete case appears. See +- **Merge policy: squash.** Every PR into `fork/dev` becomes one commit, enforced by disabling merge + and rebase merging repository-wide and by the `fork/dev` ruleset. The one exception is the upstream + ancestry merge, pushed by an automation holding a ruleset bypass actor. See [Merge policy](#merge-policy-squash-decided). +- **Validation and release are separate workflows.** `fork-ci` decides validity; `fork-release` + acts on it. See [Releasing from `fork/dev`](#releasing-from-forkdev). +- **Upstream ancestry is recorded on `fork/dev`**, so "commits behind upstream" reads true. See + [Record upstream ancestry](#record-upstream-ancestry-so-behind-stays-readable). ## Open Decisions -None of these block step 1 or step 2: +None of these block anything currently running: -- Immediate, debounced, scheduled, or manual release cadence for each target. +- Release cadence per target. Everything is immediate today; lagged promotion is available. - Frequency of upstream/provenance synchronization. - Whether identity, Discord, or VS Code needs a stable subsystem staging branch. - Whether clean downstream projection is ever built, and if so on what trigger. - Naming of the generated clean branches. - Exact rules for mapping shared-package changes to downstream consumers. +- When to delete `fork/changes` and `fork/integration`, and remove the overlay machinery. -## Recommended Decision - -Adopt the following operating principle: +## The Operating Principle > Rebase external provenance; squash stable product development onto `fork/dev`; release exact green > `fork/dev` checkpoints; generate clean downstream history separately — and only when it is > actually needed. -Steps 1 and 2 can land in a single day: cut `fork/dev` from the green `fork/integration` tip, drain -the overlays, flip the ops deploy branch, and release from exact green SHAs. That removes daily -rebases from contributor and release workflows and removes routine label administration immediately, -while preserving the ability to produce a clean, auditable downstream history later if it is ever -worth the cost. +The cutover took a single day: `fork/dev` cut from the green `fork/integration` tip, overlays +drained, the ops deploy branch flipped, and releases running from exact green SHAs. It removed daily +rebases from contributor and release workflows and removed routine label administration, while +preserving the ability to produce a clean, auditable downstream history later if it is ever worth the +cost. From 88d47fd33356598347690047d80309a51db80d6c Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:39:43 +0000 Subject: [PATCH 9/9] docs: point all PR guidance at fork/dev The overlays are drained and deregistered, but the instructions an agent or a 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) now says branch from fork/dev and target fork/dev for every kind of work, names main, fork/changes and fork/integration as bases never to use, and records that integrationOverlays is empty rather than describing how to register one. The Discord bot's turn rules pointed recovery branches at "the correct base (fork/discord overlay / fork/changes / etc.)" and now point at fork/dev. fork-stack.md, stack-ship-path.md and client-overlays.md describe the previous operating model in detail. They are 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 in them that literally instructed a base are corrected, so nothing in the repository still directs a PR anywhere but fork/dev. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- AGENTS.md | 49 +++++++++-------------- apps/discord-bot/docs/agent-turn-rules.md | 2 +- docs/client-overlays.md | 11 +++++ docs/fork-stack.md | 13 +++++- docs/stack-ship-path.md | 14 ++++++- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3ceb5394c5b..4fa12f19674 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,12 +22,10 @@ Day-to-day ship path (compose, not restack): [docs/stack-ship-path.md](./docs/st contains selected open upstream PRs that we run before upstream accepts them, one provenance commit per source PR, above Tim. The permanent `fork/changes` PR is based on `fork/candidates`, contains only our downstream layer, remains open, and is the GitHub/T3 default branch. -- Long-lived upstreamable features may be registered as `integrationOverlays`. They remain parallel - draft PRs based on `fork/changes`; `fork/integration` composes them in manifest order. Never merge - a registered overlay directly. Update its branch, or use - `pnpm fork:stack overlay-start ` and target the child PR at the overlay branch. - Draft state blocks merging while normal green CI remains meaningful. Permanent overlay PRs must - carry the **`OVERLAY`** label. +- **Integration overlays are retired.** `integrationOverlays` is empty, the four registered overlay + PRs are closed, and their content lives in `fork/dev`. Do not create one, and do not target + `fork/discord`, `fork/vscode`, `fork/identity` or the desktop deep-links branch. Client-owned work + is an ordinary PR against `fork/dev`; ownership follows changed paths. - **Closed permanent draft PRs (overlays / managed stack PRs):** do **not** open a replacement PR as the first reaction. (1) Fix the branch tip if needed (rebase onto the intended base, force- with-lease). (2) **`gh pr reopen `** the **same** PR number and restore draft + **`OVERLAY`** @@ -39,20 +37,18 @@ Day-to-day ship path (compose, not restack): [docs/stack-ship-path.md](./docs/st the layer tip. Do **not** stack permanent tip-only `fix(…)` / `style(…)` recovery commits when the tip is still operator-owned stack surface and history rewrite is allowed. New tip commits are OK for ordinary **feature** work that lands via PR merge into the layer. -- Before targeting `fork/changes`, inspect `.github/client-overlay-ownership.json` or run - `pnpm fork:overlay-owner [changed-path...]`. Changes owned by an extracted client - must update that draft overlay (or a child PR targeting it), not duplicate its implementation in - `fork/changes`. Read [docs/client-overlays.md](./docs/client-overlays.md) for mixed shared/client - changes and extraction cutovers. -- Start new work with `pnpm fork:stack start ` and open the PR against `fork/changes`. - Ordinary feature/import PRs are not added to `.github/pr-stack.json`; they enter the runnable fork - only after being reviewed and merged into `fork/changes`. -- **Never open implementation PRs against `main`.** `main` is the upstream mirror; GitHub will - report conflicts and a huge unrelated diff. Always base and retarget feature PRs on `fork/changes`. +- **Branch from `fork/dev` and open every PR against `fork/dev`.** Discord, VS Code, identity and + desktop work included: the integration overlays were drained into `fork/dev` and deregistered, so + there is no overlay branch to target. Ownership is inferred from changed paths, not from the base + branch. +- **Never open implementation PRs against `main`, `fork/changes` or `fork/integration`.** `main` is + the upstream mirror; the other two are frozen and superseded by `fork/dev`. All three produce a + huge unrelated diff, and `fork/changes` is rebased, which silently invalidates a PR based on it. + See [docs/stable-dev-release-branch-handover.md](./docs/stable-dev-release-branch-handover.md). - Before handoff (and whenever a PR is CONFLICTING / behind), run `pnpm fork:stack update --push` (or `pnpm fork:stack update --push `). That rebases or - replays the feature commits onto the PR's intended parent (`fork/changes` for ordinary features, - or the current parent branch for dependent/overlay-child PRs), retargets only an invalid base, and + replays the feature commits onto the PR's intended parent (`fork/dev` for ordinary features, or the + current parent branch for dependent PRs), retargets only an invalid base, and force-with-lease pushes so the PR stays mergeable. - After automation rebases your branch (or `fork/changes`), refresh a local checkout with `pnpm fork:stack pull`. It hard-resets to remote when local commits are patch-equivalent, and only @@ -131,7 +127,7 @@ Day-to-day ship path (compose, not restack): [docs/stack-ship-path.md](./docs/st recovery PR as done: 1. On the rewritten tip (usually `fork/changes`), run `CI= pnpm install --no-frozen-lockfile` (or `vp install` with frozen lockfile disabled) until the lockfile matches. - 2. Commit the updated `pnpm-lock.yaml` on a PR targeting `fork/changes` (or include it in the + 2. Commit the updated `pnpm-lock.yaml` on a PR targeting `fork/dev` (or include it in the recovery commit that lands the rewrite). 3. Recompose `fork/integration` if the tip already moved, then re-dispatch Fork CI. 4. Confirm install would succeed under CI: frozen lockfile is **on** in Fork CI; failures look @@ -186,12 +182,9 @@ Day-to-day ship path (compose, not restack): [docs/stack-ship-path.md](./docs/st When implementation work for a user request is done (code, docs, config — not pure Q&A): -1. **Commit** the changes on a feature branch created with `pnpm fork:stack start ` (from - `fork/changes`), or `pnpm fork:stack overlay-start ` for overlay-owned work. -2. **Open or update a PR** against the correct base before handing off: - - Ordinary features → **`fork/changes`** (never `main`, never `fork/integration`). - - Client overlay work → the **registered overlay branch** (`fork/discord`, `fork/vscode`, `fork/identity`, or - `t3-discord/f7d37879-desktop-deeplinks`), not a duplicate of that work in `fork/changes`. +1. **Commit** the changes on a feature branch cut from `fork/dev`. +2. **Open or update a PR against `fork/dev`** before handing off — for every kind of work, including + Discord, VS Code, identity and desktop. Never `main`, `fork/changes` or `fork/integration`. 3. **Let the agent ship gate own validation** before saying “updated the PR” or finishing (see _Task Completion Requirements → Agent ship gate_): - **Every agent push is gated.** Draft / no-PR pays the **static** half (`vp check` + @@ -288,10 +281,8 @@ result. **No agent push is free:** draft / no-PR still pays the static half; rea | **Ready** PR | **full** ship gate on every push | | PR state can’t resolve | **full** ship gate (**fail closed**) | -This is identical for `fork/changes`, every registered overlay base (`fork/discord`, `fork/vscode`, -`fork/identity`, desktop deeplinks), and dependent / overlay-child PRs — the gate keys off the PR’s -**ready state**, not its base. Overlay Compose success or Managed-PR draft-lock green is **not** the -gate. +This is identical for `fork/dev` and dependent PRs — the gate keys off the PR’s **ready state**, not +its base. Managed-PR draft-lock green is **not** the gate. **Publish path:** `pnpm pr:ready` — runs the **full** ship gate, then marks the open draft PR ready. Raw `gh pr ready` (and the ready-for-review APIs) reach the same place: the `.tools/bin/gh` policy diff --git a/apps/discord-bot/docs/agent-turn-rules.md b/apps/discord-bot/docs/agent-turn-rules.md index f27bd763304..bc23795f117 100644 --- a/apps/discord-bot/docs/agent-turn-rules.md +++ b/apps/discord-bot/docs/agent-turn-rules.md @@ -12,7 +12,7 @@ Don't mix up requester vs thread starter vs others. **Style:** lead with answer; concise; no status recaps. **PR:** always open for commits/landable work; draft until lint/typecheck/tests/`vp check`; then mark ready. No abandoned drafts. -**PR lifecycle:** before push/handoff, check the linked PR is still **open** (`gh pr view --json state` or equivalent). If **merged** or **closed**, do **not** keep committing on that branch — branch fresh from the correct base (`fork/discord` overlay / `fork/changes` / etc.), re-apply unmerged work, open a **new** PR (draft if still iterating). One merged PR is not a free ticket for later commits. +**PR lifecycle:** before push/handoff, check the linked PR is still **open** (`gh pr view --json state` or equivalent). If **merged** or **closed**, do **not** keep committing on that branch — branch fresh from `fork/dev`, re-apply unmerged work, open a **new** PR (draft if still iterating). One merged PR is not a free ticket for later commits. **PR footer** from turn `pr` + `t3` fields (paste at PR body end; bot may re-append): `opened by [{name}](https://discord.com/users/{uid}) in chat thread **Discord** · [{title}](https://discord.com/channels/{g}/{c}/{m}) · [T3]({t3url})` diff --git a/docs/client-overlays.md b/docs/client-overlays.md index 5a742fcae84..c86b50ea253 100644 --- a/docs/client-overlays.md +++ b/docs/client-overlays.md @@ -1,5 +1,16 @@ # Client integration overlays +> [!IMPORTANT] +> **Superseded. Do not follow this for new work.** +> +> Contributors branch from and target **`fork/dev`** — every kind of work, including Discord, VS Code, +> identity and desktop. The integration overlays are drained and deregistered, and `fork/changes` and +> `fork/integration` are frozen. See +> [stable-dev-release-branch-handover.md](./stable-dev-release-branch-handover.md). +> +> Kept as a record of how the fork operated before 2026-08-06, and because the provenance stack +> (`main` → `fork/base` → `fork/tim` → `fork/candidates`) it describes is still current. + Discord and VS Code are long-lived product integrations rather than anonymous files in `fork/changes`. Their complete client implementations live in parallel draft PRs based on `fork/changes` and are composed into `fork/integration` like the desktop-link overlay. diff --git a/docs/fork-stack.md b/docs/fork-stack.md index feefeac795e..7161e85b371 100644 --- a/docs/fork-stack.md +++ b/docs/fork-stack.md @@ -1,5 +1,16 @@ # Downstream fork workflow +> [!IMPORTANT] +> **Superseded. Do not follow this for new work.** +> +> Contributors branch from and target **`fork/dev`** — every kind of work, including Discord, VS Code, +> identity and desktop. The integration overlays are drained and deregistered, and `fork/changes` and +> `fork/integration` are frozen. See +> [stable-dev-release-branch-handover.md](./stable-dev-release-branch-handover.md). +> +> Kept as a record of how the fork operated before 2026-08-06, and because the provenance stack +> (`main` → `fork/base` → `fork/tim` → `fork/candidates`) it describes is still current. + Day-to-day merge → compose → deploy: [stack-ship-path.md](./stack-ship-path.md). This repository separates upstream history, downstream changes, temporary review branches, and the @@ -223,7 +234,7 @@ The helper starts an independent branch from `fork/changes`: pnpm fork:stack start feature/my-change ``` -Commit and push normally, then open the PR against `fork/changes` (never against `main`). Updating +Commit and push normally, then open the PR against `fork/dev` (never against `main`). Updating that branch updates the same PR and reruns PR CI. Ordinary feature and import PRs are deliberately not registered in the stack manifest, so multiple independent PRs may be open concurrently without editing central metadata. diff --git a/docs/stack-ship-path.md b/docs/stack-ship-path.md index ac46772d1a9..2a05825a251 100644 --- a/docs/stack-ship-path.md +++ b/docs/stack-ship-path.md @@ -1,5 +1,16 @@ # Stack ship path (planned operating model) +> [!IMPORTANT] +> **Superseded. Do not follow this for new work.** +> +> Contributors branch from and target **`fork/dev`** — every kind of work, including Discord, VS Code, +> identity and desktop. The integration overlays are drained and deregistered, and `fork/changes` and +> `fork/integration` are frozen. See +> [stable-dev-release-branch-handover.md](./stable-dev-release-branch-handover.md). +> +> Kept as a record of how the fork operated before 2026-08-06, and because the provenance stack +> (`main` → `fork/base` → `fork/tim` → `fork/candidates`) it describes is still current. + **Goal:** make a product change, merge it, get a green `fork/integration`, and deploy — **without** waiting for a full upstream / Tim / candidates restack. @@ -55,8 +66,7 @@ Related: [fork-stack.md](./fork-stack.md) (topology, overlays, conflict resoluti ### Current rule (keep) ```text -ordinary shared work → branch from fork/changes → PR base fork/changes -overlay-owned work → branch from that overlay → PR base = overlay branch +all work → branch from fork/dev → PR base fork/dev run / deploy → always fork/integration (compose) ```