Commit Graph
6028 Commits
Author SHA1 Message Date
Ziya 1d848cf5f7 rfc: propose interactive side sessions and merge-back 2026-07-10 02:09:17 -07:00
Hypatia May aa1dc0e2c7 feat(session-query): checkpoint build round 1 2026-07-10 16:51:19 +08:00
pku-xht 64b4e2ed2d test(workflow-workerthread): flake-proof the lifecycle spec's waits under CI load
The spec's 16 vi.waitFor sites used the 1s default timeout to wait for
worker-thread startup and child registration — CPU-bound work that blows
past 1s on a contended runner. The CI coverage lane (4 vitest workers plus
suites that spawn real subprocesses) hit this 3 times across 4 recent PR
runs, each a different subset of the cancellation/worker-death tests, each
green on rerun.

Every wait now goes through a shared helper with a 10s bound, and the file
sets a 30s test timeout to make room for it. The one deliberately tight
wait keeps its 800ms bound through the helper's override — it proves the
host (not the wedged worker's later loop turn) delivered the cancel, so a
generous bound would erase what it tests. No behavior under test changed.
2026-07-10 16:43:41 +08:00
pku-xht 62627d7625 fix(subagent-acp): contain onError sink exceptions to keep result from rejecting
spec.onError is a caller-supplied callback boundary, but the flattening
catch invoked it unguarded: a throwing sink rejected the whole async result,
breaking the seam's "result never rejects on a child-level failure"
contract (and docs/defensive-patterns.md's contain-callback-exceptions
rule). The sink's own throw is now swallowed — named as such — while the
original child failure still settles as stopReason 'error'. Regression test
drives a spawn failure through a throwing sink and asserts result resolves.

Same defect as the codex backend's, fixed there on PR #240; this is the
symmetric fix for the already-merged ACP backend.
2026-07-10 16:42:20 +08:00
kingwl de9d618f0c fix(mode): a failed flush re-parks the pending intent instead of dropping it
Review hardening (the finding's ordering premise did not hold — see the
PR thread — but its failure-path kernel did): onBoundary cleared the
pending intent BEFORE appending the mode/set, so a backend rejecting
that one write lost the switch forever while the picker kept showing it
optimistically. The intent is now cleared only after the append lands;
a failed flush stays parked and the next healthy boundary converges the
log with the picker. The containment test extends to pin the re-park
and the retry.

The bridge's re-notify keeps deriving from the logged event's value —
now documented in place: the service holds ONE coalesced pending slot
(every flush reads the latest selection, so a stale flush cannot
exist), and for any other writer the logged value is the truth the
picker should track, in log order.
2026-07-10 16:28:11 +08:00
kingwl 6a13dcb364 test(workflow): give the wedged-child waitFors loaded-runner headroom
The three wedged-child scenarios busy-spin their worker for 1.5s while the
host waits for the start RPC; under a loaded 2-core CI runner (this branch
adds several parallel suites) the RPC lands after vi.waitFor default 1s,
failing the coverage lane three runs in a row at the same three sites. An
explicit 10s waitFor timeout (well inside each test own 15s budget) makes
the assertions load-tolerant without weakening them. Belongs upstream with
dsh-workflow-workerthread; carried here because it gates this PR.
2026-07-10 15:44:38 +08:00
kingwl ca39fd89b0 feat(example): sandbox-acp-agent — the live composition; RFCs to implemented
The three-entry cordis.yml (dsh-sandbox-local + dsh-bash-sandbox at a
read-only default + dsh-approval) served over ACP: the first live approval
composition. Recorded snapshot scenarios pin the wire end to end —
config-options advertisement, the mode-switching arc as the suite pinned
header (both switches, the prompt-section delta, one changed-by-the-user
notice per knob, a confined write landing under the switched mode), and
both escalation branches over scripted permissionAnswers (a grant runs
confined under workspace-write; a rejection executes nothing and pins the
fail-closed text). The with-key escalation e2e drives a real model +
real runner + the real bridge answerer, world-verified; ci.yml snapshot
lane and e2e.yml install bubblewrap so the confined replays actually
execute.

Both RFCs move to implemented/ (Decision/Consequences form, deferred
phases tracked in their own sections), with every cross-reference flipped.
2026-07-10 15:44:38 +08:00
kingwl 3f663c9154 feat(modes): per-session sandbox/approval switching — the session log as the store, ACP config options
effective(session) = findLast(the session own knob events)?.value ?? the
composition-config default. One log-only event per knob, owned by its
domain (bash/sandbox-mode in dsh-bash, approval/policy in dsh-approval),
each exporting the same three-piece kit: the event declaration, a pure
fold, and THE write path — a switch IS its event; no owner service, no
facts map. Restart immunity and multi-session isolation fall out of the
log replay by construction.

Execution follows the fold on both sides: the bash tool stamps
escalation grant > session override > executor default, and the approval
seam prepends the never-gate that auto-rejects before any interactive
answerer. Visibility is two layers per knob: a per-agent prompt section
states the effective value on every request (logged through
request/header*, so what-the-model-was-told replays from the log), and an
agent/pre-step narrator injects at most one coalesced delta notice with
positional attribution (user switch vs operator/config drift). The ACP
bridge advertises one capability-gated select per composable knob with
currentValue folded per session, validates set_config_option against the
closed vocabularies, and anchors idle switches at the next turn
prompt-submit under the turn-enclosure contract.
2026-07-10 15:44:38 +08:00
kingwl 0e49615a3d feat(tool-bash): sandbox escalation — one approved wider retry after a denial
The tool gate advertises sandbox_permissions (an enum of exactly the modes
STRICTLY WIDER than the mounted executor default — the schema makes a
non-widening request inexpressible) plus a required justification, exactly
when ctx.bash.sandboxMode reports a confining mode at registration:
composition truth, never a dead lever. An escalating call resolves
ctx.approval BEFORE anything executes with the audit-self-contained reason
"escalate sandbox to <mode>: <justification>"; allowed-once stamps the
granted mode onto that one bash request (the seam-level per-call override),
while rejected / cancelled / unavailable and the no-service / no-agent
paths each fail closed with their own error text and execute nothing. The
description teaches the flow only when the fields exist: retry the SAME
command once after a real denial, never preemptively; a rejected
escalation is final. No new session events: the attempt is an ordinary
tool/call, the decision is the approval audit pair, the outcome an
ordinary tool/result whose facts name the mode it ran under.
2026-07-10 15:43:28 +08:00
kingwl 2eed448acf feat(bash): the sandboxed executor — per-call policy carrier, denial facts, runner-failure classification
dsh-bash grows the per-call policy carrier: BashExecRequest.sandboxMode
(request-optional, spec required-but-nullable — the owner pattern; resolve()
is the one explicit defaulting step) and the BashExecutor.sandboxMode
capability fact (undefined in the base class — composition truth the tool
layer can read). dsh-bash-local carries the field verbatim and confines
nothing.

dsh-bash-sandbox extends LocalBashExecutor and hands ctx.sandbox the exact
argv it is about to spawn. A denial is a RESULT FACT (the command RAN;
result.sandbox.denied is orthogonal to exitCode/signal), classified
conservatively against the wrap own dialect; a RUNNER failure outranks
denial — foreground re-throws the structured SANDBOX_UNAVAILABLE, a settled
background task stamps sandbox.runnerFailed — so a broken sandbox never
reads as a failing command and the command never runs unconfined.
dsh-tool-bash renders the markers and teaches the model not to retry around
a policy denial; escalation and per-session switching are staged follow-ups.
2026-07-10 15:43:02 +08:00
kingwl 7b8c3a9b40 feat(sandbox): the confinement seam and the per-platform native runner chains
ctx.sandbox (dsh-sandbox): confine(argv, policy) returns the argv to spawn
instead — wrapped so the process and its children run confined — plus the
enforcement completeness and the backend denial/runner-failure dialects;
no usable backend throws the fail-closed SANDBOX_UNAVAILABLE. Policy rides
per call. dsh-sandbox-local selects by platform and caches the verdict:
multi-candidate chains probe FUNCTIONALLY in preference order (Linux:
bwrap → the registry-installed node-addon-landlock-run launcher), a sole
candidate is selected unprobed (darwin: sandbox-exec/Seatbelt) and fails
closed at execution via runnerFailureSignatures; win32 is a reserved empty
chain. Profile parity is honest per backend (documented temp-area and ABI
differences; enforcement full|partial is a structured result fact).

CI: the sandbox-e2e matrix proves real-kernel confinement per rung (bwrap,
Landlock per architecture through the registry-installed launcher,
Seatbelt), failing on a silent all-skip; the packed-install rehearsal
installs the launcher family from the registry and asserts the binary
executable apart from kernel enforcement.
2026-07-10 15:43:02 +08:00
kingwl 80d8726601 feat(acp): the bridge approval answerer + scripted permission answers
The ACP bridge registers the first real approval answerer: an ask for an
agent it owns becomes session/request_permission attached to the already-
streamed tool call (one-shot allow_once/reject_once only), outcomes map
conservatively (unknown optionId never grants, client cancel → cancelled),
and foreign or call-less requests delegate down the waterfall. The snapshot
harness accepts scripted permissionAnswers (FIFO; an unscripted prompt
answers cancelled, fail closed) so recorded scenarios can drive the wire
keylessly.
2026-07-10 15:43:02 +08:00
kingwl ef35007d75 feat(approval): the approval seam — one-shot permission decisions over a waterfall of answerers
ctx.approval (dsh-approval): request() dispatches the approval/request
waterfall and always resolves a closed outcome — allowed-once / rejected /
cancelled / unavailable — never rejects; zero listeners fall through to
fail-closed unavailable; abort settles cancelled and discards late answers;
throwing or rogue answerers are contained as unavailable; every ask lands
the log-only approval/asked / approval/decided audit pair. dsh-tools routes
a pre-execute ask through the seam opportunistically (ctx.get) with three
distinct deny reasons, keeping the historical ask→deny degrade when the
seam is absent.

The per-session policy tier, the ACP bridge answerer, and the sandbox
escalation asker are staged follow-ups of the approval-seam RFC.
2026-07-10 15:43:02 +08:00
Yichen Jiang b5b17a7f65 fix(ci): stabilize static and coverage gates 2026-07-10 15:21:22 +08:00
kingwl 61922af5cd fix(llm-deepseek): a reasoning-only assistant turn serializes as "" content, never null
Live failure: deepseek-v4-flash answered a greeting entirely in the
reasoning channel — no text block, no tool calls. The serializer's
null-content fallback produced an assistant message with neither
content nor tool_calls, which the API 400s ('Invalid assistant message:
content or tool_calls must be set'). Because that message sits durably
in the session log, every later turn of the session re-derived the same
history and failed identically — one all-reasoning response bricked the
session permanently (log: turns 2 and 3 failing byte-identically).

content is now always the flattened text ('' when there is none); the
passback rule still keeps reasoning_content off plain turns. The old
null shape was pinned by a test whose comment claimed the wire accepts
it — live-falsified, updated together with the code, plus a regression
test for the reasoning-only shape. Existing bricked logs resume cleanly
under the fix (the poisoned message now serializes as '').
2026-07-10 15:17:49 +08:00
kingwl 02e0756b78 feat(mode): ask_user_question joins the plan allowlist; the section steers to the exit tool
Live-session feedback (a real Zed elicitation round-trip): the model
presented its finished plan as a plain reply and asked the USER to
switch modes — the exact reversal the roadmap warns about — because the
shipped section's 'present it with the exit_plan_mode tool' read as a
suggestion. The section now says a finished plan is delivered by
calling exit_plan_mode, preferred over pasting it as a plain reply or
asking the user to switch modes — firmer, without imperatives.

ask_user_question enters the shipped plan allowlist (asking is
read-only-safe), and the section points a blocked decision at it. The
plan-acp-agent example composes the bash family (default mode only —
plan's allowlist keeps excluding it, so the two modes now demo a real
difference) plus tool-ask-user; both recorded scenarios re-recorded:
the pin now shows plan = [ask_user_question, exit_plan_mode, read,
todo_write] and post-exit default = the full eight-tool surface.
2026-07-10 15:05:26 +08:00
Yichen Jiang 680ae53bb6 Merge remote-tracking branch 'origin/master' into codex/skill-system
# Conflicts:
#	docs/architecture.md
#	docs/config-catalog.md
#	docs/module-graph.md
#	docs/rfc/INDEX.md
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	packages/core/agent-core/src/index.ts
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	packages/support/acp-snapshot/src/suite.ts
#	packages/ui/acp-agent/src/index.ts
2026-07-10 14:43:33 +08:00
Yichen Jiang eee4f02108 Merge remote-tracking branch 'origin/master' into codex/project-instruction-files
# Conflicts:
#	docs/architecture.md
#	docs/config-catalog.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	packages/core/agent-core/src/index.ts
#	packages/ui/acp-agent/src/index.ts
2026-07-10 14:40:33 +08:00
Yichen Jiang 3fe196a751 Adapt workspace context to session prefixes 2026-07-10 14:32:44 +08:00
Yichen Jiang 6292d52236 feat(skill): move catalogs into session prefixes 2026-07-10 14:19:06 +08:00
Yichen Jiang b4d09d3a2d Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts:
#	docs/module-graph.md
2026-07-10 13:43:09 +08:00
Dudu-0223 8b86c3febc fix: stabilize spill snapshot path budget 2026-07-10 13:15:15 +08:00
Dudu-0223 2d9351a23a Merge remote-tracking branch 'origin/codex/truncated-design' into codex/grep-glob-rfc 2026-07-10 13:04:01 +08:00
Dudu-0223 3bb90bd4b6 fix: make search raw output recovery backend-neutral 2026-07-10 11:53:02 +08:00
kingwl 14f7976e3d fix(mode): an approved exit leaves plan mode at the step boundary, not mid-batch
Review finding: the exit tool's direct mode/set append flipped the
folded mode while the loop could still execute further tool calls from
the SAME assistant response — a same-batch exit_plan_mode + write pair
would sail past tools/pre-execute under 'default' even though the
request was assembled under the plan-shaped header. That broke the
design's own invariant (a step's executions run under the mode its
assembly folded), which the pending-intent flush was built to hold for
user flips.

The tool now records the switch as a pending intent like every other
writer, flushed at this step's end (still in-turn); pending intents
carry a narrate flag so the exit's flush stays silent — the tool result
is its narration — while user flips keep the coalesced boundary notice.
The gate, folding the logged mode only, now provably covers the whole
batch: regression test pins approve-then-write-in-the-same-batch as
denied, and the widened toolset still arrives on the next step.

The recorded scenarios are re-recorded: the fixture now shows mode/set
landing after step/end, before the widened fallback header.
2026-07-10 10:35:43 +08:00
Yichen Jiang 712d481bdf fix(bash): classify signal-terminated background processes 2026-07-10 10:19:54 +08:00
Tianyi Cui 42ebbfdf8f Merge pull request #217 from deepseek-harness/feat/subagent-process
feat(subagent): extract dsh-subagent-process shared out-of-process machinery
2026-07-10 10:15:52 +08:00
Yichen Jiang f42cd49337 Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts:
#	docs/architecture.md
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	packages/README.md
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	scripts/doc-budgets.manifest.json
#	tsconfig.base.json
#	tsconfig.build.json
#	tsconfig.json
2026-07-10 10:13:13 +08:00
Dudu-0223 31be42e92d test: add ACP spill snapshot coverage 2026-07-10 10:07:32 +08:00
pku-xht 3eea868a1b Merge branch 'master' into feat/subagent-process 2026-07-10 10:05:01 +08:00
kingwl 4e09fdeafb docs(agents): an Agent efficiency section — one-round gate runs
Repeated full-repo gate executions in agent sessions traced to one
habit: piping a long command through a filter, losing the failure
detail, and re-running the whole gate to recover it — paying both the
gate again and an extra agent round-trip. The rule lands the full
output in a temp file, prints the summary, and auto-expands capped
failure context on non-zero exit in the SAME invocation.

Fits the existing 1802-word ceiling after condensing (1800).
2026-07-10 09:30:05 +08:00
kingwl c5cb7da364 test(mode): record the plan-mode approve and keep-planning scenarios
The with-key recording session the RFC deferred. plan-mode is the 'plan'
header class's pinning scenario and necessarily carries BOTH header
shapes verbatim: the plan-shaped initial snapshot and the widened
fallback snapshot the approved exit produces mid-turn — so the suite
factory's pin guards relax from exactly-one to at-least-one header (the
classmates' uniformity anchor is the pin's FIRST header; transition
headers are legal only in the pin, matching the sandbox stack's
precedent). plan-mode-reject pins the keep-planning branch: the
corrective isError carries the reviewer's free-text feedback verbatim
and the session stays in plan mode, one header, uniform with the pin.

Recording notes, encoded in the prompts: the model is pinned to RELATIVE
paths (a recorded absolute temp path neither replays on another host nor
normalizes — the normalizers scrub the run's own cwd, not the
recording's), and the recorded model never calls a filtered tool, so the
gate's deny path stays pinned at the unit tier — that refusal is the
behavior the soft layer exists to produce. The gen-tool-catalog
meta-test pins exit_plan_mode in the harvested schema list.
2026-07-10 03:39:29 +08:00
kingwl 5a8e3a14ee docs(rfc): plan mode to implemented — amendments folded on the way
The proposal survives contact with the code with three amendments, per
the RFCs-are-proposals rule. (1) A mode transition logs a
request/header-delta only when expressible: adding exit_plan_mode
resorts the canonical tool list, and a pure reordering has no delta
form, so entering plan mode logs the full fallback snapshot — the
attributability claim holds either way. (2) The proposed/ skeleton
converts to the implemented grammar: Proposal → Decision, the roadmap's
staging (now history) drops to the standing Deferred list, and
Acceptance criteria + Risks fold into Consequences (what holds, then the
accepted costs, including the ACP v2 mode-removal migration). (3) The
two recorded scenarios stay pending a with-key session, recorded in
Deferred.

The docs tail completes: the cookbook's plan-mode row upgrades from
sketch to the shipped package, architecture.md gains the ctx.modes
capability row (ceiling 1640 → 1650: a new capability service's table
row does not fit the old budget), and every reference repoints to
implemented/.
2026-07-10 03:10:19 +08:00
kingwl edc065666f feat(mode): exit_plan_mode + the ACP session-mode picker + scriptable review answers
Plan mode's stage 2 (RFC 2026-07-07-plan-mode). The exit tool: one
required plan argument (the durable log artifact), execute re-checks the
folded mode, then conducts the review over the user-interaction seam —
one single-select question (Approve / Keep planning) with free text open
— so an approval appends mode/set back to default in-turn and every
other outcome (keep-planning feedback verbatim, aborted, no provider)
returns the corrective isError with the mode unchanged. presentCall is a
generic card titled by the plan's first heading carrying the plan
markdown; over ACP the review rides the ask_user elicitation flow, in
the terminal the stdio prompt queue — no approval-seam dependency.

The ACP bridge maps the picker 1:1 onto ctx.modes (opportunistic, a
type-only peer edge): session/new + session/load advertise
availableModes/currentModeId, session/set_mode validates through set()
and echoes an optimistic current_mode_update (the pending mode IS the
selection; the logged mode/set lands at the boundary and, matching, is
not re-sent), and a session/event listener re-notifies on each logged
flip that differs from the last sent — the tool-driven exit updates the
picker. The feature matrix rows move from 'not modeled' to the
picker-to-modes / knobs-to-config-options division, with the ACP v2
removal direction recorded as a mechanical-migration risk.

The snapshot harness gains the setMode/setModeExpectError ops and a
scripted elicitationAnswers FIFO (cancel on exhaustion; a stray choice
string reaches the agent verbatim as a non-consenting custom answer, so
a scenario bug fails safe). The suite factory's header-pin requirement
now applies only to model-turn scenarios — a protocol-only suite has no
header content to anchor. examples/plan-acp-agent is the live
composition; its keyless modes-advertise scenario pins the wire surface
(advertisement, both set_mode round-trips, unknown-id rejection). The
recorded plan-mode approve/reject arc awaits a with-key recording
session; its texts are pinned at the unit tier meanwhile.

examples/AGENTS.md ceiling 653 → 680: the new example's required smoke
row does not fit the old budget.
2026-07-10 02:57:40 +08:00
kingwl 7afdc6e3b9 docs(rfc): escalation targets are a closed static vocabulary; strict widening moves to an execution-time check (review blocker)
The default-relative ladder conflicts with per-session overrides: schemas
are registry-global while the effective mode is switchable, so a session
overridden NARROWER than the default loses exactly the lever it needs — a
workspace-write default with a read-only override could only request
danger-full-access (forced over-escalation), and a danger-full-access
default with a read-only override advertised no fields at all (confined,
no escalation path). The enum now pins the closed target vocabulary
(workspace-write / danger-full-access) whenever the executor confines;
strict widening is enforced per call against the session effective mode
(override ?? default), failing closed without prompting anyone. The
default-relative ladder and per-session dynamic schemas move to
Alternatives; the harmless Risks claim is corrected to name the runtime
check as the safety boundary.
2026-07-10 02:34:11 +08:00
kingwl d3a833fc4a docs(rfc): speak of the staged example and recordings at plan-time tense (review)
The proposed forms claimed examples/sandbox-acp-agent, its recorded
scenarios, and the approval servicing of ask as already existing; a reader
of this docs-only change would look for a composition and goldens that are
not in the tree. Usage walkthroughs now name the arc the staged scenarios
are to record, both Testing sections open with the plan-time banner, and
the grounding bullet states todays degrade honestly.
2026-07-10 02:34:11 +08:00
kingwl 3191cb9404 docs(rfc): propose the approval seam and the subprocess sandbox 2026-07-10 02:33:22 +08:00
kingwl 63ced3e0e2 feat(mode): the session-mode core — logged per-agent policy state (@deepseek-ai/dsh-mode)
Plan mode's stage 1 (RFC 2026-07-07-plan-mode): a new packages/mode/ group
with one product package owning the mode/set SessionEventMap vocabulary
(log-only, non-surface, whole-value replace), the pure foldMode, and the
ctx.modes service (list/get/set). User flips are pending intents flushed
at turn/start / step/end — turn enclosure makes an idle append illegal —
with one coalesced context/message notice when the flushed mode differs
from what the last logged request header told the model; a folded mode
the config no longer defines reads as default plus one boundary notice.

Enforcement is two covering layers: a system-prompt/assemble wrapper
filters the RETURNED assembly's tools to the mode's allowlist (and shows
exit_plan_mode IFF the folded mode is plan) beside the mode:policy
section at order 50, and a tools/pre-execute gate denies deny-by-default
against the same allowlist, judging by the logged mode only. The default
mode is the absence of policy — assemblies stay byte-identical to a
no-dsh-mode deployment.

AgentOptions.mode (declaration-merged) seeds a child's initial mode
through the same flush on agent/created; the stdio app gains /mode
(print/switch, never sent to the model) over an opportunistic
ctx.get('modes'). Config is an explicit resolve step: the built-in plan
definition (read-only allowlist; bash/subagent excluded until the
sandbox family lands) merges unless overridden, 'default' as a key
throws at load, unknown names throw at set() time.
2026-07-10 01:38:39 +08:00
Tianyi Cui af242dbae5 Preserve snapshot refresh seed boundaries 2026-07-10 01:04:06 +08:00
Tianyi Cui 5f7177a0a9 Stabilize workflow worker coverage wait 2026-07-10 00:55:47 +08:00
Tianyi Cui 9d2cf8ce82 Add keyless snapshot refresh mode 2026-07-10 00:48:27 +08:00
Tianyi Cui a5c972b7a4 Merge pull request #233 from deepseek-harness/workflow-vm-to-workerthread
workflow: script-driven multi-agent orchestration (workflow seam + node:worker_threads engine + tool)
2026-07-10 00:41:48 +08:00
imccyu 3d141eea57 Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread 2026-07-10 00:35:16 +08:00
Tianyi Cui 0cfb657f98 Merge pull request #234 from deepseek-harness/turtle/docs-human
docs: a human touch
2026-07-10 00:31:54 +08:00
kingwl ab8dc1ec72 docs(rfc): the exit review rides the user-interaction seam; the stack bases on master
The RFC seated the exit approval on the approval seam because that was
the only asking machinery in flight when it was written. ask_user_question
has since merged (PR #108): the exit moment is a question, not a
permission — the user-interaction seam gives the review options plus a
free-text channel, so a keep-planning answer carries the user's feedback
to the model verbatim, and an approval can later grow options. The gate
no longer returns ask (that vocabulary stays free for genuine permission
gating); the tool conducts the review inside its own execution and
degrades to manual mode-toggling without a provider. The approval-seam
shape moves to Alternatives considered with the reasoning.

Consequences: the roadmap's hard prerequisite on the sandbox branch
disappears (this stack bases on master), the recorded scenarios script
elicitation answers instead of permission answers, and the ACP v2
session-mode-removal direction is recorded as an accepted migration
risk.
2026-07-10 00:30:55 +08:00
imccyu a53be53d64 workflow: forward TSX_TSCONFIG_PATH through the worker env scrub
The empty-env hardening wiped the one variable the UNBUILT worker's
loader depends on: tsx finds a tsconfig by searching up from the
worker's cwd, and a parent running outside the repo (the ACP snapshot
harness pins the repo tsconfig through TSX_TSCONFIG_PATH exactly
because its child cwd is a temp dir) lost the dsh-* paths map - the
worker then resolved workspace imports to unbuilt lib/ bundles and died
on CI with ERR_MODULE_NOT_FOUND (green locally only because stale built
lib/ masked the wrong resolution).

Forward exactly that variable when the parent carries it - loader
plumbing, not a secret; the built shape stays fully empty - and pin the
whole contract with an escape-based test: the worker env is exactly
{TSX_TSCONFIG_PATH}, the credential canary still never crosses.
2026-07-10 00:23:11 +08:00
Turtle c60eb87860 make wordcount budget guidance clearer and dedup docs 2026-07-10 00:20:35 +08:00
imccyu aff657cc28 fix: ci run without build lib for snapshot 2026-07-10 00:16:29 +08:00
Turtle f77174f13a make it clearer 2026-07-10 00:13:06 +08:00
kingwl 775fa64c77 Merge remote-tracking branch 'origin/master' into worktree-session-modes-rfc 2026-07-10 00:11:16 +08:00