Commit Graph
10 Commits
Author SHA1 Message Date
jinyeandQwen-Coder f3af3dd857 refactor(managed-agent): Split server into Maven modules (#15871)
* refactor(managed-agent): split server into Maven modules

Separate contracts, orchestration, persistence, adapters, assembly, and verification while retaining the existing service delivery model. Move tests and build consumers together and document local evidence and pending CI acceptance.

Co-authored-by: Qwen-Coder <[email protected]>

* refactor(managed-agent): reduce configuration and SDK boilerplate

Co-authored-by: Qwen-Coder <[email protected]>

* refactor(managed-agent): narrow contracts and simplify test scaffolding

Co-authored-by: Qwen-Coder <[email protected]>

* fix(managed-agent): repair verification after module migration

Resolve shared fixtures and worker paths from the Maven module root, update CI packaging assertions, and align existing test fixtures with the extracted contracts. Restore restricted Workspace constructors and remove redundant test visibility changes without altering inherited MySQL coverage.

Co-authored-by: Qwen-Coder <[email protected]>

* test(managed-agent): align remaining Harness and OSS fixtures

Preserve SDK refusal messages and register the managed OSS adapter at fixture injection points. Keep the existing retry and client shutdown assertions intact.

Co-authored-by: Qwen-Coder <[email protected]>

* style(managed-agent): order Hosted cleanup Harness imports

Co-authored-by: Qwen-Coder <[email protected]>

* test(managed-agent): fix recovery smoke fixture and import order

Supply the required transcript coverage in the recovery browser fixture and wait for history before testing a complete handoff. Preserve its existing safety and submission assertions. Order the cleanup coordinator test imports for Checkstyle.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-10-30 14:56:35 +00:00
a8eb74fdf5 fix(web-shell): polish files and trim preview resources (#14143)
* fix(web-shell): polish files and trim preview resources

* test(cli): observe pending child Hook promises

* fix(web-shell): address Files browser review findings

* fix(vscode): sync Pierre modification notices

* chore: ignore the repo-local .pnpm-store fallback directory

pnpm falls back to a project-local store at .pnpm-store/ when the global
store directory is not writable (e.g. sandboxed CI runners). It is a
regenerable cache that node_modules hard-links into; its thousands of
untracked files otherwise dirty the working tree and fail deterministic
workspace-clean verification.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): address Pierre preview and file display review findings

- getPreviewLanguage folds case on the extension only, so README.MD and
  config.YML highlight while whole-name detector keys (Gemfile, BUILD)
  keep their case-sensitive behavior
- formatFileName strips the full Default_Ignorable_Code_Point class so
  zero-advance Hangul fillers and variation selectors cannot desync the
  tree search projection from the rendered name
- pin the previously unwitnessed pierre-build guards: redirected Shiki
  subpaths count toward the 4 MiB budget, getModuleInfo may return null
  for redirected ids, emitted assets are skipped, and entry-module
  resolveId calls carry no importer
- pin the theme name->module mapping with exact token colors and give
  the formatFileName display sites discriminating coverage with
  repeated-space names

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-16 16:45:56 +08:00
jinyeandQwen-Coder 28415ddff3 refactor(managed-agent): Consolidate server and broker sources (#14277)
* refactor(managed-agent): Consolidate server and broker sources

Move the combined Java application beside sdk-java and remove the standalone Runtime Broker artifact. Update build, development, packaging and CI references while preserving Java package identities, runtime protocols and schema resources.

Co-authored-by: Qwen-Coder <[email protected]>

* docs(managed-agent): Preserve historical evidence and verification guidance

Restore commit-pinned H3 probe inputs and align the Java verification environment contract with the consolidated server layout.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): Run hosted Broker tests under Bash

Keep Maven property arguments intact on Windows and stop before Broker tests when the SDK install fails. Exercise both successful and failed SDK installation in the workflow regression tests.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): Quote the hosted Broker shell value

Follow the repository yamllint single-quote rule without changing the parsed workflow configuration.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-10-16 00:28:58 +00:00
ytahdnand钉萁 df5470e430 feat(web-shell): Add workspace file browser and previews (#13878)
* feat(web-shell): Add workspace file browser and previews

* fix(web-shell): Restore source preview CI fixtures and notices

* fix(web-shell): Address file pane review blockers

* fix(web-shell): Resolve review regressions and strengthen file checks

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-13 09:38:47 +00:00
Shaojin Wen 692a5d7446 feat(managed-agent): Add the public API contract and contract tests (#12808)
* feat(managed-agent): Add the public API contract and contract tests

Move the reviewed Managed Agent OpenAPI into the server module as the
single source for the public and WebShell routes (v1.13), and check the
server and the WebShell client against it.

- A contract test compares the mapped Spring routes, the API records,
  and the real requests, responses, declared headers and SSE frames of
  every partial route with the spec. Current differences are recorded
  as expected failures that the later Stage D slices remove; a fixed
  gap that stays listed fails as well.
- The WebShell client types are generated from the spec without its
  planned surface, and a test fails when the committed types are stale.
- Rename, unarchive, archive and delete, which the server already
  serves, are recorded as partial routes.

Part of #12793.

* test(managed-agent): Cover archived reads and WebShell first input in the contract

Address review feedback on the contract test and its callers.

- The real-traffic scenario now reads an archived Session through get
  and list, and creates a WebShell Session with its first message. That
  records three more gaps: archived Sessions read back with status
  "archived", which the contract's enum lacks, and the WebShell create
  request carries the same "text" input type as the other D2 lines.
- A property that appears in several oneOf or allOf members counts as
  planned only when every member marks it planned.
- Replace the deprecated JsonNode.fields() calls.
- The managed-progress e2e spec builds a Session with agentId and
  events with data, as the generated types require.
- The design note records the gaps that the gap file cannot hold:
  undeclared error responses, the full-page has_more semantics,
  workspace-bound Sessions, and the merge order with W0d.

* test(managed-agent): Reject unknown gap categories and quiet the validator

Address the sandboxed verification of the contract test.

- Every line of the known-gaps file must start with a category that a
  test filters on. A line with any other prefix was never compared, so
  it could not be reported as resolved; it now fails every contract
  test and names the line.
- The validator treats keywords outside JSON Schema as annotations
  without logging, which removes the nine "Unknown keyword" warnings
  that loading the whole OpenAPI document produced on every run.
- The design note no longer says that no route changes status: archive
  and delete move from planned to partial, and none becomes implemented.

* docs(managed-agent): State exactly what openapi-typescript reads from supports-color

The override comment and the design note said the generator reads only
stdout.hasBasic, which 7.2.0 provides. It reads stdout and its hasBasic
flag; 7.2.0 exports the same stdout, false without a TTY, and the
truthiness check on stdout is what keeps colours off there.
2026-09-27 04:08:41 +00:00
易良andQwen-Coder 7837c6c200 fix(web-shell): verify wildcard export targets against the packed file list (#12364)
* fix(web-shell): verify wildcard export targets against the packed file list

verify-publish-artifacts.mjs statted every `exports` value as a literal
path, so a legal subpath pattern ("./*": "./dist/*") was reported as
`missing ./dist/*` and prepublishOnly refused a publishable package.

Pattern entries now skip the meaningless literal stat and are matched
against the npm pack file list instead: at least one packed file must
match the pattern, so a manifest pointing at nothing shippable still
fails closed. Literal entries keep the existing checks unchanged.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmua2h4ec9e

* fix(web-shell): gate wildcard export branch on the exports key, not the value

Node gives `*` pattern meaning only when the exports key carries it, so
deciding "subpath pattern" from the exports value silently certified a
manifest the base verifier refused: `{ "./foo": "./dist/*.js" }` resolves
to a literal path and every consumer hits ERR_MODULE_NOT_FOUND. Thread the
key through the flatten and gate the wildcard branch on
`key.includes('*') && entry.includes('*')` (two-sided, so a pattern key
with a literal target still gets the relative-import chunk scan), with an
explicit seen-set keyed on key+target text since `[key, value]` pairs
never dedupe by identity.

Tests: pin the regression with a literal-key/wildcard-value case asserting
`missing ./dist/*.js` (red before, green now, red again if the key check
is removed), and make the wildcard fails-closed fixture discriminating by
writing `dist/absent/sdk.js` — on disk but unpacked, so swapping the
packed-list membership check for a filesystem-existence oracle no longer
survives. Both mutants verified killed against this suite.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmua8wlpr9r

* fix(web-shell): match Node star cardinality in wildcard exports gate

R2-1: the wildcard gate tested whether `*` is present on both sides, but
Node honours a pattern key only when it carries exactly one `*`, and
substitutes that single capture into every `*` in the target. The gate now
requires exactly one `*` in the key, and the target regex captures on the
first `*` and back-references it for every later `*`. Witness fixtures
{"./*/*": "./dist/*"}, {"./*": "./dist/*/*"}, {"./*": "./dist/*/*.d.ts"}
and {"./*": "./dist/*/*.js"} all flipped exit 0 -> 1 (base refused all
four), while a packed dist/a/a.js still satisfies {"./*": "./dist/*/*.js"}
(exit 0), so the gate is not a blanket refusal of multi-star targets.

R2-2: the dedup comment now states the invariant the key half protects
(which branch a pair takes depends on the key, so two keys sharing one
target must both be checked), pinned by a fixture advertising one target
through both a pattern key and a literal key.

R2-3: a pattern key with a literal target keeps the literal chunk scan,
pinned by a fixture whose packed entry imports an unpacked nested chunk.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmuafc326a5

* fix(web-shell): require subpath keys and valid targets in the exports gate

The wildcard pattern branch counted the stars on the exports key but not
Node's other conditions for honouring a subpath pattern, and built its
regex from the normalized target:

- a `*`-bearing key that is not `./`-prefixed (`"dist/*"`, `"*"`) took
  the pattern branch and was certified publishable, where Node rejects
  the whole manifest with ERR_INVALID_PACKAGE_CONFIG for every specifier,
  the root import included; the base verifier refused it.
- a target that is not `./`-relative, or that carries a `.`/`..` segment,
  was matched after `join` normalized those segments away, so
  `./dist/../dist/*.js` was certified while Node throws
  ERR_INVALID_PACKAGE_TARGET for every subpath through that key.
- the positional `\1` emitted for every non-first star glued onto a
  following digit: `./dist/*/*1.js` compiled `\11` as the Annex B octal
  escape U+0009, leaving a pattern no packed path can satisfy and
  refusing a manifest Node resolves fine. A named capture cannot absorb
  the digit.

Also pins that the `*` capture may cross a `/`, which nothing in the
suite observed: narrowing it to `([^/]*)` left all twelve existing cases
green while refusing wildcard families whose only packed members are
nested.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmub7ww4cbn

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-22 03:56:13 +00:00
dvd233 733f58b6fc fix(web-shell): isolate npm pack diagnostics (#12367) 2026-09-22 02:42:12 +00:00
易良andQwen-Coder 46f6aebdc3 fix(web-shell): check publish artifacts against the npm pack list (#12343)
The `prepublishOnly` guard tested every export target and relative chunk
with `existsSync` against the working tree, so a file the build emitted but
`files` never published passed. `dist/*.js` does not cross a `/`, which makes
the fail-open case exactly the one the script's own comment warns about: an
entry point at `./dist/nested/index.js`, or a chunk in a subdirectory, is on
disk, is announced to consumers, and is absent from the tarball. A published
version cannot be replaced, so the guard has to answer for what npm ships.

Ask npm: `npm pack --dry-run --json --ignore-scripts` lists the paths that
would actually be packed. Membership is decided against that list, after
reducing both sides to the package-root-relative posix form npm reports
(no leading `./`). The existing `missing` / `which was not built` messages
are kept, and the packed-list problems are added alongside them.

If the pack list cannot be obtained the run reports it as a problem and
refuses the publish rather than skipping the membership checks.

Tests copy the real script into a throwaway package root and drive it the
way `prepublishOnly` does: the two fail-open shapes above now exit 1, an
export target that was never built still exits 1, and a fixture with the
shape the published package actually ships still exits 0.

Fixes #12310


Patrol-Run: qwen-issue-patrol/jmu9nh0028l

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-20 12:04:46 +00:00
易良 9e6d058b41 fix(web-shell): make the published package resolvable for consumers (#12188)
* fix(web-shell): make the published package resolvable for consumers

Three packaging defects were invisible until after `npm publish`, where a
version can no longer be corrected in place:

- Three UI components imported sibling primitives through the repo-only `@/`
  alias, and `tsc` copied that alias verbatim into the emitted declarations, so
  consumers could not resolve the field, alert-dialog and toggle-group types.
  Those imports are now relative and the declaration tree carries no `@/`.
- `fzf`, `@tanstack/react-virtual` and `@xterm/addon-fit` were declared as
  runtime dependencies although the library bundles inline them and no emitted
  declaration mentions them; they move to devDependencies. The packages the
  declarations do reference — `@xterm/xterm`, `@tanstack/react-table` and
  `@modelcontextprotocol/ext-apps` — stay declared.
- A failed type step leaves the shared dist without declarations while the
  exports map still announces them. A `prepublishOnly` hook now refuses to pack
  when an exports target is missing or a declaration imports through `@/`.

* fix(web-shell): keep bundled deps declared for the extension notices

Demoting `fzf`, `@tanstack/react-virtual` and `@xterm/addon-fit` to
`devDependencies` expires the companion's NOTICES.txt freshness check: that
file is generated from the companion's production dependency graph, and the
extension bundles this package's inlined code while shipping the notices in
the VSIX. Both `dist/extension.cjs` and `dist/webview.js` contain that code,
so regenerating would delete 79 lines of MIT notices for software that still
reaches users.

The three packages therefore stay in `dependencies`, and the lockfiles match
the base commit again. `npm run generate:notices` is a no-op at 659
dependencies.

* fix(web-shell): guard shared chunks and drop the last alias sibling import

`dialog.tsx` was the only component still importing a sibling primitive
through the repo's `@/` alias. Its button import is elided from the emitted
declarations today, so nothing leaks yet, but it is the same alias in the same
position and would leak the moment the button reaches a public type. All four
sibling imports are now relative.

The publish guard checked that `exports` targets exist on disk, which says
nothing about the chunks the bundles import by relative path: `dist/index.js`
and `dist/daemon-react-sdk.js` both pull in a hash-named chunk at the `dist`
root, published only because the file list globs `dist/*.js`. A chunk emitted
into a subdirectory would be announced by an entry point but never packed, so
the guard now refuses to pack when an entry's relative import was not built.
2026-09-19 09:57:54 +00:00
Shaojin Wenandqwen-code-dev-bot 6c962b3719 feat(web-shell): manage git remotes from the workspace branch picker (#11163)
* feat(web-shell): manage git remotes from the workspace branch picker

The workspace git popover could pull, commit, push and switch branches,
but the remotes themselves were read-only context: a user who clones from
a zip (no origin), works triangular (fork + upstream), or wants to drop a
stale remote had to leave the Web Shell for a terminal.

Adds a Manage Remotes panel inside the popover — list, add, remove —
backed by new workspace-scoped daemon routes and the SDK methods for
them. The listing reads git through its structured accessors rather than
the rendered "remote -v" output, which git annotates for partial-clone
remotes and which a line parser silently misreads. Mutations answer with
the fresh list so the panel re-renders in one round trip, and a remove
also refreshes the branch listing and the tracking state of the chip,
because git deletes the remote-tracking refs and upstream config together
with the remote.

Names and URLs are validated before git is spawned (flag injection,
refname rules for add; removal stays as lenient as git itself so a
hand-edited config can always be cleaned up), and rendered through a
display sanitizer that strips the invisible-character set — a git config
the user did not author can carry bidi marks that spoof the displayed
URL.

Design doc: docs/design/git-manage-remotes.md

* fix(web-shell): harden git remotes management after review audits

The config-scope read, removal verification and client refusal handling
carried gaps the reverse audits surfaced: include.path/worktree scopes were
under-listed yet un-removable (destructive dead ends), GIT_ALLOW_PROTOCOL
was deleted outright (a fail-open swap against config-file policy), the
classifier let echoed names/URLs steal config-write failures, and several
guards shipped without witnesses. Fixes:

- list the repository-owned scope via `config --list --show-scope -z`
  (local+worktree), completing worktree-scope removals git cannot edit
- normalize an inherited GIT_ALLOW_PROTOCOL (strip ext/fd) instead of
  deleting it; keep deny-by-default force
- order sendGitError's anchored config-write shapes first; add
  remote_config_unparsable for unparsable configured refspecs
- follow git's boolean/integer grammar exactly (valueless/empty/hex/kmg)
- keep the URL visible under row shrink; restore focus after in-flight
  mutations; disarm confirms on refusal; silent re-reads stay silent
- witness every guard above in unit/e2e suites; sync the design doc

* fix(web-shell): close remotes edge cases from the second review round

Seven Critical threads from the review bot's second batch, all fixed with
witnesses and a ten-round reverse audit on top:

- add pre-flights all config scopes and refuses a same-name inherited
  section (remote_shadows_inherited), after probing repository-ness;
  removal verifies resolution across every scope so an inherited survivor
  can never be reported as removed (fail-closed on killed reads)
- sendGitError shapes match git's message start only (line 1, or git's
  two-line lock chain): config-chosen names/URLs can carry any keyword,
  and config values can carry real newlines, so deeper line-initial text
  is attacker-controllable; classification reads the full redacted detail
  while the client message stays bounded
- killed config reads rethrow with the all-scope stdout dump stripped
  (stderr diagnostics preserved) on every read path
- focus restores: add submit button, remove-row button when focused,
  back-button fallback; restores are gated on lent focus, cleared on view
  exit and workspace switch; refused removal awaits the silent re-read so
  the restore lands on the converged list
- no_such_remote remove refusals also refresh branches and status

* fix(core): harden git remote removal verification and upstream cleanup

- iterConfigRecords: shared NUL-framed --show-scope record walk for all
  four config-read consumers
- promisor bool grammar: match git's strtoimax exactly (C-isspace
  leading class, attached sign, hex/octal/decimal, case-insensitive
  k/m/g, [INT_MIN, INT_MAX] bound)
- removal: pre-removal pointingBranches snapshot (worktree-over-local,
  last value) so mixed-scope upstream keys stay attributable
- removal: sweep worktree-scope branch.<b>.remote/merge/pushRemote and
  remote.pushDefault that git's rm cannot write (--fixed-value
  --unset-all for value-matched keys), re-verify and refuse
  remote_still_configured
- removal: refuse when an upstream key still resolves to the removed
  remote from a file this module will not edit (include.path'd file or
  inherited global/system scope, incl. rm-unmasking), resolved with
  git's effective last-value-wins semantics
- removal: converge a retry after a failed cleanup on the
  no-such-remote path instead of dead-ending

* fix(web-shell,cli): close remotes edge cases from the fourth review round

- core: never fire the worktree completion when git died BEFORE
  mutating — the refusal phrase matches at a line start only, and an
  error:/fatal: invalid refspec at any line start vetoes it outright
  (a config-chosen refspec value can carry the phrase on a deeper
  line); the refusal keeps its remote_config_unparsable answer and the
  row stays
- cli: redact gitdir paths git echoes outside the workspace tree — a
  linked worktree's shared main .git dir, a submodule's gitdir, a
  relocated admin dir (via the commondir file), the symlink-canonical
  spelling of each (git realpaths at setup), and over-long targets
  (head-bounded reads, truncated lines redacted as prefix tokens)
- web-shell: IME-owned Enter guard on the add form (isComposing /
  keyCode 229, both inputs)
- web-shell: whitespace-lookalike marking — names/URLs differing only
  by whitespace (CSS-collapsed) now flag, with escapes in the tooltip
  and aria-labels; the search filter collapses both sides to match the
  inked text
- web-shell: sticky stash warning survives the remotes round trip —
  snapshot on entry, restore on back/Escape/dismiss, busy-gated so a
  settling mutation's footer wins, a newer standing warning always
  outranks a held snapshot, and re-entry never nulls a held one
- web-shell: focus capture/restore resolves from the popover content's
  own root (shadow-portal embedding), and the settle restore bails
  when the content is unmounted mid-mutation (no cross-instance focus
  theft)

* fix(core,web-shell): close remotes edge cases from the R4 review batch

- core: sweep orphaned refs/remotes/<name>/* after a certified removal
  (git's rm deletes them only through a parseable fetch refspec), with
  --no-deref so a planted symref can never delete its target, plus the
  no-such-remote converge arm so a retry after a failed sweep does not
  dead-end; re-verified, refusing remote_still_configured on a survivor
- core: extraPushUrls falls back to the url list when no pushurl exists
  (git pushes to every url then) so the badge no longer under-reports
  git's push fan-out
- cli: route suite gains strict-mutation-gating (per-call + count),
  mid-flight generation close → 503, runtime-env threading, both lock
  chains, the ancestor-walk ceiling, and the host system/global config
  precondition guards
- web-shell: armed remove confirm disarms on filter-out / Escape /
  add-submit; the popover mock now unmounts on close and dismisses on
  an un-prevented Escape (Radix DismissableLayer parity); Escape events
  in the witnesses are cancelable
- web-shell: a successful removal no longer awaits the branch refresh
  (busy/focus released immediately); a successful add clears the
  remotes filter so the new row is visible; the removing row shows a
  spinner; the armed confirm's aria-label names the consequence badge
- web-shell: search matches the extras badge text and sanitizes the
  needle; the push/fetch tooltip labels are localized (en + zh-CN);
  the e2e primary journey carries @smoke
- sdk: workspaceGitRemoteAdd/Remove accept an optional per-call
  timeoutMs; the panel passes 600s (the mutation chains git spawns
  that each carry their own 30s budget)
- docs + PR body updated to the shipped implementation (config-scope
  reads, WRITES-vs-RESOLVES scope split, six classifier branches)

* fix(core,cli,web-shell): close remotes edge cases from the R5/R6 review batch

- core: refuse include-held/split remote sections BEFORE spawning
  `git remote remove` (git rm deletes the tracking refs and upstream
  keys before failing on a section it cannot write — every retry
  destroyed again); record origins are resolved against the worktree
  toplevel (git chdirs) with both literal and realpath spellings, and a
  killed probe refuses instead of answering blind
- core: the upstream-key sweep now covers local scope too and matches
  multi-valued keys by exact value (`--fixed-value`), since git's rm
  skips multi-valued keys with a warning; the re-verification pins only
  value-matched keys (an include-held inert merge key must not refuse a
  completed removal)
- core: the survivor gate gains the unmask case — a pointed branch or
  `remote.pushDefault` whose shadowing record the removal unset must
  not fall back to an inherited record naming a remote with no section
  (the pre-removal snapshot proves causation), while git's sectionless
  forms (`.`, URLs, scp-like, local paths) and pre-existing dangling
  values never refuse
- core: promisor values are resolved by the host git itself
  (`--type=bool --get-all`, additive like git's registration) instead
  of an in-module bool grammar whose integer bound tracks a moving git
  target; a promisor record at any scope marks the badge, and a killed
  badge read rethrows stripped instead of answering false
- core: Apple Git's runtime-prefix defaults file reports scope
  `unknown`; the record walk recognizes it so the verification folds
  stay sound (the listing and sweep still filter to repository scopes)
- cli: redaction covers the inherited config files git echoes by
  absolute path ($HOME/.gitconfig, the XDG config — including empty-XDG,
  no-HOME and trailing-slash verbatim-concatenation spellings — and the
  build-time system gitconfig), NUL-truncated gitdir AND commondir
  pointer targets, head-truncated targets keyed on the NUL-normalized
  form, and the bad-config-line / unable-to-access payload shapes at
  arbitrary include.path locations (transport URLs are not config
  targets and stay verbatim)
- web-shell: the popover installs the house preserveImeEscape mask
  while open, so a composition-cancelling Escape no longer tears down
  the remotes view nor swallows the native IME cancel (the popover mock
  now delivers Escape from document-capture like Radix's
  DismissableLayer)
- e2e: remotes spec selects on data-testid instead of hashed
  CSS-Modules class substrings
- kill suite: the mock chains pin exact call counts (a retargeted kill
  read can no longer pass silently)

* fix(core,web-shell): close the slash-namespace and render-identity gaps

- core: isRemovableRemoteName now refuses names containing `/` — a
  slashed remote's tracking namespace is a subdirectory of the prefix
  remote's (refs/remotes/origin/staging/* lives inside
  refs/remotes/origin/*), so no post-removal sweep can tell the remote's
  own refs from the prefix remote's branch refs, and a never-configured
  slashed name would 404 only after the converge-arm sweep destroyed
  them; the panel answers 400 before anything runs and the terminal
  remains the tool for the shape
- web-shell: the remotes row identity check gains the two structural
  arms the strip list cannot enumerate — a name that is not NFC (a
  canonical twin inks identically) and a Latin name mixing in another
  script's letters (the Cyrillic-о homoglyph shape) — both marking the
  unusual row with its distinguishing characters escaped in the tooltip
  and aria-label, while a legitimate non-ASCII name stays unmarked
- docs: the design record's absolute render-identity claim and the
  remove-route leniency claim are narrowed to the shipped mechanisms

* test(core): pin worktree-over-local upstream attribution on removal

* fix(cli,web-shell): bound the loose classifier keywords and keep git's sentence boundary

- cli: the legacy loose keyword branches (dirty / already exists / no
  upstream / …) match the CAPPED 512-char slice again — an unbounded
  scan reclassified long push/pull output whenever a path or URL past
  the cap carried a keyword (a `dirty-cache.git` push rejection
  answered 409 dirty_working_tree); the anchored remote branches keep
  the full detail for the two-line lock chain
- web-shell: git error text in the single-line footer now collapses
  whitespace before stripping the invisible class — a stripped newline
  fused git's two-line lock chain into one unreadable sentence

* test(cli): allow the redaction's process-scoped HOME/XDG_CONFIG_HOME reads

* fix(core,cli,web-shell): close the R9-batch removal integrity gaps

- core: the upstream-key sweep now reaches a multi-valued key's
  NON-effective entry naming the removed remote (residue whose later
  surfacing would dangle), and the merge key is swept only when the
  same scope's remote key holds no surviving entry — a branch that
  keeps its upstream keeps its merge
- core: the tracking-refs sweep resolves ownership by longest-prefix
  against the configured remote set (a configured slashed sibling's
  namespace is no longer taken down with the prefix remote) and covers
  the exact bare ref refs/remotes/<name>
- core: removal certification asks git's own resolver
  (`ls-remote --get-url`, documented not to contact the remote), so a
  legacy $GIT_DIR/remotes/<name> file or an insteadOf alias can no
  longer keep a "removed" name fetchable
- core: the sweep now walks every linked worktree's config.worktree
  (the invoking worktree's reads never see siblings' files), lazily,
  fail-closed per worktree
- core: the pre-removal snapshot carries per-branch local value lists,
  and a local [branch] section git rm destroyed while its shadowed copy
  named a SURVIVING remote is restored — a worktree-shadowed fallback
  upstream is no longer collateral damage
- cli: the unable-to-access redaction arm accepts drive-letter absolute
  paths (the Windows lane runs the redaction suite)
- web-shell: a remote URL carrying non-ASCII bytes fails closed — URLs
  are an ASCII-only surface, so the row marks and the tooltip spells
  the homoglyph out

* fix(core,cli,web-shell): close the R10-batch removal and confusables gaps

- converge arm: ask git's own transport for the bare-word path leg
  (bundles resolve; --resolve-git-dir called them too large), skip the
  sweep for empty pushInsteadOf aliases, re-verify the ref sweep, and
  carve win32 backslash spellings as sectionless values
- unmask gate: push-side arms count pushurl records and pushInsteadOf
  aliases; the path leg answers through git's transport
- pre-flight: inherited refusal only when a repository half exists to
  destroy; union gate and worktree completion keep their race backstops
  (kill-suite witnesses)
- redaction: linear /etc/gitconfig token arm, open-family in-file arm,
  fail-closed absolute-path sweep with the apostrophe-tail fragment drop
- confusables: table-first fold with NFC closure at generation time,
  symmetric all-ASCII collision marking, skeleton as a search target,
  U+2028/2029 escaped emission, self-map and empty-alias guards
- design docs: bilingual zh-CN twin plus the round's gloss corrections

* ci: run the platform lanes for the git remotes path handling

The win32 path-spelling carve and the local path-transport probes behave
differently per host and no POSIX-only lane can tell whether they work on
Windows; classify the two git-remotes files platform-sensitive (exact
paths, not a keyword) so both lanes see future changes to them.

* test(core): write the slashed sibling section instead of remote-adding it

git newer than 2.50 refuses `git remote add a/b` over an existing `a`
("remote name 'a/b' is a subset of existing remote 'a'"), which killed
the macOS and Windows lanes in fixture setup. A config-written section
is git-version independent and models the hand-edited config the lax
removal predicate exists for.

* Revert "ci: run the platform lanes for the git remotes path handling"

main retired the platform-lane pull_request trigger and froze
classify-platform-sensitivity.mjs untouched so the future restoration
revert stays clean (ci.yml comment). The GIT_PATH_HANDLING entries
extended a script no CI path invokes — the lanes actually ran through
workflow_dispatch event gating — and broke that freeze.

* fix(core,cli,web-shell,docs): close the R11 confusables split and deferred batch

- web-shell: the generator closed prototypes under NFC + table only,
  but the runtime fold also NFKC-folds table-absent halves, so 29
  emitted values split one ink-identical class across two skeletons
  (R11-2). Close every value under the exact consumer fold map-wide,
  delete self-maps inside the convergence loop so the fixed point is
  computed against the final map, post-verify the invariant, and
  regenerate (29 values, witness: U+1D52 vs U+00BA).
- core: escape the NUL separator in pushKey (a raw NUL byte made grep
  and ripgrep treat the module as binary); rename the misnomer
  repository-scope describe.
- cli: readHead opens workspace-controlled paths synchronously on the
  shared error path; a FIFO commondir wedged the daemon event loop.
  Guard on isFile (FIFO witness, POSIX-gated).
- web-shell test: replace the vacuous cross-workspace focus assertion
  with an equality on the pre-settle active element.
- docs (en + zh-CN): consumer-fold closure clause, ls-remote probe
  spelling, orphaned bullet fragment repaired, §6 witness rows.

* test(core): retry the tmp-dir teardown on Windows EBUSY

The windows lane lost an otherwise-passing not-a-repo test to a
transient EBUSY on the afterEach rmdir (a handle held by the indexer
or an exiting git child). Retry the teardown a few times instead of
failing a test whose assertions already passed; non-EBUSY errors and
exhausted retries still throw.

* docs(web-shell): state the consumer-fold example's mechanism exactly

The `º/₀` halves NFKC to `o/0`; the capital O comes from the per-half
table chance (0030 -> 004F), not from NFKC. The result string was
right, the stated mechanism was not — anyone re-predicting a fold
whose NFKC half is itself a table key would mis-derive it. Also name
the tooltip-escape consumer at the delete arm and print the code
point (not the raw char) in the fixed-point diagnostic.

* fix(core,web-shell,docs): close the R13 Criticals and their audit fallout

- R4-3: widen EXECUTING_HELPER_URL to admit git's empty-name helper
  form (`::payload` execs a PATH-resolved `git-remote-`); rationale
  corrected — the empty name is not on git's deny-by-default list, and
  a protocol.allow policy that would cover it is overridable from
  config files. Witness rows `::sh -c id`/`::0`/`::`; mutation of the
  pattern turns them red while the IPv6 literals stay accepted.
- R12-1: restore the local upstream backups at the TOP of the
  removal-failure path (above the !completed throw and the converge
  classification), so a refusal following an already-destructive rm
  cannot skip the rollback; a killed restore read rethrows instead of
  letting git's original answer surface un-rolled-back (kill witness
  pins the restore read's argv, separating it from the converge gate's
  scope read).
- R13-2: the converge arm excludes the removed name's own
  namespace-less top-level bare ref from sweep AND re-verify while
  keeping the longest-prefix ownership resolution whole — a bare ref
  exactly owned by a configured slashed sibling keeps that owner
  (first attempt at this fix regressed exactly that; witnesses:
  remote-HEAD symref, flat layout, slashed sibling bare ref).
  Accepted residuals disclosed: a formerly-configured name's bare-ref
  residue is orphaned, and a flat layout's slashed branch refs stay
  sweepable.
- R13-3: mark BOTH rows when a collision group's skeleton is non-ASCII
  and the variance is a table fold rather than canonical equivalence
  (skeletonAscii/allCanonical on the group); witness öö × ةة; the
  all-ASCII and pure-canonical polarities keep their pinned behavior.
- Audit fallout: the TR39 fold moves to
  client/utils/remote-name-skeleton.ts beside its table integrity gate
  (no self-maps, no value containing a table key, key folds to value,
  value is a fold fixed point, NFC); the non-discriminating
  lowercase-spoof completion test is removed (the exact-prefix test
  carries the veto witness); design docs synced EN+ZH for the third
  marking polarity, the error-path restore and its fail-closed
  masking, the converge residuals, and the new test rows.

* fix(core,cli,web-shell,docs): close the R14 Criticals and their audit fallout

- R14-2: the TR39 fold iterates to a fixed point (capped) instead of
  running one pass — the closing NFC can compose a table key and a
  decomposed spelling only meets the table after composition; new
  remote-name-skeleton.ts carries the fold beside its integrity gate
  and fixed-point corner tests.
- R14-3: redaction gains a quoted-path arm (git's die(_('%s' …))
  family owns a quoted space-bearing payload whole) plus a slash-
  carrying fragment arm for apostrophe-bearing paths.
- R14-4: tracking-ref ownership skips refs inside a SURVIVING remote's
  fetch-dest namespace (NUL-framed get-regexp read), on sweep and
  re-verify alike; closes the flat-dest slashed-branch residual.
- R14-5: the restore's presence read is NUL-framed, so an empty value
  reads as present instead of absent (no more doubled pushDefault).
- R14-6: isSectionlessUpstream narrows to `.`/colon-bearing values;
  dangling slashed/backslash upstreams now refuse like their bare-word
  twin; win32 UNC keeps a carve (network transport, offline-share
  timeout); existing paths still certify through the path probe.
- R14-7: the post-refusal branch/status refresh runs in the same
  synchronous block as the staleness guard, ahead of the awaited
  re-read, so a mid-await workspace switch cannot seed the new
  workspace's panel with the old closure's data.
- Audit fallout: stale comments corrected (sectionless shapes, flat
  dests, probe classes), win32 UNC carve, NUL-framed dest read without
  the speculative JSON.parse arm, prettier-clean test files, and EN/ZH
  design-doc sync for all six changes plus matrix/Files-affected
  attribution of the new skeleton module and its tests.

* fix(core,web-shell,docs): close the R15 Criticals and their audit fallout

- R15-1: foreign-namespace roots derived per wildcard position; mid-
  wildcard dests (refs/remotes/*/main, or*/main) fail closed. The
  reverse audit caught an unsatisfiable star-position conjunction that
  disabled the orphan sweep in any repo with another default-refspec
  remote; fixed and pinned by an origin + refspec-less orphan witness.
- R15-2: the sibling-worktree sweep verifies the sibling shares this
  repository's common dir before any read or write, so a planted
  .git/worktrees gitdir can no longer receive its unset writes;
  planted-gitdir witness, real-sibling tests unchanged.
- R15-3: the remotes search folds the needle in three case forms (the
  TR39 table is case-sensitive); Istanbul/lstanbul witness.
- R15-4: the fold is invariant under canonical equivalence — two-level
  decomposition fallback at runtime, generator closure over NFD(key),
  table regenerated (6564 entries, U+021A dropped, 27 values re-classed,
  lunate sigma preserved); full invariance sweep pinned.
- Deferred-triage fixes: hermeticity precondition regex, win32 UNC
  short-circuit witness, worktree-completion kill witness, dash-URL
  client-guard witness, NFKC-fallback pin, timeout-ceiling and
  mixed-scripts comment accuracy, zh predicate-bullet and UNC doc
  parity, pushl marker-comment accuracy.

* refactor(core,web-shell,docs): land the R15 reverse-audit follow-ups

- The sibling sweep skips the invoking worktree BEFORE the ownership
  probes, so a repo whose only worktree record is its own pays no
  probe spawn (just the one lazy toplevel read, reused per sibling).
- The killed-sibling witness is re-aimed at the new spawn order and
  pins the kill to the sibling's config.worktree read; under the old
  queue the kill landed on an ownership probe while the test passed.
- The remotes search dedupes its case-folded needles and folds each
  row's ink once instead of once per needle variant.
- EN design doc: two stranded fragments repaired; ZH counterpart gets
  the missing subject in the marker sentence (parity).

* test(web-shell): route the remotes e2e through the post-#11700 picker entry

Main's context-overview rework removed the sidebar git pill and the
welcome-view branch chip this spec opened the picker through; the
merge-head smoke run failed waiting for a pill that no longer renders.
The spec now enters the scenario session and opens the picker from the
environment panel's branch row — the same route the branch-picker
visual spec uses — keeping every assertion unchanged.

* build: raise the tsc build heap ceiling to 4096 MiB

A fresh `tsc --build` of packages/cli on the merged tree peaks just
above the 3072 MiB ceiling: two consecutive CI Lint runs and a local
clean-tsbuildinfo build all OOM at ~3.0/3.1 GiB, while each parent tree
alone stays under the ceiling and incremental builds never showed it.
Workspace build children inherit NODE_OPTIONS from the root build
script, so raising it there is what fits the union program; the test
ceilings are untouched.

* test(web-shell): pin the remotes e2e to the scenario branch and armed state

- openBranchPicker derived its click target from a hardcoded branch
  name while accepting scenario overrides; it now reads the scenario
  branch, pinned by the search test whose fixture switches to feat/x
  (RED without the derivation).
- The smoke test asserts the armed Confirm label between the two
  remove clicks, so a single-click regression fails at the witness
  instead of as a second-click timeout; pinned by a temporary
  single-click mutation that turns the assertion RED.

* fix(cli): move the git routes off the core barrel per the #11798 rule

Main's new lint rule bans new cli value-imports from the core package
root; the remotes and branches serve routes imported their symbols from
the barrel. Point them at the defining core modules (utils/git-remotes,
utils/git-branches, utils/gitDirect, utils/gitUtils) and drop the
allowlist entry the branches route no longer needs, per the list's
shrink-only contract.

* fix(core,cli,web-shell,docs): address R16 review and convergence-audit findings

- Refuse removal up front when a repository-scope section's name stays
  push-resolvable through a url.*.pushInsteadOf prefix: a
  post-destruction refusal wedges the upstream keys the certify sweep
  can no longer reach (the retry's 404 converge arm skips over the same
  alias. Six shape witnesses: local-section, strict-prefix,
  inherited-only 404 doctrine, worktree scope, and the included- and
  inherited-refusal ordering ahead of the alias refusal.
- Convert the truncated-gitdir redaction from an unbounded \S* regex to
  a linear per-token replace with identical semantics.
- Picker collision groups key on the casefolded skeleton; a per-pair
  case-twin arm marks case-only pairs at any script (a non-ASCII third
  member no longer disarms an ASCII pair); U+FFFC/U+FFFD placeholder
  names mark with the (lookalike name) copy; every arm and polarity
  carries a mutation-verified witness.
- Drop the core barrel re-export of utils/git-remotes.js: its only
  consumer deep-imports the module per the #11798 rule.
- Sync EN+ZH design docs to the four structural arms, arms 1-4 witness
  attribution, the six pre-flight witnesses, and the documented
  fetch-side wedge as a known house limitation.
HEREDOC
)

* fix(cli,core,docs): address R17 verification findings and convergence-audit findings

- M6/M8 (four-time survivors): the bounded-slice test now pins the
  not-a-repo keyword family past the 512 cap (deep-cap probes for
  `not a git repository` and `invalid reference`), the `invalid
  reference` alternation half gains its positive table row, and a new
  table test pins the line-1 anchor of the remote-already-exists arm
  (a line-2 occurrence falls to the loose keyword arm). Each pin is
  mutation-verified: its mutant reddens exactly that test while every
  sibling stays green.
- The inherited-scope-alias wedge (a global/system/unknown
  pushInsteadOf alias prefix-matching a listed repository-scope
  section) gains its repo-suite witness, and the pre-flight comment
  plus both design docs name it, including that the 409 names no alias
  at any scope so the UI never shows the cause (fail-closed, terminal
  is the only escape).
- Verification-bot dispositions: the wire-visible distinct error code
  for the pre-flight refusal is deferred as a follow-up; the leaked
  HEREDOC commit-message trailer is declined (fixing it would require
  rewriting published history).

* fix(remotes): close R18 review findings and local audit findings across rollback, converge and fold

R18-1: the ambient named-section guard matched across lines
(`[^.]+` spans newlines), so a host `remote.pushDefault` read as a
named section and skipped the whole route suite; exclude the newline,
stop the scan at the `=`, and pin the line boundary both ways
(two-component pushDefault plain / followed by a dotted key / carrying
a dot in its value must not match; dotted section names must).

R18-2: the no-such-remote converge arm ran the certify path's
destructive sweep without any unmask gate, so a removal could destroy
the local shadow of a dangling inherited upstream and still answer
404; run the snapshot-scoped half (sweptUpstreamResolving re-verify
plus unmaskedPointedUpstream) after the sweeps, keeping git's 404 for
inherited survivors the snapshot never pointed at.

R18-3: an include-held value equal to the removed name blocked the
collateral-damage restore; the new discountGate discounts it from the
presence reads when the section is gone from every scope AND the name
no longer resolves at all — resolver probe (legacy $GIT_DIR/remotes
file, insteadOf alias) plus local-path probe (same-named directory
repo or bundle file; a sectionless name short-circuits before the path
probe, which would otherwise put a scp-like spelling on the network),
and a probe that cannot answer yields no-discount inside the gate
rather than aborting the rollback ahead of the merge arm. The discount
spans the remote, pushRemote and pushDefault presence reads; the
merge pairing reads the undiscounted presence; the exit-0
split-section path restores a second time after its worktree-half
completion so the gate never sees a half the completion removes.

R18-4: isSectionlessUpstream treated any colon as a network transport,
failing the unmask gate open over colon-after-slash local paths;
apply git's colon-before-first-slash rule, keeping the dot and win32
UNC carves.

R18-5: an invisible character split the skeleton collision group
(group key folded the raw name while the search folded the sanitized
one); key the group, member flags and render lookup on the sanitized
fold, keeping the polarity arm on the raw name.

R18-6: the settle effect restored mutation-start focus without
reading settle-time focus, yanking focus out of a control the user
moved to mid-flight; record the start element and skip the restore
when in-content focus moved elsewhere.

R18-7: the skeleton fold was not invariant under canonical
equivalence (positional table walk before the closing NFC);
canonicalize at the entrance (NFC is a canonical function) and cover
fused code points with a longest-decomposed-prefix table lookup,
mirrored in the generator so the table stays closed.

Round-7 audit additions on top of the R18 fixes: the discount gate's
local-path leg and its probe-failure carve-out (a live directory-repo
or bundle upstream must not read as dangling residue; a wedged
resolver must not abort the rollback), the sectionless-name
short-circuit, the split-section second restore, the pushDefault
arm's dead conjunct, and witnesses for each: directory-repo and
bundle no-re-point pairs, probe-failure merge restore, pushRemote
write-back, split-section completion write-back, and the kill-suite
pin that a scp-like removed name never reaches the path probe.
Rounds 8-9 closed the remaining witness gaps: the pushurl-only
section scope-leg witness (the fetch-side resolver and the path probe
are both blind to a pushurl-only section), the kill-suite pin that a
killed gate probe yields no-discount while a later gate surfaces the
kill, the split-section witness now asserting certification, the
sanitized render-lookup witness (the skeleton-escape tail spells the
fold-covered code point), the pushDefault write-back comment
re-attributed to the unmask arm that actually refuses its fixture, and
the generator's vestigial NFD argument removed (old and new generator
produce byte-identical tables). Accepted residuals recorded in code
comments and both design docs: merge-key re-sweep on late refusals,
the sibling-worktree blind spot of the gate's section leg, the
swallowed discount when a surviving `[branch <b>]` section takes an
in-section insert ahead of a later `[include]` directive, the ambient
guard's `=`-in-name miss, and the six formerly-unified ink classes of
the regenerated table.
Mutation-verified: each mutant reddens only its target witness.
Rounds 10-11 closed the last witness gaps and comment drift: the
win32 drive-letter fail-open in isSectionlessUpstream (a drive-letter
prefix is a LOCAL path on win32 — the carve is win32-gated — while
every other platform reads it scp-like ssh, so the colon rule answers
it sectionless and keeps every probe off the wire; witnessed per
platform, with zero ssh spawns on POSIX), the converge-arm comment's
false claim that a legacy file never reaches the arm (git's rm exits 0
over it, so the resolver leg is what skips the sweep on a retry), the
per-arm sanitized member-flag witnesses (all-ASCII-only and
case-twin-only), the inventory rows' three omitted witnesses, the
comment counts ("Three refusal sources", "One gate decision per
pass"), the win32 drive-letter carve's missing no-separator spelling
(has_dos_drive_prefix needs only letter plus colon, so `C:x` probes
locally on win32 too), the merge-churn residual's code-comment record
at the post-certification cleanup, and the generator paragraph the
entrance-NFC rewrite orphaned (the ink-equal-value phenomenon is
systematic — 1010 of 6564 values — and its example is now a real
composition exclusion, U+FB30).
Rounds 13-14 rebuilt isSectionlessUpstream as a faithful mirror of
git's url_is_local_not_ssh: has_dos_drive_prefix (any non-NUL ASCII
character, or one whole non-ASCII code point, plus a colon), the
is_valid_win32_path conjunct (NTFS-forbidden characters, reserved
device-name segments, segments ending in space or period, and the
whole-path trailing-dot magic), and forward-slash UNC — a drive-letter
url whose tail NTFS rejects is routed by git to ssh, so probing it
would spawn ssh from a config string; the predicate is exported and
unit-tested under a stubbed platform (original descriptor restored),
because the win32 legs are not executable off-win32. Both design docs
enumerate the win32 classification correctly, and the accepted
residuals are numbered one to four.
Round 15 closed the last predicate divergences against git's
url_is_local_not_ssh / is_valid_win32_path: reserved device names match
as git's PREFIX rule (terminated by end, `.`, `:`, or a separator,
after optional spaces; LPT accepts any digit, COM 1-9), the
per-segment trailing-period rule exempts all-period segments of
length <= 2 (`C:/..` and `C:/.` are valid local paths, not ssh), and
the colon/slash legs are evaluated before the drive-prefix leg exactly
as git's expression orders them; the unit table covers the divergent
shapes and three mutants (old reserved regex, reordered legs, old
period rule) each redden only their unit tests.
Recorded deferrals from rounds 15-17: the per-pass gate memo is a
latency-only guard with no behavioral witness (spawn count, never
outcome); core.protectNTFS=false on win32 makes git read reserved-name
drive tails as local while the predicate calls them sectionless
(fail-safe direction, explicit opt-out only, same family as the UNC
over-approximation); and 1024 of the 6564 emitted confusables keys are
not NFC fixed points and therefore unreachable under the entrance NFC
(dead weight, behavior-preserving to drop). Round 17 also added the
kill-suite witness for the discount gate's scope leg sitting outside
its try (a killed scope read aborts before any destructive cleanup,
while a killed probe leg answers no-discount inside the gate).

Deferred follow-ups (recorded, not dropped): the merge key a refusal
landing after unsetUpstreamKeys can re-sweep when the local remote
key does not stand (include-held residue on a pointed branch); the
sibling-worktree blind spot of the discount gate's section leg; the
swallowed discount when git's rm leaves the local `[branch <b>]`
section standing and an `[include]` directive sits after it; the
ambient guard's `[remote "a=b"]` miss; the race-only pre-existing
TOCTOU window between the union gate and the tail surviving-keys
gate; the six formerly-unified ink
classes of the regenerated confusables table; the remoteInkKey helper
unification; the equal-combining-class skeleton limitation; the
converge-arm duplicate dump spawn; a wire-visible error code for the
pre-flight refusals.

* fix(remotes): certify removals push-side as well as fetch-side

The removal certification's resolver leg answered fetch-side only:
`ls-remote --get-url` applies insteadOf and never sees
url.<base>.pushInsteadOf, the rewrite git uses when pushing — a name
kept resolving push-side by an alias racing in after the pre-flight
read was certified as no longer resolving. The union gate and the
discount gate now read the push-side alias prefixes from the same
all-scope dump their section legs already take, at zero extra spawns
(the kill suite's spawn accounting is unchanged): a name kept
push-live refuses 409 remote_still_configured, and a residue equal to
a push-live name is not discounted into a silent re-point. The
pre-flight owns the steady-state shape (a section plus its alias
refuses before destruction; the converge arm skips the same alias on
retries); these legs are the backstops for the race window.
Witnesses: the union-gate push-alias refusal and the discount-gate
push-alias no-discount kill-suite cases (each mutant reddens only its
witness); the docs state the push-side leg in both languages.

---------

Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-09-17 13:40:47 +00:00