Commit Graph
39 Commits
Author SHA1 Message Date
Yichen Jiang 9b456b4e93 perf(forge): compare from the recorded base while confirming the head
Status snapshots carry the base commit the host recorded with the head
(baseSha). ForgeService.diff passes it to the provider when the latest
status names the requested head, and forge-github starts the compare read
from it while its GraphQL read confirms the head; a stale head or another
base cancels and joins the early read.
2026-11-07 16:22:24 +08:00
Yichen Jiang ef34a844de fix(forge-panel): refuse commit runs off the first-parent line and harden review state
- Shift-chosen commits must follow each other as first parents; the picker
  says why it keeps a run that leaves the line, and the Host refuses one with
  forgePanel/commits-not-linear for the Branch scope and change requests.
- The Branch scope lists its newest maxCommits commits with
  --no-show-signature, so log.showSignature cannot corrupt the listing.
- Viewed marks merge with the stored entry on each write and follow the
  storage event, so tabs of one Session keep each other's marks.
- .gitattributes patterns follow Git's wildmatch rules, checked against
  git check-attr; change digests include file mode lines.
- GitHub commit pagination refuses a repeated cursor or a changed count;
  commits validates the repository before its host like the other reads.
- The commit list is read once per head, base, or refresh; the lens menu
  hides the count of the disabled Tests lens.
2026-11-07 12:56:23 +08:00
Yichen Jiang fb637c2bfd Merge remote-tracking branch 'origin/master' into feat/forge-review-viewed-lenses
# Conflicts:
#	apps/web/tests/forge-panel.e2e.ts
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/subsystems/forge.i18n.yaml
#	docs/subsystems/forge.md
#	docs/subsystems/forge.zh.md
#	packages/experimental/client-ui-forge/README.i18n.yaml
#	packages/experimental/client-ui-forge/README.md
#	packages/experimental/client-ui-forge/README.zh.md
#	packages/experimental/client-ui-forge/src/client/ChangesTab.tsx
#	packages/experimental/client-ui-forge/src/index.ts
#	packages/experimental/client-ui-forge/tests/controller.host.spec.ts
#	packages/experimental/forge-github/README.i18n.yaml
#	packages/experimental/forge-github/README.md
#	packages/experimental/forge-github/README.zh.md
#	packages/experimental/forge-github/src/index.ts
#	packages/experimental/forge-github/src/provider.ts
#	packages/experimental/forge-github/tests/plugin.spec.ts
#	packages/experimental/forge-github/tests/provider.spec.ts
#	packages/experimental/forge-github/tests/review.spec.ts
#	packages/experimental/forge/README.i18n.yaml
#	packages/extensions/tool-cordis/src/api-catalog.ts
2026-11-07 12:13:56 +08:00
Yichen Jiang 0e971200c4 feat(forge-panel): let repositories declare review layers in .gitattributes
A repository's root .gitattributes now decides a file's review layer first:
dsh-review-layer names test, interface, or implementation, and
linguist-generated still places a file in the test layer. The testPaths and
interfacePaths settings add globs before the built-in rules and are empty by
default. The built-in rules follow the default conventions of common
languages and tools instead of treating every JSON or YAML file and every
name containing config as interface. This repository declares its own locale
dictionaries, snapshot tree, pairing records, and types modules.
2026-11-06 18:47:22 +08:00
Yichen Jiang e07b170d77 fix(forge): address review of pinned diffs and thread counting
- forge-github: repeat a compare read without --allow-escape-sequences when
  a gh older than 2.97 rejects the option; such a gh prints escape sequences
  unchanged, so every version reads every diff (checked with gh 2.96.0 and
  2.100.0 on a pull request whose patch holds an ESC byte).
- client-ui-forge: a stale refusal now re-runs the read effect, so a status
  that arrived during the read, including a failed poll that keeps the
  earlier head, starts the next read at once instead of leaving the changes
  view loading.
- forge: ctx.forge.diff polls at once only for the first stale read at a head
  commit; later stale reads at it wait for the regular poll, which bounds the
  read-poll loop while GitHub's answers briefly disagree.
- forge-github: review thread pages share the byte budget of a complete
  check read.
- client-ui-forge: a truncated count shows its "+" even when it is zero.
- READMEs: document the batch deadline and rewritten-base limitations.
2026-11-06 18:17:04 +08:00
Yichen Jiang c313392bf8 feat(forge-panel): review changes by layer, mark files viewed, and view chosen commits
The changes view gains a lens of All, Interface, Implementation, and Tests
that spans the file tree above it, a switch that hides test files from the
tree and the diff, and a viewed checkbox on each file header that collapses
the file. The toolbar counts viewed files and V toggles the file being read.
Marks are kept per Session, source, path, and change digest in browser
storage, so a file that changes again reads as changed since viewed. All
commits beside Submit review, and beside the base branch of the Branch scope,
shows one commit or a Shift run of commits.
2026-11-06 17:51:22 +08:00
Yichen Jiang 94336c6b3d fix(forge): pin change request diffs to the polled head and count every review thread
ForgeProvider.diff now takes the head commit it must describe. The GitHub
provider reads headRefOid before and after `gh pr diff` and returns the new
ForgeFailure code `stale` when either read names another commit, so the
forge panel never caches a patch under a head it does not describe. A stale
read polls the change request at once, and the changes view waits for the
next status before reading again.

Status polling pages reviewThreads up to the new `maxReviewThreads` setting
(default 1,000) instead of counting only the first 100, and sets
ChangeRequestSnapshot.threadsTruncated when the count is a lower bound.
2026-11-06 16:15:24 +08:00
Yichen Jiang 691ebd7b6c refactor(worktrees): move archive cleanup into the worktree service
The worktree service now records each checkout it creates as a
`plugin:worktree/created` record, folds them in its own `worktree`
projection with fork-aware ownership, and reclaims a clean, unreferenced
checkout when the Session that created it is archived. The Forge panel
reads the service's record and forwards its cleanup state; it no longer
writes a `created` record, owns a retention projection, or gates every
Session's directory access.

Admission registers the attempt inside the Workspace write queue and
returns; inspection runs afterwards, reading cold Sessions through the
projection cache when mounted and from their log otherwise, with subagent
children of an archived ancestor counted as archived. Only a path being
removed holds `working-directory/access`, whose request now carries the
access's `signal`, and listeners withdraw before the drain. Ignored files
no longer retain a checkout, matching Git's non-force removal; processes
and terminals are not consulted.

The Forge bundle mounts one `worktree` service: the `forge-worktree` row
and the `optional-forge-panel` group that isolated a second service are
gone, and `forge-panel` is a top-level row. `canRemove`, `remove`, the
`forgeWorktreeRetention` projection, and `ForgeWorktreeCleanup` are
removed; `WorktreeService.cleanupOf`, `WorktreeCleanup`, and the
`worktree/cleanup` Host event replace them. The upgrade guide moves to
the current version directory and covers the id removals.
2026-11-05 12:28:50 +08:00
Yichen Jiang a2d8340421 Merge origin/master into worktree/archive-session-worktrees 2026-11-05 11:45:46 +08:00
Yichen Jiang a703e9e4b0 fix(forge): address review of the base branch picker
Identify a local review comparison by its compared ref instead of the
displayed base name. Branch records stored before this change appear in
the automatic comparison again, and comparisons that share a name, such
as a worktree start and a local branch, stay apart.

List base branch refs within maxDiffBytes, so a repository with more refs
than maxOutputBytes allows keeps a working menu, and document the
remaining limit.

Move the merge button fill toward the label color so its label reaches
4.5:1 contrast in both themes, name the automatic menu group, and drop an
unused DOM attribute.

Refs #7781
2026-11-03 15:52:36 +08:00
Yichen Jiang 61addcbcb9 Merge remote-tracking branch 'origin/master' into worktree/forge-diff-base-branch-4ffd61 2026-11-03 13:49:51 +08:00
Yichen Jiang fcc1becf11 feat(forge): choose the base branch of local changes
The Branch scope of the local changes view compared only with the
automatic fork point. Its header now names the checked-out branch and a
searchable base branch menu that lists the automatic fork point, then
remote-tracking and local branches; a chosen branch compares with its
merge base with HEAD. The choice lives in the view tab, a missing or
unrelated branch offers the default instead of a retry, and local Branch
review quotes name the base they were compared with.

Refs #7781
2026-11-03 13:49:13 +08:00
Yichen Jiangandimccyu d721353f86 feat(forge): unify plugin entry for reviews and worktrees (#7721)
* feat(forge): unify the worktree plugin entry and add user previews

* docs(forge): show session-linked GitHub pull requests in preview

* refactor(forge): keep plugin unification out of profile loading

* docs(forge): document manual upgrade to the unified plugin entry

* fix(forge): localize preview descriptions and address review feedback

* test(web): refresh creation recommendation catalog for the forge description

---------

Co-authored-by: imccyu <[email protected]>
2026-11-02 22:34:01 +08:00
Yichen Jiang 0f1a3e68a0 Merge remote-tracking branch 'origin/master' into worktree/archive-session-worktrees
# Conflicts:
#	apps/web/tests/forge-panel.e2e.ts
#	packages/client/ui-workspace/tests/apply.client.spec.ts
#	packages/experimental/client-ui-forge/README.i18n.yaml
#	packages/experimental/client-ui-forge/README.md
#	packages/experimental/client-ui-forge/README.zh.md
#	packages/experimental/client-ui-forge/src/client/locales.ts
#	scripts/type-equiv.manifest.json
2026-10-29 16:31:47 +08:00
Yichen Jiang dc169c71e7 Merge pull request #7482 from deepseek-harness/worktree/forge-review-navigation-checks
fix(forge): deduplicate checks and improve review navigation
2026-10-29 11:47:17 +08:00
Yichen Jiang 4f40200ed8 fix(forge): clarify filter failures and query guarantees 2026-10-29 11:16:34 +08:00
Yichen Jiang f08ad306cc fix(forge): load complete checks on demand per pull request 2026-10-29 11:10:35 +08:00
Yichen Jiang 2b9511548b fix(forge): honor session permissions for Git inspection 2026-10-28 16:54:20 +08:00
Yichen Jiang bd1c30cd81 feat(forge): add file tree and inline review comments 2026-10-28 11:42:45 +08:00
Yichen Jiang 5c37ee0163 feat(worktrees): clean up safe UI checkouts when sessions are archived 2026-10-27 12:57:10 +08:00
Yichen Jiang e99a597b16 fix(forge): drain list state streams and refresh reopened requests 2026-10-26 21:22:30 +08:00
Yichen Jiang a36d02a596 Merge master into worktree/forge-session-list 2026-10-26 21:17:53 +08:00
Yichen Jiang bbb031654e Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel
# Conflicts:
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/experimental/forge-github/README.i18n.yaml
#	packages/experimental/forge-github/README.md
#	packages/experimental/forge-github/README.zh.md
#	packages/experimental/forge-github/src/provider.ts
#	packages/experimental/forge-session/README.i18n.yaml
#	packages/experimental/forge-session/README.md
#	packages/experimental/forge-session/README.zh.md
#	packages/experimental/forge-session/src/index.ts
#	packages/experimental/forge/README.i18n.yaml
#	packages/experimental/forge/README.md
#	packages/experimental/forge/README.zh.md
#	packages/experimental/forge/src/index.ts
2026-10-26 18:14:53 +08:00
Yichen Jiang 000583c135 fix(forge-panel): retain incomplete worktree recovery across reloads 2026-10-26 17:58:51 +08:00
Yichen Jiang a5460dbe41 fix(forge): preserve refresh requests during active polling 2026-10-26 17:52:30 +08:00
Yichen Jiang dd0ed998e7 docs(forge): define and verify worktree request type 2026-10-26 17:52:30 +08:00
Yichen Jiang 8d4c8865dd fix(forge-session): resync watches when providers change 2026-10-26 17:48:19 +08:00
Yichen Jiang cba0128268 fix(forge): drain provider calls before unregistering 2026-10-26 17:42:09 +08:00
Yichen Jiang 1abf0f55fe docs(forge): clarify discovery limits and provider contracts 2026-10-26 17:39:09 +08:00
Yichen Jiang df4f3fd0c1 fix(forge-session): bind numeric PR references with explicit repositories 2026-10-26 16:10:51 +08:00
Yichen Jiang 53d8a336f7 Merge commit 'bf1df01ae99' into worktree/forge-session-list
Brings the Session list branch up to date with the forge panel, whose
held messages now wait at inbox insertion.

- client-ui-forge keeps the Session list's remote-access import and the
  panel's messageOf; the agent-controller import is gone.
- The README and the forge panel Agent Note combine both sides: the
  forgeWorktree projection keeps its client view next to the inbox hold,
  and the Session list paragraphs stay.
- The config catalog is regenerated, and the README, forge subsystem
  page, and Agent Note pairings are recorded again.
2026-10-24 22:02:44 +08:00
Yichen Jiang eea6d3b7ec feat(forge): hold a sent message until the Session's worktree is ready
The agent-controller admission hook was reverted on master, so the forge
panel now holds the next Turn with Agent maintenance started from the
agent/inbox/inserted notification. The message stays in the inbox until
the hold ends.

- A message sent before the requested worktree exists waits while the
  panel creates it. After a failed or stopped attempt the message keeps
  waiting until the card retries, through the new
  forgePanel.retryWorktree, or withdraws the request so the message goes
  on in the project directory.
- A message sent after the Session's worktree was removed waits on the
  missing card until the worktree is restored or the Session returns to
  the source checkout.
- Restoring a worktree reports its prepare and checkout steps. The card
  shows them in place of its actions, and the tab's restore action shows
  a spinner while it runs.
- The panel state names what a waiting message waits for and the running
  restoration; the agent-controller dependency is removed.
2026-10-22 17:17:55 +08:00
Yichen Jiang 4916ce9bff feat(forge): show worktree and change request state in the Session list
The branch label left the Session header, and finding which Sessions
work in their own worktrees, or which of their change requests need
attention, meant opening each Session's tab.

- A Session row shows a branch glyph while the Session works in the
  worktree the panel created for it, and the state glyph of its most
  urgent change request with a dot for its checks. The row's hover card
  names the worktree branch and its base, then up to three change
  requests with their state and checks.
- The forgeWorktree projection follows the Session's working directory;
  its client view is the created worktree while the Session works in
  it. The forgeChangeRequests projection gains a client view with the
  bindings, so the Session summary carries both without loading a
  Session.
- The forgePanel.changeRequestStates stream serves the states of the
  change requests every listed Session binds from one Host run. It
  polls each until it is merged or closed and stores every change in
  the forge_panel_change_requests storage domain, so a restart shows
  the last known states before the next poll.
- A new Agent Note records the decision; the READMEs, the forge
  subsystem page, and the catalogs follow.
2026-10-22 15:10:29 +08:00
Yichen Jiang e99272d04e feat(forge): bind change requests from gh pr view/edit/checkout and the checkout's branch
- forge-session recognizes `gh pr view`, `gh pr edit`, and `gh pr checkout`
  as binding evidence with the new `referenced` origin. It binds the URL
  given as the subcommand's first argument and the URL the output prints
  for that change request: gh's `url:` field, a URL alone on a line before
  a description, or a JSON `url` member. Links in a description do not
  bind. Pending calls carry this evidence, and the projection's state
  version moves to 2, so existing Sessions fold their logs again.
- forge-session adds `discoverBranch(session, remote, branch)`, the lookup
  that follows a push, for a branch that a caller reports.
- The panel reports the checkout's upstream branch when a provider
  recognizes its remote, once per remote and branch until a refresh, so a
  Session on a branch with an open change request shows it without a push.
- The empty panel names every way a change request becomes linked.
2026-10-20 14:31:10 +08:00
Yichen Jiang d0fc1f6458 feat(forge): show the changes of a Session and of its change requests
The forge panel opens a changes view in the right sidebar for the
checkout's local changes and for the diff of each change request.

- ForgePanelController reads local diffs in the branch, uncommitted,
  unstaged, and staged scopes through Git in the Session's read-only
  sandbox, change request diffs through the forge provider, and the text
  of a changed file to expand unchanged lines. Change request patches stay
  cached by head commit and base branch within maxCachedDiffBytes. A local
  diff over maxDiffBytes fails as forgePanel/diff-too-large, and a provider
  read over its output limit fails as too-large, which the view explains
  with a link to the code host; a gh timeout fails as unavailable.
- forge adds the diff, fileText, and fileUrl provider methods and the
  too-large failure code, and forge-github implements them with gh.
- The panel shows a local changes card that opens the view and asks the
  Agent for a change request. The view collapses files past a line
  budget, draws lines side by side or in one column with optional
  wrapping, expands unchanged lines on request, and collapses or expands
  every file from its toolbar; file headers use a bare chevron.
- The checkout row copies the worktree or project directory path, the
  refresh glyph turns while a refresh runs, change request cards keep
  their actions while collapsed, and the header entry uses the shared
  compact button.
- client-ui-forge classifies changeRequestKey and FsError as
  peer-required Host imports, which keeps forge and dsh-fs as its peers.
2026-10-20 13:34:27 +08:00
Yichen Jiang 80254034c7 feat(forge): mark change requests ready and merge them from the panel
An expanded card now marks a draft ready for review and merges a change
request that the host would merge now:

- The forge seam gains provider actions `markReady` and `merge`, which
  return a typed `ForgeActionFailure`; the registry polls a watched change
  request at once after either settles. Snapshots carry `mergeReady` and
  the `mergeMethods` the repository allows, the user's usual one first.
- GitHub marks ready with `gh pr ready` and merges through the REST merge
  endpoint with the head SHA the card showed, so new commits refuse the
  merge; it never enables auto-merge, joins a merge queue, or deletes the
  head branch.
- The panel Remote acts only on visible bindings of the Session, and the
  card offers 标记为可评审 for drafts and a 合并 menu with the allowed
  methods and a confirmation, reporting each outcome in a toast.
- The forge panel e2e replaces the worktree branch before the ARIA
  capture normalizes durations, so a random suffix no longer breaks it.
2026-10-20 11:50:10 +08:00
Yichen Jiang 06d1616224 feat(forge): redesign the change request panel
Call every bound item a change request, whatever its code host calls it,
and rebuild the panel around the redesign:

- State glyphs for open, needs attention, draft, merged, closed, and
  unknown change requests; an open one with failed checks, requested
  changes, or conflicts needs attention, as the header dot does.
- A card whose title opens the change request, with a plain number line,
  a collapsed status summary, and expanded checks, review, merge, and diff
  rows; the checks row opens a list with failed checks first.
- The only open change request of a Session starts expanded.
- The checkout row names the branch or detached commit and the kind of
  checkout, with Restore for a removed worktree; the Host home shortens
  paths.
- Checkout inspection reports linked worktrees and the HEAD commit.
2026-10-20 11:50:10 +08:00
Yichen Jiang fc43a2d507 feat(forge): add the pull request panel and worktree Sessions
The forge panel shows the pull requests a Session created or pushed in a
right-sidebar tab, with a Session header button and branch label, and lets
a new Session start in its own Git worktree from the New Session options
row. The forge bundle ships the panel with the forge registry, the GitHub
provider, the Session bindings, and an isolated worktree service.

The panel listens to the agent-controller/admission stage. It holds the
first message of a Session with a worktree request while it creates and
enters the worktree as Agent maintenance, and refuses messages while the
worktree the Session works in is gone. A failed or stopped creation
returns the message to the composer: the card above the composer shows the
failure, sending again retries with a new branch name, and Continue
without a worktree withdraws the request. A removed worktree can be
checked out again or left for the source checkout. A message that enters
without admission still gets its worktree at the first step proposal.
2026-10-20 11:49:51 +08:00
Yichen Jiang dcbfd8079a feat(experimental): bind code host change requests to Sessions
Add the experimental forge capability seam:

- dsh-experimental-forge: provider-neutral repository and change request
  references, a provider registry, and shared status polling. Watchers of
  one change request share one loop; each forge has one batched fetch in
  flight, and the cadence follows check, mergeability, and lifecycle state.
- dsh-experimental-forge-github: GitHub provider that runs the GitHub CLI
  on the host with a scrubbed environment and reads every due pull request
  of a host in one GraphQL query.
- dsh-experimental-forge-session: a host-only projection derives Session
  bindings from gh pr create and git push shell results and from
  plugin:forge-session/binding records; push results trigger head-branch
  discovery of open pull requests, and Session watchers poll bound change
  requests through ctx.forge.
2026-10-20 11:49:25 +08:00