Skip to content

feat(fork): refuse a fixup! commit that does not fold into its owner - #1619

Merged
donjor merged 1 commit into
hyprwsfrom
fix/fork-ci-fixup-fold-proof
Oct 4, 2026
Merged

donjor merged 1 commit into
hyprwsfrom
fix/fork-ci-fixup-fold-proof

Conversation

@donjor-agent

@donjor-agent donjor-agent Bot commented Oct 4, 2026

Copy link
Copy Markdown

🤖 claude/anthropic claude-opus-5-5 (high)

Problem

Pull-request CI never checks that a fixup! commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

Change

New vp run fork:fixup-fold --base <upstream base> --head <head>:

  1. Skips a stack with no fixup!
  2. Resolves owners with the sync's resolveFixups
  3. Replays with autosquash in a temp worktree
  4. Keeps rerere off during the replay
  5. Requires no stops and a tip-equal tree
  6. Names each failing fixup and owner

Owner lookup handles a squash (#N) suffix.
It runs as step 4 of fork:ci and as a Check job step in hyprws-ci.yml.
fork-ci-flags.ts now also emits the base= output that step reads.

Scope and approval

Closes #1554.

  • fork:ci: refuses a fixup that does not fold
  • Check job: runs the same proof
  • Refusal: names the fixup and its owner

Verification

check result
Fold, squash suffix included passes
Fixup that applies only at tip refused, names both
Fixup naming no fork commit refused
Stack without fixups replay skipped
command exit
vp test run, 4 touched files, 77 tests 0
scripts tsc --noEmit 0
vp run fork:fixup-fold on this head 0
vp run fork:ci, scripts 735 tests 0

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

The sync folds each fixup! into the commit it names during its rebase, so a fixup that only applies at the branch tip landed green in CI and then blocked the next sync. `vp run fork:fixup-fold` now replays the stack from the upstream base in a throwaway worktree with the sync's own fixup placement and requires a stop-free, tree-equal replay; the hyprws CI Check job runs the same step.

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no
@donjor
donjor force-pushed the fix/fork-ci-fixup-fold-proof branch from 24899d1 to c31b693 Compare October 4, 2026 07:54
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
@donjor
donjor merged commit 6498663 into hyprws Oct 4, 2026
@donjor
donjor deleted the fix/fork-ci-fixup-fold-proof branch October 4, 2026 08:22
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 4, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 6, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 6, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 6, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 6, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 8, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 8, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 10, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 11, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 11, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 11, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 16, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 16, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 16, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 16, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 18, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 19, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 20, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 21, 2026
…1623)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

The ACP replay agent sends a frame, then writes its replay status.
The adapter can read that status, or stop the agent, as soon as it sees the last answer.
So the completeness check can read a stale cursor or truncated JSON.
This failed `Test Server 3` on #1619 and in the `.2644` sync dry run (`Unexpected end of JSON input`).

## Change

1. Status writes go to a temp file, then rename
2. Each frame is recorded before it is sent
3. A missing pending request stops before sending

## Scope and approval

Small fix of an obvious test race in `apps/server/scripts/acp-replay-agent.ts`.
No product code changes.

The `Test Server 2` interrupt failure on the same run is a different race.
Upstream fixed it in the `v0.0.46-nightly.20261004.2644` tag; the fork gets it from that sync.

## Verification

| command | exit |
| --- | --- |
| `vp test run` on `AcpRegistryAdapterV2.test.ts` and `OrchestratorReplayFixtures.integration.test.ts`, 130 tests | 0 |
| server `tsc --noEmit` | 0 |
| `vp run fork:ci` | 0 |

Not proven locally: the race itself, which needs CI timing.

Claude Opus 5.5 in Claude Code, inside T3 Code.

## Fork trailers

Fork-Domain: upstream-fixes
Fork-Tier: bugfix
Fork-Upstreamable: yes

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/acp-replay-status-before-answer@3b2ef78","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T07:19:19Z"} -->
donjor pushed a commit that referenced this pull request Oct 21, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 21, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
donjor pushed a commit that referenced this pull request Oct 21, 2026
…1619)

> 🤖 claude/anthropic claude-opus-5-5 (high)

## Problem

Pull-request CI never checks that a `fixup!` commit folds into the commit it names.
The sync folds each fixup during its rebase, so a fixup that only applies at the tip lands green and blocks the next sync.

## Change

New `vp run fork:fixup-fold --base <upstream base> --head <head>`:

1. Skips a stack with no `fixup!`
2. Resolves owners with the sync's `resolveFixups`
3. Replays with autosquash in a temp worktree
4. Keeps rerere off during the replay
5. Requires no stops and a tip-equal tree
6. Names each failing fixup and owner

Owner lookup handles a squash `(#N)` suffix.
It runs as step 4 of `fork:ci` and as a Check job step in `hyprws-ci.yml`.
`fork-ci-flags.ts` now also emits the `base=` output that step reads.

## Scope and approval

Closes #1554.

- [ ] **`fork:ci`:** refuses a fixup that does not fold
- [ ] **Check job:** runs the same proof
- [ ] **Refusal:** names the fixup and its owner

## Verification

| check | result |
| --- | --- |
| Fold, squash suffix included | passes |
| Fixup that applies only at tip | refused, names both |
| Fixup naming no fork commit | refused |
| Stack without fixups | replay skipped |

| command | exit |
| --- | --- |
| `vp test run`, 4 touched files, 77 tests | 0 |
| scripts `tsc --noEmit` | 0 |
| `vp run fork:fixup-fold` on this head | 0 |
| `vp run fork:ci`, scripts 735 tests | 0 |

Claude Opus 5.5 in Claude Code, inside T3 Code; implemented by a Pi child agent.

## Fork trailers

Fork-Domain: fork-meta
Fork-Tier: core
Fork-Upstreamable: no

<!-- gh-bot:attest {"v":2,"repo":"RSI-Software/t3code-hyprws","ref":"fix/fork-ci-fixup-fold-proof@24899d1","build":"252611418","agents":[{"role":"host","iface":"claude","provider":"anthropic","model":"claude-opus-5-5","effort":"high","harness":"[email protected]","session":"da5d3d04239a"}],"ts":"2026-10-04T03:05:16Z"} -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pull-request CI never checks that a fixup! folds into its owner [📥]

1 participant