Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion bstack/commands/ready.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Verify skill: !`ls "$(git rev-parse --show-toplevel 2>/dev/null)"/.agents/skills
Fix what fails at each step, then rerun that step. Note every command, its exit code, and where its evidence is.

1. Re-read the job (the beads issue when there is one) and check each part of it is done.
2. Run the tests that cover the diff.
2. Run the tests that cover the diff. Read `git` (`gh` | `gt` | `origin`) from `handoff-spawn --info`; dependent branches must use that backend's stack commands (`gh stack init/add/submit/sync`, `gt create/track/submit/get`, or `origin stack track/get/restack/push` and `origin pr create --stack-on`). Never run `gt` in a `gh` repo or hand-make a PR merely based on another PR's branch. This readiness check does not authorize submitting, pushing, or merging.
3. When the verify skill above is not `none`, read it and follow it from launch to cleanup.
4. Run any formal check whose coverage the diff touches: a `statechart` spec in `.bercail/statechart/` or committed in the repo. When none covers it, say so.
5. When a beads issue names the job, ask `bstack-judge` one question, giving it the issue and the diff: does the diff match the issue? It names what is missing or was never asked for, and it changes nothing.
Expand Down
7 changes: 7 additions & 0 deletions bstack/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ that is strongest at it. These rules apply to every bstack agent and subagent.
options, wrappers, or compatibility shims nobody asked for.
- Fix the root cause. A guard is a fix only when the state it guards is legitimate.
- Get the data shape right first; the code that follows should be obvious.
- Read the repo's git backend from `handoff-spawn --info` (`git`: `gh`, `gt`,
or `origin`). Dependent branches use that backend's stack commands:
`gh stack init/add/submit/sync`, `gt create/track/submit/get`, or
`origin stack track/get/restack/push` with `origin pr create --stack-on`.
Never hand-make a PR merely based on another PR's branch, and never run `gt`
in a `gh` or `origin` repo. Stage only unless asked to publish; autopilot
may commit and push its PR branch, never merge.
- Match the code around you: naming, comment density, error handling, test style.
- For library docs, error messages, and anything newer than you, search the web
(`websearch`, `webfetch`) instead of guessing.
Expand Down
13 changes: 8 additions & 5 deletions config/agentic-dev/config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ judge = "claude"
# works when off: the lead reviews alone.
council = false

# [graphite]
# Handoff detects Graphite by the branches gt tracks. Override it per repo, keyed
# by the main checkout's path: true for a Graphite repo with no stack right now, false
# for a repo where gt once ran but you don't use it.
# "~/Work/some-repo" = false
# [git]
# Git backend per repo: gh (git + GitHub CLI), gt (Graphite), or origin (Cursor
# Origin). Keys are main-checkout paths; linked worktrees share that choice.
# Without an override: real Graphite tracking, then Cursor Origin, else gh.
# "~/Work/some-repo" = "gh"
# "~/Work/graphite-repo" = "gt"
# "~/Work/origin-repo" = "origin"
# Legacy [graphite] true/false still maps to gt/gh; [git] takes precedence.
6 changes: 3 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ lead = "claude"
build = "grok"
judge = "claude"

[graphite] # optional, one line per repo; see below
"~/Work/some-repo" = false
[git] # optional, one line per main checkout
"~/Work/some-repo" = "gh" # gh | gt | origin
```

After you edit it, `bercail reconfigure` re-reads it, refreshes the skills and bstack, and installs the desk agent and review tool if they are missing. What the `[bstack]` keys do (pins, `effort`, roles) is in [Make it yours](bstack/08-make-it-yours.md#models-and-roles); `review` and `auto_review` in [review](review.md); `[graphite]` in [handoff](handoff.md#graphite).
After you edit it, `bercail reconfigure` re-reads it, refreshes the skills and bstack, and installs the desk agent and review tool if they are missing. What the `[bstack]` keys do (pins, `effort`, roles) is in [Make it yours](bstack/08-make-it-yours.md#models-and-roles); `review` and `auto_review` in [review](review.md); `[git]` in [handoff](handoff.md#git-backend). Legacy `[graphite]` booleans still work (`true` → `gt`, `false` → `gh`), but `[git]` takes precedence.

## Files bercail manages

Expand Down
44 changes: 41 additions & 3 deletions docs/handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ bd create "Fix the login timeout" --description "..." # Shep (the Grok bot)
~/.agents/skills/handoff/scripts/handoff-spawn --branch NAME --stage cursor --issue ID [--dirty|--clean] [--space ID] [--base BRANCH]
```

`--base BRANCH` cuts the desk branch from BRANCH instead of the default branch, so a dependent issue's desk stacks on its parent's branch ([Graphite](#graphite), when the repo uses it, tracks BRANCH as the parent). It works only with `--branch`, and stage `autopilot` can't take `--branch`: give it `--worktree` on the PR's checkout.
`--base BRANCH` cuts the desk branch from BRANCH instead of the default branch, so a dependent issue's desk stacks on its parent's branch. The [git backend](#git-backend) registers that parent with `gh stack`, `gt track`, or `origin stack track`. It works only with `--branch`, and stage `autopilot` can't take `--branch`: give it `--worktree` on the PR's checkout.

**Prompt.** One line pointing at the issue, the same for every harness: ``Your job is beads issue <id>. Run `bd show <id>` (read its comments too) and do the work it describes.``

Expand All @@ -40,9 +40,47 @@ bd create "Fix the login timeout" --description "..." # Shep (the Grok bot)

Each `--branch` spawn is logged in `${XDG_STATE_HOME:-~/.local/state}/agentic-dev/handoffs.jsonl`; headless runs are not logged there. Follow a `--branch` desk through [agent status webhooks](shep.md#agent-status-webhooks) or with `hc wait <desk>`; `desk` and `space` (and `base`, when set) are in the spawn JSON.

## graphite
## Git backend

With `--branch`, a repo that uses Graphite gets `gt track` on the new branch and a line in the agent's handoff comment telling it to use `gt`. `--info` and the spawn JSON report it as `graphite`.
`handoff-spawn --info` reports `"git": "gh"`, `"gt"`, or `"origin"`.
Use that backend throughout the job. `graphite` remains for one release,
derived as `git == "gt"`, so existing callers keep working.

Detection order is: a per-repo `[git]` override, a legacy `[graphite]` override,
real Graphite tracking, a Cursor Origin repo, then `gh`. Keys are main-checkout
paths; linked worktrees share the choice. New overrides use:

```toml
[git]
"~/Work/bercail" = "gh"
"~/Work/stacked-repo" = "gt"
"~/Work/cursor-repo" = "origin"
```

`[git]` takes precedence over `[graphite]`, whose `true`/`false` still maps to
`gt`/`gh`. Detection never runs `gt`.

Origin detection first checks the remote's exact host and repo path
(`origin.cursor.com` or `origin-<environment>.cursor.com`). Only those repos
get a bounded `origin repo view --json mirrorStatus` lookup: `no-mirror` and
`outbound` select Origin; other reported statuses select GitHub. A missing CLI
or failed lookup warns and falls back to `gh`; set `[git]` to settle an unknown
result before publishing. Ordinary GitHub repos make no Origin API call.

| Backend | Dependent branches and publishing |
|---------|-----------------------------------|
| `gh` | `gh stack init` adopts existing parent/child branches; `gh stack add` extends a stack. Publish with `gh stack submit`; update with `gh stack sync`. |
| `gt` | `gt create` or `gt track --parent` records branches; `gt submit` publishes and `gt get` fetches stack updates. |
| `origin` | `origin stack track --onto` records local parents. Publish child PRs with `origin pr create --stack-on <parent-PR> --status open`; update with `origin stack get`, `origin stack restack`, and `origin stack push --all`. |

Never hand-make a dependent PR that merely targets another PR's branch, and
never run `gt` in a `gh` repo. Handoff stages work without committing or
publishing; these commands do not authorize a push. Autopilot may commit and
push to its PR branch, never merge.

### Graphite

With `--branch`, a repo that uses Graphite gets `gt track` on the new branch and a line in the agent's handoff comment telling it to use `gt`.

Any `gt` command initializes the repo it runs in, so a stray one must not turn this on. A repo counts as Graphite when `gt` tracks a branch other than trunk. A repo that only has Graphite's config file doesn't count. The spawn prints a note because a real Graphite repo looks the same once its last stack is merged. The check reads `.graphite_metadata.db` with `sqlite3`. Without `sqlite3`, or if the file can't be read, the spawn skips Graphite and warns. Install `sqlite3` or use the override below.

Expand Down
5 changes: 3 additions & 2 deletions docs/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ The code in a teammate's PR was likely written by an agent, and review bots alre
bercail review-pr https://github.com/OWNER/REPO/pull/123
```

1. **Discover and gather**: Graphite-initialized repos use `gt` in an isolated clone. Other repos and cross-repo fork entries walk GitHub PR bases down to trunk and children up to the top. Calling with any PR in the stack resolves the same bottom (root) PR. PR bodies, closing issues, Linear ids (only noted), pinned commit messages, diffs, and `gh pr checks` are gathered. Checks are kept even when gh exits non-zero. Artifacts live in `${XDG_STATE_HOME:-~/.local/state}/bercail/reviews/<host>/<owner>/<repo>/stack-<root>/`, with one `pr-<n>/<head sha>/` per PR head. `stack.json` records order, parent SHAs and the current PR; `stack.md` gives the stack's order, bases and intent. Each PR's diff and commit range are against its parent, not trunk.
1. **Discover and gather**: The repo's `git` backend selects discovery: `gt` keeps the Graphite walk in an isolated clone; `gh` uses `gh stack` for a registered stack, otherwise walks GitHub PR bases down to trunk and children up to the top; `origin` uses `origin stack` and `origin pr`. A stray Graphite config alone never selects `gt`. Calling with any PR in the stack resolves the same bottom (root) PR. PR bodies, closing issues, Linear ids (only noted), pinned commit messages, diffs, and `gh pr checks` are gathered. Checks are kept even when gh exits non-zero. Artifacts live in `${XDG_STATE_HOME:-~/.local/state}/bercail/reviews/<host>/<owner>/<repo>/stack-<root>/`, with one `pr-<n>/<head sha>/` per PR head. `stack.json` records order, parent SHAs and the current PR; `stack.md` gives the stack's order, bases and intent. Each PR's diff and commit range are against its parent, not trunk.
2. **Worktree and space**: one detached sibling worktree, such as `~/Work/leftlane_app.review-123`, starts at the root PR. Bercail uses its normal worktree layout helper, so Herdr groups the desk under the reviewed repo rather than creating an unrelated state-directory review space. The main checkout's branch, files, config and other worktrees stay untouched. Graphite synchronization happens only in an isolated clone, never in your checkout.
3. **Thin check**: an empty body, a body that only restates the title, no linked issue (GitHub or Linear), or only "part N of M". The printed JSON lists flags and pass status **per PR**. Thin PRs wait; the rest start immediately. Shep asks only about flagged PRs. `bercail review-pr run N --stack DIR` explicitly reviews one, including a thin PR. `--no-run` gathers without starting any passes.
4. **The decisions passes**: with Herdr open, one read-only `bstack-pr-review` session produces the cards on the review space's desk. PR passes queue for that session instead of opening independent agents. Bercail never passes `--model`. The session runs in the stack dir, outside the reviewed repo, with immutable, SHA-pinned file context, so moving the worktree cannot change what it reviews. Its permissions allow only read, glob and grep. No bug hunting, style, nits, or tests. Validated output becomes `review.json` (`bercail.review/v1`). `run.json` records `ready`, `blocked`, or `failed` with the desk's session id. Per-PR locks prevent duplicate passes. Gathering cannot replace inputs during a live pass. Without Herdr, CLI passes still run headlessly in parallel.
5. **The cards**: the Stack tab is bercail's `bercail-review` Herdr mod. The UI needs the Herdr app. Without it `bercail review-pr ui` fails and says so. Each picker row shows the PR title, one truncated code-inferred intent line, then pass status and CI. There is no second PR list. The card has distinct section headings for Inferred intent, Clue comparison, Decisions, Unmet, Questions, Bots and CI. Intent shows confidence, evidence and whether it looks like the right thing to build. Each clue shows its source, alignment and comparison. Each decision has separate labeled lines for Kind, Summary, Evidence, Stated, Fit and Alternative. File references and code identifiers use compact, colored monospace chips. Long paths show their basename and line, with the full path on hover. The mod reads `bercail review-pr state` and reloads when the run dir changes. `bercail review-pr ui` shows the tab, or makes it.
6. **Each PR's actions**, by key or by their buttons on the card (**j**/**k** pick a PR):
- **c Code review**: a `Review #N` tab on `tuicr pr <n>` in the review space (`bercail review-pr open N`). Each question goes in as a draft note by `bstack`, starting `[AI Agent] `, on the decision's line (the whole PR when it has none). Edit, delete, add your own, then `:submit`. One tuicr per review space: the Review tab is made fresh each time (drafts stay in tuicr's session).
- For Origin, **c Code review** opens `origin pr view --web` instead. The installed tuicr PR mode does not support Origin, so local review cards are not seeded into its browser review.
- **r Re-review**: gather the stack again (heads, CI, bodies), then run this PR's decisions pass again, even if it was ready (`bercail review-pr rereview N`).
- **o Shell**: the review worktree on the PR's branch and a `Shell #N` tab in it (`bercail review-pr checkout N`). A missing local branch is made at the PR head. Bercail never moves a branch of yours: when yours is elsewhere, or checked out in another worktree, the worktree goes to the head detached and the tab says why.

Expand All @@ -42,7 +43,7 @@ When every current card is ready, that same session writes a stack summary: over

Agents never post. You do, from tuicr's `:submit` or GitHub, and every draft you keep goes out as yours, labelled `[AI Agent]`. In opencode, `/pr-review <run dir>` runs the same pass by hand.

If no checkout is found, bercail clones to `~/Work/<repo>` (or `~/Work/<owner>-<repo>` if the first path belongs to something else), then creates its sibling review worktree. It never overwrites an existing checkout. A newly cloned repo uses the generic GitHub walk because it has no local Graphite configuration.
If no checkout is found, bercail clones to `~/Work/<repo>` (or `~/Work/<owner>-<repo>` if the first path belongs to something else), then creates its sibling review worktree. It never overwrites an existing checkout. A newly cloned GitHub repo without stack metadata uses the generic GitHub walk; a Cursor Origin repo uses Origin's stack discovery.

If a process is killed during lock recovery, an empty `*.lock.reap` directory can remain and block retries. After confirming no gather, pass or open process is using that stack, remove that empty directory with `rmdir`. Do not remove a live process's lock.

Expand Down
3 changes: 3 additions & 0 deletions lib/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ read_layout_review() {
fi
}

# shellcheck disable=SC2034 # Read by the installer that sources this library.
RECONFIGURE=0

# The Herdr app half of bercail: the bercail layout, its mods (the desk, and the
Expand Down Expand Up @@ -377,6 +378,8 @@ deploy_lib() {
deploy_install_file "lib/$libfile" "${AGENTIC_DEV_SHARE_DIR}/lib/$libfile"
done
fi
# Installed lib/ has no adjacent skills tree.
deploy_install_file "skills/handoff/scripts/git-backend.sh" "${AGENTIC_DEV_SHARE_DIR}/lib/git-backend.sh"
}

_recorded_install_source() {
Expand Down
1 change: 1 addition & 0 deletions lib/git-backend.sh
Loading
Loading