Commit Graph
9203 Commits
Author SHA1 Message Date
imccyu 226d829a1b Merge branch 'master' into codex/goal-clear-single-flight 2026-08-01 00:08:00 +08:00
Turtle cb1a6c74dc Merge remote-tracking branch 'origin/master' into fix/checkout-workdir-prompt
# Conflicts:
#	packages/ui/app-boot/README.i18n.yaml
#	packages/ui/app-boot/README.md
#	packages/ui/app-boot/README.zh.md
2026-08-01 00:04:49 +08:00
Turtle 2f92cb7b3c Merge remote-tracking branch 'origin/master' into feature/installer-adopt-checkout
# Conflicts:
#	README.i18n.yaml
2026-07-31 23:52:11 +08:00
Turtle c435350561 docs(install): the path bug is symlinks, not /var
The comments and Agent Note blamed macOS resolving /var through
private/var. That is only how the mismatch surfaced in temp fixtures, since
mktemp hands back a /var path there.

The real condition is broader: Git always reports resolved paths, so
comparing one against an unresolved path disagrees whenever a symlink sits
anywhere above the checkout. A symlinked home directory alone triggers it —
reproduced with no /var involved — which is common wherever homes live
behind a symlink or on a network mount.

Naming the cause correctly keeps a reader from dismissing resolve_dir as
macOS-only defensiveness.
2026-07-31 23:47:58 +08:00
Turtle 6792a12d8c Merge remote-tracking branch 'origin/master' into perf/tui-resume-scan
# Conflicts:
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/ui/tui/README.i18n.yaml
#	packages/ui/tui/tests/tui.spec.ts
2026-07-31 23:42:54 +08:00
Turtle db432c1e74 fix(install): correct a false claim about shell assignment semantics
Review flagged the resolve_dir comment: it claimed `x=$(cmd) || fallback`
never fires "because the assignment succeeds even when the substitution
fails." That is wrong — command substitution propagates exit status and the
fallback does fire, confirmed in sh, bash, dash, and zsh.

Reproducing the original code shows the fallback also worked, so the second
"recurrence" the Agent Note described never existed. Both real defects were
the same one: comparing a resolved path against an unresolved one. The note
now says that instead of inventing a mechanism.

resolve_dir keeps its `|| printf` because it makes every caller a plain
assignment, so no site can compare against an empty path by forgetting its
own fallback — the reason is now stated accurately.

Also from review: REPO_COMMON is now resolved on both branches, matching
REPO_ROOT, and _repo_root notes why it is already physical without its own
resolve_dir call.
2026-07-31 23:40:40 +08:00
Turtle 2225ad435e test: cover experimental gate through the built bin; fix Agent Note facts 2026-07-31 23:37:53 +08:00
Turtle 20e51abd4e feat: rename experimental-meta/-upgrade to meta/upgrade behind --experimental gate 2026-07-31 23:37:53 +08:00
Turtle 1d0997d3e0 Merge remote-tracking branch 'origin/master' into fix/tui-terminal-restore-on-boot-failure 2026-07-31 23:11:01 +08:00
Turtle 992fdc0cee Merge pull request #1102 from deepseek-harness/test/refresh-queue-actions-golden
test(web): refresh queue-actions golden for the lineage-free header
2026-07-31 23:10:35 +08:00
_Kerman 1a09174987 refactor(goal): persist state with domain events 2026-07-31 22:52:18 +08:00
_Kerman b6cf9298e3 refactor(workspace-context): project updates through inbox 2026-07-31 22:43:50 +08:00
kingwl 5e1c4b2b7b Merge remote-tracking branch 'origin/master' into codex/remove-scoped-bash
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-31 22:41:07 +08:00
kingwl 277c540a92 test(web): refresh preserved queue snapshot 2026-07-31 22:40:22 +08:00
Turtle 6e913fe8bb docs(skills): drop installer details from the git workflows
These skills resolve the layout from the PATH launcher and Git, so how the
checkout was installed never enters the procedure. Describing install
shapes, the installer script, and its variables added detail a reader must
hold and would go stale whenever the installer changes.

Both skills now describe the observable state they resolve. The cases that
mattered survive as properties of that state: the main clone may sit
anywhere on any branch, and a launcher may link straight at a worktree with
no `current`.
2026-07-31 22:35:27 +08:00
Turtle 8d62467823 docs(skills): one canonical resolution, no DSH_SOURCE
DSH_SOURCE is an install-time shell variable the installer never exports, so
a skill reading ${DSH_SOURCE} at runtime reads nothing. Verified unset in a
running dsh process.

Git resolves the main clone identically for every install, so the curl-vs-
adopted distinction was never a branch point in these workflows. Verified
one launcher-then-Git recipe against three shapes: a curl install cloning
into the container, an adopted clone nested far outside any container, and
a custom DSH_SOURCE container.

dsh-customize now states that single procedure and warns off the installer
variables. dsh-upgrade's Layout describes what the resolution finds rather
than a path convention, and no longer teaches install shapes as cases.
2026-07-31 22:29:53 +08:00
Turtle 3a6c253cc7 test(web): refresh queue-actions golden for the lineage-free header
PR #1100 recorded the golden with the session-lineage navigation still in
the header; PR #1099 removed it. Master's snapshots job was skipped on the
combining merge, so the conflict first surfaced on the next PR's CI.
2026-07-31 22:29:14 +08:00
Turtle f3ff2e6ab4 docs(skills): say how to resolve the main clone
Both skills said to derive the main clone from the checkout without saying
how, and dsh-upgrade names dsh-customize as the owner of checkout discovery
— so the technique belonged there and was missing.

dsh-customize now gives it: `git rev-parse --git-common-dir` from the
checkout yields the shared git directory, whose parent is the main clone.
It also names the two ways to get this wrong — the answer is relative for a
plain clone, and paths must be compared physically, since macOS reaches
/var through a symlink to /private/var.

dsh-upgrade links to that procedure rather than restating it.

Verified against both shapes: an adopted clone outside the container, and a
curl-shaped install whose clone is at <source>/master.
2026-07-31 22:26:11 +08:00
Turtle 2c29fedaf9 docs(skills): call it the main clone, not the master clone
"Master clone" named the repository after a branch it need not be on. An
adopted clone keeps whatever branch it had — verified: adopting a clone
checked out on a feature branch leaves it there — so the name was wrong for
every install that did not come from curl.

Renamed to "main clone" in dsh-upgrade and dsh-customize, describing its
actual role: the one real clone whose object store every worktree shares.

dsh-upgrade also now says not to assume the main clone sits on `master` or
that its `origin` is authoritative upstream, since an adopted clone may
point at a fork. The fetch itself was already correct: step 1 resolves
authoritative upstream separately, and step 4 fetches upstream `master`
from it rather than from the clone's own branch.
2026-07-31 22:21:55 +08:00
_Kerman e0988abc74 Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.zh.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	.agents/notes/implemented/feature/2026-07-27-tmux-location-context.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-public-agent-stop-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-30-private-agent-send.i18n.yaml
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/config-catalog.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/compaction.i18n.yaml
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/core.md
#	docs/core-data-structures/core.zh.md
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/core-data-structures/llm-streaming.md
#	docs/core-data-structures/llm-streaming.zh.md
#	docs/core-data-structures/session.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/persistence-catalog.md
#	examples/acp-agent/tests/goal-snapshots/goal-session/session.expected.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/acp-agent/tests/snapshots/bash-spill/session.jsonl
#	examples/acp-agent/tests/snapshots/bash-tool-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/cancel-tool-calls/session.jsonl
#	examples/acp-agent/tests/snapshots/cancel/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	examples/acp-agent/tests/snapshots/empty-response-retry/session.jsonl
#	examples/acp-agent/tests/snapshots/error-finish/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-edit/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-glob-sampling/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-policy-reject/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read-window/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write-overwrite/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-invalid-matcher/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-invalid-matcher/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-pretool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/lsp-definition/session.jsonl
#	examples/acp-agent/tests/snapshots/multi-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/packed-chunks/session.jsonl
#	examples/acp-agent/tests/snapshots/parallel-tool-calls/session.jsonl
#	examples/acp-agent/tests/snapshots/pty-tools/session.jsonl
#	examples/acp-agent/tests/snapshots/repeat-tool-guard/session.jsonl
#	examples/acp-agent/tests/snapshots/session-query-spill/session.jsonl
#	examples/acp-agent/tests/snapshots/session-sandbox-root/session.jsonl
#	examples/acp-agent/tests/snapshots/session-title-after-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/skill-load/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.jsonl
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/todo-write/session.jsonl
#	examples/acp-agent/tests/snapshots/tool-call-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/web-fetch/session.jsonl
#	examples/acp-agent/tests/snapshots/workflow-run/session.1.jsonl
#	examples/acp-agent/tests/snapshots/workflow-run/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-edit/session.jsonl
#	examples/headless-agent/tests/semantic-checkpoint-snapshots/tool-outcome-unknown/session.expected.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/goal-tools/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/missing-credential/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/provider-retry/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/pty-tools/session.jsonl
#	examples/headless-agent/tests/snapshots/pty-tools/stream-json.expected.jsonl
#	examples/headless-agent/tests/snapshots/ralph-loop/stream-json.expected.jsonl
#	examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/child.expected.jsonl
#	examples/headless-agent/tests/subagent-inheritance-snapshots/parent-override/parent.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/bash-tool/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/bash-tool/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/persistent-tools/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/persistent-tools/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/session.1.jsonl
#	examples/jsonrpc-agent/tests/snapshots/subagent-spawn/session.jsonl
#	examples/jsonrpc-agent/tests/snapshots/text-turn/notifications.expected.jsonl
#	examples/jsonrpc-agent/tests/snapshots/text-turn/session.jsonl
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/src/client/sessions/request-inspection.ts
#	packages/compact/compact-basic/README.i18n.yaml
#	packages/compact/compact-basic/README.md
#	packages/compact/compact-basic/README.zh.md
#	packages/compact/compact-basic/src/index.ts
#	packages/context/time-context/tests/time-context.spec.ts
#	packages/context/tmux-context/README.i18n.yaml
#	packages/context/tmux-context/tests/tmux-context.spec.ts
#	packages/context/workspace-context/tests/workspace-context.spec.ts
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-loop/README.i18n.yaml
#	packages/core/agent-loop/README.md
#	packages/core/agent-loop/README.zh.md
#	packages/core/agent-loop/src/agent.ts
#	packages/core/agent/README.i18n.yaml
#	packages/core/agent/README.md
#	packages/core/agent/README.zh.md
#	packages/core/agent/src/types.ts
#	packages/core/session/README.i18n.yaml
#	packages/core/session/README.md
#	packages/core/session/README.zh.md
#	packages/fs/tool-str-replace-editor/tests/tools.spec.ts
#	packages/goal/command-goal/tests/command-goal.spec.ts
#	packages/goal/goal/tests/goal.spec.ts
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/host/apiproxy/src/api/index.ts
#	packages/host/apiproxy/tests/api-proxy-workspace.spec.ts
#	packages/llm/llm/README.i18n.yaml
#	packages/llm/llm/README.md
#	packages/llm/llm/README.zh.md
#	packages/llm/llm/src/index.ts
#	packages/pty/pty-local/tests/index.spec.ts
#	packages/pty/pty-local/tests/local.spec.ts
#	packages/pty/pty/tests/service.spec.ts
#	packages/pty/tool-bash-persistent/tests/loader-composition.spec.ts
#	packages/pty/tool-bash-persistent/tests/tools.spec.ts
#	packages/pty/tool-pty/tests/loader-composition.spec.ts
#	packages/pty/tool-pty/tests/tools.spec.ts
#	packages/session-persistence/session-checkpoint-policy/tests/crash-recovery.e2e.ts
#	packages/skill/tool-skill/tests/tool-skill.spec.ts
#	packages/tasks/tasks-local/tests/tasks.spec.ts
#	packages/ui/tui/README.i18n.yaml
#	packages/ui/tui/tests/tui.spec.ts
#	packages/ui/user-approval/src/index.ts
#	packages/ui/user-approval/tests/approval.spec.ts
2026-07-31 22:16:40 +08:00
Turtle 54e541d33f fix(app-boot): drop the unreachable timer guard on the fail-loud release path
The timeout promise's executor runs synchronously while the race is
constructed, so the timer is always assigned; the undefined check was a
dead branch the per-file coverage gate rejected.
2026-07-31 22:14:43 +08:00
Turtle adb88ad36d docs(skills): the master clone may live outside the container
Adoption installs the master wherever the adopted clone already is, so
dsh-upgrade and dsh-customize can no longer state <source>/master as fact.

Both skills already derive the master from the launcher, so the procedures
hold; only the layout description was wrong. dsh-upgrade now names
`git rev-parse --git-common-dir` as the way to resolve it.

The legacy-migration clauses stay: installs made before this change can
still link PATH straight at a worktree.
2026-07-31 22:14:32 +08:00
Turtle 998f9c931d Merge remote-tracking branch 'origin/master' into fix/tui-terminal-restore-on-boot-failure 2026-07-31 22:11:50 +08:00
_Kerman afedf18ccf fix inbox lifecycle downstream contracts 2026-07-31 22:00:39 +08:00
Wenlu Wang e3e6621e48 Merge branch 'master' into codex/goal-clear-single-flight 2026-07-31 21:54:14 +08:00
Wenlu Wang 9a3bdd599c Merge pull request #1099 from deepseek-harness/codex/hide-session-lineage-header
fix(web): hide session lineage in header
2026-07-31 21:51:56 +08:00
Wenlu Wang 880203a738 Merge pull request #1100 from deepseek-harness/codex/web-stop-preserve-queue
fix(web): preserve queued prompts when stopping
2026-07-31 21:51:25 +08:00
kingwl 7b826bf16c Merge remote-tracking branch 'origin/master' into codex/remove-scoped-bash
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-31 21:32:02 +08:00
kingwl 6879c0c9ca Merge remote-tracking branch 'origin/master' into codex/hide-session-lineage-header
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-07-31 21:31:04 +08:00
kingwl aa85e0c6cd Merge remote-tracking branch 'origin/master' into codex/goal-clear-single-flight 2026-07-31 21:30:22 +08:00
kingwl 32972f1675 Merge remote-tracking branch 'origin/master' into codex/web-stop-preserve-queue 2026-07-31 21:30:19 +08:00
Wenlu Wang fb9da7587c Merge pull request #1098 from deepseek-harness/codex/refresh-translation-prompt-snapshot
test: refresh translation prompt snapshot
2026-07-31 21:28:47 +08:00
Ziya 3a01dc814e Merge pull request #1088 from deepseek-harness/agent/web-context-injection-auto-height
fix(web): let context injection cards fit content
2026-07-31 09:28:45 -04:00
Turtle f3a1ff41b7 cleanup(install): drop the master.path record
Nothing read it. The launcher, dsh-upgrade, and the installer's own re-run
all ignored the file, and the diagnostic it was meant to feed was never
built, so it was write-only state.

Git already owns the fact it recorded: a staging worktree's .git file names
the repository path, and `git worktree list` in that clone enumerates every
worktree depending on it. An installer-written copy only adds state that can
go stale while nothing validates it.

The containment caveat it documented is real and stays in the script header
and the Agent Note, now pointing at git's own records.
2026-07-31 21:17:28 +08:00
kingwl 07b0efc49e fix(web): hide session lineage in header 2026-07-31 21:08:57 +08:00
kingwl f38d37be11 test: refresh translation prompt snapshot 2026-07-31 21:06:03 +08:00
kingwl 690ca800b4 fix(test): assign goal e2e to host program 2026-07-31 20:55:43 +08:00
kingwl e73cdd6b7d fix(web): single-flight goal clear 2026-07-31 20:54:23 +08:00
kingwl f001f0c3d7 Merge remote-tracking branch 'origin/master' into codex/web-stop-preserve-queue 2026-07-31 20:53:25 +08:00
kingwl 32a0e871b7 fix(web): preserve queue on stop 2026-07-31 20:51:05 +08:00
kingwl ddda8be736 docs: record scoped bash translation pairs 2026-07-31 20:48:52 +08:00
07akioni 0c59e3e089 fix: cr 2026-07-31 20:48:22 +08:00
07akioni 84255e1d71 fix: ci 2026-07-31 20:48:22 +08:00
07akioni c3e4aeca90 fix: missing notes 2026-07-31 20:47:55 +08:00
07akioni 575e1217bb fix: remove scoped bash 2026-07-31 20:47:55 +08:00
Tianyi Cui 1f6f1cc32f Merge origin/master into workspace-write default
# Conflicts:
#	apps/cli/README.i18n.yaml
2026-07-31 20:44:15 +08:00
ZiyaZhang 344ad0d6fb fix(client): floor the fork anchor to a real event seq
The fork button on a stopped assistant message was inert. Frozen
interrupted nodes carry a flow-ordering seq of turnEnd.seq - 0.9, and
session.fork takes a non-negative integer on the wire, so every such
request was rejected as invalid-params before reaching the host — where
an aborted turn's logged turn/end has always made it forkable.

SessionsService.fork floors atSeq at the wire boundary. Flooring stays
inside the anchor's own turn (every turn opens with turn/start), so the
host's first-turn/end-at-or-after cut still closes on that turn.
2026-07-31 05:41:45 -07:00
Tianyi Cui b694c33d18 Default shipped UI sessions to workspace-write 2026-07-31 20:39:53 +08:00
Turtle b4f1675360 docs(app-boot): correct the pre-fix capture claim and pin the exit seam contract
The PTY capture does continue past the terminal-takeover bytes with the
fatal diagnostic; only the reset never follows. State that precisely in
both notes.

Document on FailLoudProcess.exit that callers treat it as the end of the
run, matching how the release path already relies on it.
2026-07-31 20:37:16 +08:00
Turtle b35b06396d fix(app-boot): keep the fail-loud exit fatal while the terminal is released
Review of the previous commit found two defects in the release path, both
reproduced against the implementation:

- The timeout guarding a never-settling release was unref'ed. An
  unhandledRejection listener suppresses Node's default fatal exit, so with
  nothing else referenced the process reached an empty event loop and exited
  0 on the very failure it was reporting. Keep the timer referenced and clear
  it once the race settles.

- The handler uninstalled itself before awaiting the release. A second
  concurrent rejection then became uncaught and killed the process
  mid-teardown, stranding exactly the terminal state this restores. Replace
  the uninstall with a latch: the first rejection is the reported one, and
  later rejections (teardown's own included) fall through to the pending exit.

Add the PTY regression the fake-process tests cannot express: boot the shipped
tree over a fixture whose llm-pi-ai providers value is list-shaped, expect exit
1, and assert the captured bytes carry both the diagnostic and ESC[?2004l.
Against the pre-fix source the stream ends at ESC[?2004h ESC[>7u ESC[?u ESC[c
with no reset and the case fails, so it pins the actual bug.

Split the two-shape formatting test into one install per case; a latched
handler reports once by design.
2026-07-31 20:37:16 +08:00