Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
aea338c
Keep sidebar reopen button outside tab scroller
May 6, 2026
8566039
Handle Codex app-server startup disconnect race
May 6, 2026
41aab4a
Handle transient Codex sidecar identity reads
May 6, 2026
7eae9ac
Cover sidebar reopen overflow behavior
May 6, 2026
913f888
docs: define main mirror and dev self-host model
May 6, 2026
1d54176
fix: guard self-host launch and source updates
May 6, 2026
4c1f0dc
chore: add dev PR queue tooling
May 6, 2026
2bc8790
fix: disable scroll-to-arrowkey fallback for opencode provider
May 5, 2026
ed440dc
fix: update scroll policy tests and skip useless scrollLines in alt s…
May 5, 2026
93c0e15
fix(codex): start app server in requested cwd
May 6, 2026
0a334be
feat: support factory terminal orchestration
May 6, 2026
d6831ad
docs: complete dev self-host workflow documentation
May 6, 2026
2080ba1
docs: record initial dev PR queue
May 6, 2026
48927ee
fix: update opencode contract lab note version facts
May 5, 2026
faf1f82
docs: update initial dev queue for amended PR 319
May 6, 2026
e15a86d
Remove Codex terminal notification args
May 7, 2026
26601ce
Use sessionRef in terminal orchestration payloads
May 7, 2026
fc8a953
Port durable session restore identity
May 6, 2026
b34c7ae
fix: restore codex sidecar resilience
May 7, 2026
c2e57b7
docs: update dev queue with parity PRs
May 7, 2026
129bc9d
Merge remote-tracking branch 'refs/remotes/pr/323' into dev
May 7, 2026
057e9e7
Merge remote-tracking branch 'refs/remotes/pr/321' into dev
May 7, 2026
19d0c06
Merge remote-tracking branch 'refs/remotes/pr/309' into dev
May 7, 2026
cd19fee
Merge remote-tracking branch 'refs/remotes/pr/319' into dev
May 7, 2026
83b1f3a
Merge remote-tracking branch 'refs/remotes/pr/324' into dev
May 7, 2026
4f37b64
Merge commit '83b1f3a1' into codex/sidecar-resilience-parity
May 7, 2026
37b6ffa
docs: refresh queued sidecar PR head
May 7, 2026
2a9f2b8
docs: update dev queue with parity PRs
May 7, 2026
662c66c
docs: avoid queued PR hash churn
May 7, 2026
c897068
Merge remote-tracking branch 'refs/remotes/pr/323' into dev
May 7, 2026
d8f9c44
Merge remote-tracking branch 'refs/remotes/pr/321' into dev
May 7, 2026
591ed60
Merge remote-tracking branch 'refs/remotes/pr/309' into dev
May 7, 2026
666f29d
Merge remote-tracking branch 'refs/remotes/pr/319' into dev
May 7, 2026
bbd8f6c
Merge remote-tracking branch 'refs/remotes/pr/324' into dev
May 7, 2026
e480586
Merge remote-tracking branch 'refs/remotes/pr/326' into dev
May 7, 2026
5439764
Merge commit 'e480586a' into fix/remove-terminal-notification-args
May 7, 2026
7f6ef8c
docs: update dev queue with parity PRs
May 7, 2026
fa6f191
docs: refresh notification PR queue note
May 7, 2026
44a5a5f
Merge remote-tracking branch 'refs/remotes/pr/323' into dev
May 7, 2026
8ee5740
Merge remote-tracking branch 'refs/remotes/pr/321' into dev
May 7, 2026
41da1c0
Merge remote-tracking branch 'refs/remotes/pr/309' into dev
May 7, 2026
431872b
Merge remote-tracking branch 'refs/remotes/pr/319' into dev
May 7, 2026
ab3a634
Merge remote-tracking branch 'refs/remotes/pr/324' into dev
May 7, 2026
40e380a
Merge remote-tracking branch 'refs/remotes/pr/326' into dev
May 7, 2026
93f4b7c
Merge remote-tracking branch 'refs/remotes/pr/325' into dev
May 7, 2026
9386118
Merge commit '93f4b7c5' into replacement/factory-terminal-orchestration
May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions .claude/skills/release-freshell/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,14 @@ Present the proposed README changes to the user for approval before proceeding t

## Release Steps

All work happens on a release branch in a worktree main is untouched until the final atomic fast-forward. This protects the running Freshell instance.
All release preparation happens on a release branch in a worktree from `origin/main`. Local `main` is a mirror of `origin/main`; do not commit to it, fast-forward it, or push it directly.

### 1. Create the release branch

```bash
# From the main repo
git worktree add .worktrees/release-vX.Y.Z -b release/vX.Y.Z main
# From the repo root
git fetch origin
git worktree add .worktrees/release-vX.Y.Z -b release/vX.Y.Z origin/main
cd .worktrees/release-vX.Y.Z
npm install
```
Expand Down Expand Up @@ -129,19 +130,14 @@ All of these are committed to the release branch:
2. **Update README:** Change `--branch vOLD` to `--branch vNEW` in the clone command, and apply the approved Features changes
3. **Commit** with message like `release: vX.Y.Z`

### 4. Fast-forward main
### 4. Open and merge a release PR

```bash
# Back in the main repo working directory
git merge --ff-only release/vX.Y.Z
```

If `--ff-only` fails, go back to the worktree and rebase onto main until it can fast-forward.
Push the release branch and open a PR against `main`. After user approval and required checks, merge it through GitHub. If conflicts appear, rebase the release branch onto `origin/main`, resolve in the worktree, retest, and force-push with `--force-with-lease`.

### 5. Tag and publish

```bash
git push origin main
git fetch origin
git tag -a vX.Y.Z -m "vX.Y.Z"
git push --tags
gh release create vX.Y.Z --title "vX.Y.Z" --notes "..." # with the release notes
Expand All @@ -156,8 +152,8 @@ git branch -d release/vX.Y.Z

## Safety

- Main can contain work-in-progress; users clone a specific release tag
- You are running inside Freshell — if you break main mid-release, you kill yourself
- All release prep happens on a branch in a worktree, so main is never modified until the atomic fast-forward
- Local `main` mirrors `origin/main`; release work happens on a PR branch.
- Users clone a specific release tag.
- The self-hosted integration branch is `dev`, not local `main`.
- Commit the version bump before tagging so the tag points to the right commit
- If any step fails, stop and assess, then make recommendations to the user, rather than pushing forward
31 changes: 11 additions & 20 deletions .claude/skills/reviewing-prs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ We never push work back onto contributors. Our goal is to harvest **good ideas f

1. Create a detached worktree for all PR work:
```bash
git worktree add .worktrees/pr-review --detach HEAD
git worktree add .worktrees/pr-review --detach origin/main
```
2. List open PRs with `gh pr list`
3. Process oldest-to-newest (reduces cascading merge conflicts)
Expand All @@ -28,11 +28,11 @@ We never push work back onto contributors. Our goal is to harvest **good ideas f

```bash
cd .worktrees/pr-review
git fetch origin && git checkout --detach origin/main
git fetch origin && git switch --detach origin/main
gh pr checkout <N>
```

Always reset the worktree to latest main before each PR.
Always reset the worktree to latest `origin/main` before each PR.

### 2. Review the Diff

Expand Down Expand Up @@ -85,7 +85,7 @@ Present build, test, and fresheyes results to the user. Include:

Address all fresheyes findings and test failures before merging:
- Commit fixes on the PR branch with detailed messages
- **Show the diff and get approval before pushing** to the PR branch or main
- **Show the diff and get approval before pushing** to the PR branch
- Re-build and re-test after fixes

### 7. Merge (only after user approves)
Expand All @@ -96,14 +96,9 @@ gh pr merge <N> --merge
```

**If GitHub's merge state is stale** (common after rebase/force-push):
```bash
# In the main repo (not the worktree)
git fetch origin && git merge --ff-only origin/main
git merge --no-ff <branch> -m "Merge pull request #N from ..."
git push origin main
```
refresh the PR branch on `origin/main`, force-push it with `--force-with-lease`, and use GitHub merge after the merge state updates. Do not merge locally into `main` or push `main` directly.

**If conflicts exist:** rebase the PR branch on main, resolve, rebuild, retest, force-push, then merge.
**If conflicts exist:** rebase the PR branch on `origin/main`, resolve, rebuild, retest, force-push with `--force-with-lease`, then merge through GitHub.

### 8. Comment and Close

Expand All @@ -116,7 +111,7 @@ Leave an effusive comment summarizing:
gh pr comment <N> --body "..."
```

Then ensure the PR is closed. Local merges (the fallback path) don't trigger GitHub's auto-close:
Then ensure the PR is closed:

```bash
# Check if still open; close if needed
Expand All @@ -128,10 +123,6 @@ gh pr view <N> --json state -q '.state' | grep -q OPEN && gh pr close <N>
Once all PRs are landed and there is no unfinished work, clean up:

```bash
# Switch back to main in the primary repo
cd <primary-repo>
git checkout main && git pull --ff-only origin main

# Remove the pr-review worktree
git worktree remove .worktrees/pr-review
```
Expand Down Expand Up @@ -192,7 +183,7 @@ The user may respond with:

##### c. Fix (if approved)

- Create a branch: `git checkout -b fix/issue-<N> origin/main`
- Create a branch in a worktree from `origin/main`.
- Implement the fix in the worktree
- Build and run targeted tests
- **Show the diff and get approval before pushing**
Expand All @@ -219,7 +210,7 @@ Always sign the comment `— Codex CLI`.
| Tests | `go test ./...` (targeted) | — |
| Present results | Build/test/fresheyes summary | **User approval (Gate 2)** |
| Fix | Commit + show diff + get approval before push | Build + tests green |
| Merge | `gh pr merge` or local merge | All fixes landed + user says merge |
| Merge | `gh pr merge` | All fixes landed + user says merge |
| Comment + Close | `gh pr comment` + `gh pr close` if still open | Merge complete |
| Reset | `git checkout --detach origin/main` | Before next PR |
| Teardown | `git worktree remove .worktrees/pr-review` | Queue empty, all landed |
Expand Down Expand Up @@ -248,10 +239,10 @@ If you catch yourself thinking any of these, you are about to violate a gate:
| Proceeding without user approval | There are TWO hard gates. Always wait at both. |
| Treating ambiguous signals as approval | Only explicit approval words count. When in doubt, ask. |
| Merging after build+fresheyes without asking | Gate 2 requires presenting results and waiting for go/no-go. |
| Pushing fixes to main without showing diff | Show the diff first. Get approval before any push. |
| Pushing fixes without showing diff | Show the diff first. Get approval before any push to the PR branch. |
| Merging with fresheyes FAILED | Fix all findings first. Never merge a failed review. |
| Forgetting to update worktree between PRs | Stale base causes unnecessary conflicts. Always reset to origin/main. |
| Forgetting to close after local merge | Local merges don't trigger GitHub auto-close. Always check and `gh pr close` if still open. |
| Pushing directly to `main` | `main` mirrors `origin/main`. Use PR branches and GitHub merge. |
| Signing comment as "Codex" or "Claude" | Always sign as `— Codex CLI`. |
| Processing PRs newest-first | Oldest-first minimizes cascading conflicts since earlier PRs often touch files later ones depend on. |
| Processing issues oldest-first | Issue triage is newest-first so the most recent reports and follow-ups are evaluated first. |
19 changes: 11 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Freshell is a self-hosted, browser-accessible terminal multiplexer and session o

## Repo Rules
- Always work in a worktree (in \.worktrees\)
- New behavior changes start on a worktree branch from `origin/main` and are submitted as PRs to `origin/main`; local `dev` only consumes PR heads.
- Many agents may be working in the worktree at the same time. If you see activity from other agents (for example test runs or file changes), respect it.
- Specific user instructions override ALL other instructions, including the above, and including superpowers or skills
- Server uses NodeNext/ESM; relative imports must include `.js` extensions
Expand All @@ -26,16 +27,18 @@ Freshell is a self-hosted, browser-accessible terminal multiplexer and session o
- Use `npm run test:vitest -- ...` for a repo-owned direct Vitest path. Raw `npx vitest` is not a coordinated workflow.
- `test:unit` is the exact default-config `test/unit` workload, `test:integration` is the exact server-config `test/server` workload, and `test:server` stays watch-capable unless you pass an explicit broad `--run`.

## Merging to Main (CRITICAL - Read This)
## Branch Model And Self-Hosting (CRITICAL - Read This)

**You are running inside Freshell right now. This session, the terminal the user is typing in, is served by the main branch. If you break main, you kill yourself mid-operation and the user has to clean up your mess with a separate agent.**
**This checkout is self-hosted from local `dev`, not local `main`.**

- Never run `git merge` directly on main - merge conflicts write `<<<<<<< HEAD` markers into source files, which crashes the server instantly
- Always merge main INTO the feature branch in the worktree first, resolve any conflicts there
- Before fast-forwarding main, run `npm test` and confirm all tests pass (not just related tests)
- If you find failing tests, you must stop everything to understand them and make improvements, even if you do not think you were responsible for them.
- Then fast-forward main: `git merge --ff-only feature/branch` - this is atomic (pointer move, no intermediate states)
- If `--ff-only` fails, go back to the worktree and rebase/merge until it can fast-forward
- Local `main` is a mirror of `origin/main`; do not commit to it, merge into it, or self-host from it.
- Local `dev` is the self-hosted integration branch. It is rebuilt from `origin/main` plus pending PR heads.
- Do not edit production behavior directly on `dev`.
- If a change is needed on `dev`, create or update a PR against `origin/main`, then apply that PR head to `dev`.
- If applying a PR to `dev` needs semantic conflict resolution, stop and fix the PR branch or create a replacement PR. Do not hide behavior changes in a local-only `dev` merge commit.
- Never run `git merge` directly on `main`.
- Never reset, force-push, or fast-forward local `main` unless the user explicitly asks to realign it to `origin/main`, the running server is no longer using `main`, and the intentional OpenCode notification-argument removal has been preserved in a PR that is included in `dev`.
- We cannot approve our own PRs. `dev` may contain unapproved pending work, but `origin/main` changes still require independent review.

## Process Safety (CRITICAL)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ Drop a directory with a `freshell.json` manifest into `~/.freshell/extensions/`

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Contributions are welcome. Start from `origin/main`, submit a Pull Request against `main`, and keep behavior changes on PR branches. Local development self-hosting uses the `dev` integration branch described in [docs/development/branch-model.md](docs/development/branch-model.md).

## Community Projects

Expand Down
102 changes: 102 additions & 0 deletions docs/development/branch-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Branch Model

Freshell development uses two local integration concepts:

- `main`: exact mirror of `origin/main`
- `dev`: self-hosted local integration branch

## Branch Responsibilities

`main` is disposable. It should always be resettable to `origin/main` with no local work lost.

`dev` is where the local Freshell instance runs. It is assembled from `origin/main` plus pending PR heads. It is not where new behavior is authored.

## Pending PR Definition

A PR is pending for `dev` only when all of these are true:

- It is open.
- It targets `main`.
- It is not draft.
- It is not marked do-not-merge, superseded, or approval-artifact-only.
- The user wants it in the self-hosted integration queue.
- Its branch applies cleanly to `origin/main`, or its branch has been updated so it does.

If a PR cannot be amended because it comes from an external fork, create a replacement PR before adding that behavior to `dev`.

## Change Flow

1. Start work from `origin/main` in a worktree.
2. Implement the change.
3. Push a PR against `origin/main`.
4. Add that PR head to local `dev`.
5. Wait for independent review before merging the PR to `origin/main`.

Never put behavior changes only on `dev`.

## Conflict Policy

If a PR conflicts with `origin/main`, fix the PR branch.

If two pending PRs conflict with each other, fix one or both PR branches.

Do not resolve semantic conflicts only on `dev`. `dev` must remain reproducible from `origin/main` plus PR heads.

## Excluded PRs

Draft PRs, do-not-merge PRs, closed PRs, superseded PRs, and approval artifacts are excluded from `dev` unless the user explicitly says otherwise.

## Building `dev`

Use an explicit queue. Do not blindly apply every open PR.

Example:

```bash
npm run dev:queue -- plan --prs 323,321,309,319,324,326,325,322
```

The queue script must fail if a PR is draft, closed, not targeting `main`, or cannot be applied cleanly. Fix PR branches before rebuilding `dev`.

To rebuild local `dev`:

```bash
git switch dev
npm run dev:queue -- assemble --prs 323,321,309,319,324,326,325,322
```

Use replacement PR numbers instead of external or superseded PRs. If the script stops on a conflict, do not resolve the conflict on `dev`. Abort the merge, fix the PR branch, and rerun the queue.

Initial migration queue:

| PR | Head SHA | Purpose |
| --- | --- | --- |
| #323 | Current PR head | `dev` branch workflow, launch guardrails, and queue tooling |
| #321 | `7eae9acf13d2ecf36de6ecade8354cb22b944f7b` | Sidebar reopen corner behavior |
| #309 | `93c0e15f8b3e04d7e1bbd8ab312619ae28cfefa2` | Codex startup cwd fix |
| #319 | `48927eef6b46a2232ebe31d1e1dea38d2203eb72` | OpenCode native scroll behavior |
| #324 | `fc8a953565c8c4e416fc7bc0e951b0888c8ed421` | Durable session restore identity parity |
| #326 | Current PR head | Codex sidecar resilience parity |
| #325 | Current PR head | Intentional removal of broken Codex notification launch args |
| #322 | `26601cec20434790936af3a3f9cc823c8c19f984` | Replacement for externally-owned factory terminal orchestration PR |

Initial migration exclusions:

| PR | Head SHA | Reason |
| --- | --- | --- |
| #297 | `8cad328c158a6b33d9779ce1748bfe725ecd0d1c` | Externally-owned and superseded by #322 |
| #289 | `4e4782699adadc3e006b96143f6ead6bda8b136d` | Draft approval artifact |

## Local Main Realignment

Only realign local `main` after Freshell is self-hosting from `dev`, the user has explicitly approved the reset, and the intentional OpenCode notification-argument removal has been preserved in an open PR that is included in `dev` or confirmed already present in a selected pending PR.

The intended final state is:

```bash
git switch main
git fetch origin
git reset --hard origin/main
```

Do not run that command during ordinary development. It belongs only to the migration task that realigns local `main` after self-hosting has moved to `dev`.
17 changes: 10 additions & 7 deletions docs/lab-notes/2026-04-20-coding-cli-session-contract.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Coding CLI Session Contract Lab Note

This note records the real-binary provider probes rerun on `2026-04-26` inside `/home/user/code/freshell/.worktrees/trycycle-codex-session-resilience`. Binary version facts were refreshed on `2026-05-03` inside `/home/user/code/freshell/.worktrees/land-local-main-codex-sidecar-lifecycle`.
This note records the real-binary provider probes rerun on `2026-04-26` inside `/home/user/code/freshell/.worktrees/trycycle-codex-session-resilience`. Binary version facts were refreshed on `2026-05-03` inside `/home/user/code/freshell/.worktrees/land-local-main-codex-sidecar-lifecycle`; the Claude Code binary version fact was refreshed again on `2026-05-05` inside `/home/user/code/freshell/.worktrees/codex-sidebar-reopen-corner-20260505` after the installed binary changed. The later version-only refreshes did not re-prove the behavior contract, so `capturedOn` remains `2026-04-26`.

The implementation plan file is dated `2026-04-19` because the design work was written the day before. This note is dated `2026-04-26` because the real-provider contracts were re-proved on the implementation machine on that date, and that verification date is the one Freshell is allowed to build on.

Expand All @@ -9,7 +9,8 @@ The implementation plan file is dated `2026-04-19` because the design work was w
{
"capturedOn": "2026-04-26",
"planCreatedOn": "2026-04-19",
"dateReason": "The plan was drafted on 2026-04-19, but the checked-in note is dated 2026-04-26 because that is when the durable behavior contract was re-proved on the implementation machine and the earlier 2026-04-23 contract capture was superseded by the newer provider behavior. Binary version facts were refreshed on 2026-05-03 after the installed provider versions changed.",
"binaryVersionFactsRefreshedOn": "2026-05-05",
"dateReason": "The plan was drafted on 2026-04-19, but the checked-in note is dated 2026-04-26 because that is when the durable behavior contract was re-proved on the implementation machine and the earlier 2026-04-23 contract capture was superseded by the newer provider behavior. Binary version facts were refreshed on 2026-05-03 after installed provider versions changed, and the Claude Code binary version fact was refreshed on 2026-05-05 after the local installed binary changed to 2.1.129. These later version-only refreshes did not re-prove the behavior contract.",
"cleanup": {
"liveProcessAuditCommand": "ps -eo pid,ppid,stat,cmd --sort=pid | rg \"codex|claude|opencode\"",
"ownershipReportFields": [
Expand Down Expand Up @@ -81,7 +82,7 @@ The implementation plan file is dated `2026-04-19` because the design work was w
"executable": "claude",
"resolvedPath": "/home/user/bin/claude",
"isolatedBinaryPath": "/home/user/.local/bin/claude",
"version": "2.1.126 (Claude Code)",
"version": "2.1.129 (Claude Code)",
"exactIdCommandTemplate": "HOME=<temp-home> /home/user/.local/bin/claude --bare --dangerously-skip-permissions -p --session-id <uuid> <prompt>",
"namedResumeCommandTemplate": "HOME=<temp-home> /home/user/.local/bin/claude --bare --dangerously-skip-permissions -p --resume <title-or-uuid> [--name <title>] <prompt>",
"transcriptGlob": ".claude/projects/*/<uuid>.jsonl",
Expand All @@ -94,7 +95,7 @@ The implementation plan file is dated `2026-04-19` because the design work was w
"opencode": {
"executable": "opencode",
"resolvedPath": "/home/user/.opencode/bin/opencode",
"version": "1.14.33",
"version": "1.14.39",
"runCommandTemplate": "opencode run <prompt> --format json --dangerously-skip-permissions",
"serveCommandTemplate": "opencode serve --hostname 127.0.0.1 --port <port>",
"globalHealthPath": "/global/health",
Expand Down Expand Up @@ -238,9 +239,11 @@ command -v claude
# /home/user/bin/claude

claude --version
# 2.1.126 (Claude Code)
# 2.1.129 (Claude Code)
```

This Claude Code version line was refreshed on `2026-05-05`; the behavior observations below remain from the `2026-04-26` real-provider proof.

The wrapper at `/home/user/bin/claude` shells out to `/home/user/.local/bin/claude`. The isolated probes used the actual binary and overrode `HOME` to keep persistence inside the probe temp root.

Fresh exact-id durability was probed with:
Expand Down Expand Up @@ -287,7 +290,7 @@ command -v opencode
# /home/user/.opencode/bin/opencode

opencode --version
# 1.14.33
# 1.14.39
```

Fresh isolated runs were probed with:
Expand All @@ -312,7 +315,7 @@ curl http://127.0.0.1:<port>/session/status

Observed control behavior:

- `/global/health` returned a healthy payload with version `1.14.33`.
- `/global/health` returned a healthy payload with version `1.14.39`.
- `/session/status` returned `{}` while idle.
- During an attached `opencode run ... --attach http://127.0.0.1:<port>`, `/session/status` returned the same authoritative `sessionID` with `{ "type": "busy" }`.

Expand Down
2 changes: 1 addition & 1 deletion extensions/opencode/freshell.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"supportsModel": true,
"terminalBehavior": {
"preferredRenderer": "canvas",
"scrollInputPolicy": "fallbackToCursorKeysWhenAltScreenMouseCapture"
"scrollInputPolicy": "native"
}
},
"picker": {
Expand Down
Loading
Loading