Why: plan U6 vector slice (KTD11 correctness step) - same-dimension vectors from different models must never mix, and PG tiers should run filtered exact top-k in the database instead of scanning every row in-process.
Evidence: full suite green on SQLite and real PostgreSQL 18.6 with pgvector (35 packages each tier) - pgvector_test covers model-identity and dimension isolation, update/delete atomicity (BLOB and vec_pg swap together), shared top-5 with score monotonicity, Recall@10 = exact-scan parity, deny-all predicate leakage; storage-migrate tests green after excluding the derived vec_pg column.
Impact: v25 migration adds model/content_hash columns and (PG, best-effort DO block) the pgvector extension plus vec_pg column - absence degrades to in-process scan; UpsertKnowledgeVectorExt dual-writes BLOB (cross-tier authority) and vec_pg; SearchKnowledgeVectorsModel/SharedModel filter by model identity and use SQL exact top-k (ORDER BY vec_pg <=> q) with automatic degradation; embedders expose Model() and the rag pipeline threads it through indexing and both retrieve paths; storage-migrate excludes vec_pg as a derived column.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U6 slice 3, KTD11; R9.
Why: plan U5 remainder (KTD5/KTD9 + exit criteria) - commands must commit atomically with business writes, artifacts need a shared object store for multi-instance, and the two-real-process acceptance matrix must run in CI.
Evidence: full suite green on SQLite and real PostgreSQL 18.6 (35 packages each tier) - outbox tx test (rollback removes the command, commit makes both visible, idempotent re-enqueue), S3 store tests (SigV4-signed round trip, 404 sentinel, idempotent delete, presigned GET, env fallback), real-process acceptance (testbinary self-spawn of two serve processes with per-process identity and command consumers against one schema: same-conversation contention, cross-instance cancel command claimed exactly once, stale-owner settle fenced with run unchanged).
Impact: EnqueueCommandInTx for in-transaction command outbox and AppendEvents now notifies in the same transaction (KTD6 front half); blobstore.S3 (SigV4, presign, prefix isolation) injected from BLOBSTORE_S3_* env when configured, local fallback otherwise; realproc_test provides the process-level multi-instance acceptance gate (PG-env gated, runs in CI test-pg job).
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U5 KTD5/KTD9, section 5 exit criteria; R4/R7/R10.
Why: Field evidence from the dev instance - the present tool was
projected and enabled but never called, because every resident prompt
module steers 'make me a report' toward file artifacts; the tool
description carried only the component catalog, not when to use it.
Evidence: genui suite green (catalog assertion extended implicitly by
existing shape test); end-to-end model behavior verified post-deploy
in the Dev task.
Impact: Tool description text only (self-gating: description appears
only when the activation gate projects the tool).
Refs: Dev task fix/genui-page-visibility
EOF
)
Why: The approved U5 scope needs the action half of the loop: an
idempotent ledger (migration v24), a single-choke SubmitAction (plugin
activation, current-revision, action whitelist, params-hash
idempotency), dispatch through the command bus so DangerHigh lands on
the existing inline confirm card with no Confirmed passthrough (the
endpoint does not even bind the field), the button-only
generative-ui.submit storing form data in the plugin KV ledger, and
catalog discipline rejecting unknown action ids at present time.
Evidence: go build/vet clean; full go test ./... green; surfacesvc/
genui/store suites also green under AGENT_PG_TEST_DSN; red matrix
covers disabled/stale-revision/cross-owner/unknown-action/idempotent
same-and-different-params/danger-high-await-confirm and the endpoint
forged-confirmed case.
Impact: Additive migration v24 + new endpoint/command; existing
endpoints and command semantics untouched (B-class whitelist starts
empty per the approved scope).
Refs: U5 action-scope r2 (approved, Documents #71); UI #46/#47;
Frontend #56; Dev task feature/generative-ui-u5
Why: PR #152 pinned the bundled core back to 7a98a7e citing "Dev core is
at 7a98a7e", but Dev origin/main pins modules/core at 89f3906; the
misalignment breaks Dev check-pins for any tree that materializes
recorded pointers (exposed by a fresh task worktree running make test).
Evidence: go build ./... and internal/server + internal/store tests
green on the repinned tree.
Impact: restores modules/backend/deps/agent-system-core == Dev
modules/core (89f3906), unblocking Dev gitlink bumps.
Refs: Dev task feature/admin-auth-gate-fix.
Why: a browser holding a revoked API key in localStorage sent the Bearer
header on every request; key precedence 401'd all APIs despite a valid
login session, locking admins out of user management. /api/me also
reported mustChangePassword for key principals, who cannot change
passwords (session-owned), dead-ending the forced-change gate.
Evidence: new TestStaleKeySessionFallback covers fallback and authVia;
TestAuthMiddleware/TestAdminAccountFlow unchanged and green; full
go test ./... green.
Impact: invalid key falls back to a valid session cookie before 401
(cookie-less programmatic clients unaffected); /api/me gains
authVia ("key"|"session"|""), letting the frontend keep key holders
out of the change-password gate.
Refs: Dev task feature/admin-auth-gate-fix.
Why: Dev modules/core is at 7a98a7e7 (core PR #26); backend's internal pin lagged at 89f3906 after a parallel merge, failing Dev check-pins.
Evidence: build and store/runmgr suites green with the aligned pin; check-pins green after this merge.
Impact: gitlink bump only.
Refs: core PR #26/#28 lineage.
Why: plan U5/KTD4+KTD5 - before a second instance may serve writes, conversation execution must have a single valid owner (DB lease, epoch increments on takeover), stale owners must not settle (in-transaction fence check), and cancel/HITL must reach the executing instance via a persistent command inbox.
Evidence: full suite green twice-relevant on SQLite and real PostgreSQL 18.6 (35 packages each tier) - lease tests (claim/contention/re-claim/renew fencing/expiry takeover/monotonic epoch, command inbox idempotent enqueue, atomic concurrent claim) and multi-instance tests (same-conversation contention rejected, stale-owner settle fenced with run unchanged, cross-instance cancel consumed exactly once by the owner instance, non-owner does not consume).
Impact: v23 migration (conversation_executions + run_commands); ClaimConversation stacks a DB lease on PG tiers (15min, release on conversation release); settle path fenced (fenced runs emit run.fenced, no terminal write); StartCommandConsumer polls the inbox for commands on locally-owned executions (cancel routes to local cascade or DB-level close-out; HITL revalidates gate and rejects expired explicitly); server cancel/HITL fall back to command enqueue with 202 when the run is not local; app wires instance identity (host:addr on PG tiers) and starts the consumer.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U5 KTD4/KTD5; R4/R10.
Why: Core #27/#28 landed the surface snapshot types and the
surface.updated union entry; the backend dep copy must match Dev's
modules/core pin or check-pins fails.
Evidence: scripts/check-pins.sh + combined make test in the Dev
workspace after re-pinning.
Impact: Gitlink bump only.
Refs: Core PR #27/#28; Dev task feature/generative-ui-u4
Why: Generative UI U3 - the agent needs a real submission path and the
next turn needs to see prior pages: a built-in in-process plugin
registers generative-ui.present (exposeAsTool) carrying the catalog
(prompt=validation single source) with a schema-repair strategy
measured against real MiniMax-M3 deviations; the command bus gains an
owner-scoped tool gate so a disabled plugin stops injecting its tool
(execution guard remains in surfacesvc); ctxasm projects saved surfaces
back into context so the model can revise existing structure; doc-refs
are authorized against the owner's real artifacts.
Evidence: unit suites green (genui guards/epoch/doc-ref, gate fail-closed,
projection content); full go test ./... green; cmdbus/ctxasm also green
under AGENT_PG_TEST_DSN; real-model closed loop (env-gated
TestRealModelSurfaceLoop) passes: task->spec->save->projection->revise
with revision 1->2 and structure change, CNY 0.08 total.
Impact: New internal/genui package; additive cmdbus/ctxasm/runmgr/
runtime hooks; no storage or contract changes (U2 layer untouched).
Refs: Documents proposal r2 U3; Core PR #25/#26; Backend #143; Dev task
feature/generative-ui-u3
Why: Dev top-level modules/core advanced to 7a98a7e7 (core PR #26, optional node id/source TS contract fields); check-pins requires the backend internal pin to match.
Evidence: go build and store/pgbus suites green with the bumped pin; check-pins green in the Dev worktree after this merge.
Impact: gitlink bump only (additive optional contract fields).
Refs: core PR #26.
Why: plan section 2/KTD6 found the listener fetched only the last event of a batch (intermediate events invisible to live subscribers) and kept waiting on a dead connection after a drop (no actual reconnect).
Evidence: pgbus suite green twice against real PostgreSQL - batch of 64 all visible with continuous seq, 1200 events across pages, connection-kill reconnect recovers the outage window, no-notify events delivered by the periodic rescue; full backend suite green on both tiers.
Impact: Listener tracks per-conversation cursors (first-seen catch-up from 0 is harmless under SSE seq dedup), pages via EventsAfter to the notified watermark, rebuilds the connection with backoff and resumes known streams, and a rescue ticker covers lost notifications; Notifier contract unchanged.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U6 slice 1, KTD6; R5.
Why: Core PR #26 made ts/types.ts carry the node id/source fields; the
backend dep copy must match Dev's modules/core pin or check-pins fails.
Evidence: scripts/check-pins.sh in the Dev workspace; combined make
test rerun after re-pinning.
Impact: Gitlink bump only.
Refs: Core PR #26; Dev task feature/generative-ui-u2
Why: the copy-order coverage guard caught that backend PR #143's new tables (surfaces, surface_revisions, plugin_activation) were absent from the migration list - they would have been silently skipped on real migrations.
Evidence: TestCopyOrderCoversAllFixtureTables failed on the merged main and passes with the addition; full migrator suite green on SQLite and real PostgreSQL.
Impact: copyOrder list only; ordered after their referenced owners/projects.
Refs: backend PR #143; Dev U4 task.
Why: plan U4/R7 - the SQLite-to-PostgreSQL path needs a managed entry that never replays SQLite dumps as PG SQL, fixes migration identity against a frozen snapshot, refuses foreign or non-empty targets, verifies per-table counts and ordered checksums, and keeps secrets out of reports (KTD14).
Evidence: integration tests against real PostgreSQL green twice (end-to-end with bool/NULL/Unicode/BLOB/sequence assertions, idempotent rerun, interrupted-table resume, foreign-data and wrong-snapshot refusals, unknown-owner block); SQLite unit tests for inventory/backup/coverage; full backend suite green on both tiers.
Impact: new cmd/storage-migrate binary (make build now emits bin/storage-migrate); store gains a DB() accessor documented for ops tooling; no server or runtime behavior change.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U4 and section 6; docs/guides/team-storage.md section 10.
Why: Generative UI proposal U2 needs the host side of the contract:
owner-isolated surface storage with revision CAS, an execution-level
enable/disable ledger (default-off, epoch bumped on every transition so
in-flight submits after a disable are rejected), a single-choke-point
service that validates specs (strict decode + catalog rules) before
saving, event publish with pending recovery, the optional SurfaceHost
extension capability, and direct API endpoints that share the same
guard (no bypass by entrance).
Evidence: go build/vet clean; full go test ./... passes (SQLite);
surface store tests also pass under AGENT_PG_TEST_DSN (PostgreSQL);
surface.txt regenerated via the AST snapshot test.
Impact: Additive migration v22 (new tables only); existing endpoints and
plugin_grants semantics untouched; old consumers unaffected. U3+ (data
binding, actions, frontend) deliberately out of scope.
Refs: Documents proposal 2026-10-10-generative-ui-plugin.md (r2 U2);
Core PR #25; Dev task feature/generative-ui-u2
Why: deliverNotices re-invoked itself synchronously from a defer after a failed startNoticeRun requeued the batch - a persistently failing delivery (busy conversation, missing row, slow DB) recursed without bound and overflowed the stack (real-PG CI job 2352, internal/runmgr).
Evidence: regression test TestNoticeDeliveryFailureIsBounded (missing conversation row) hangs on the old code and passes with the fix; full backend suite green on SQLite and against real PostgreSQL 18.6 + Redis; clean-HEAD reproduction isolated an unrelated count=2 admin-login artifact.
Impact: failed batches now return after requeue (retried on the next enqueue or restart); notices arriving during delivery are picked up by a bounded loop tail instead of defer recursion; no behavior change on the success path.
Refs: Dev PR #333; plan R8 real-database gate.
Why: plan U3 remainder (R8/KTD13) - execution admission must be bounded and shared (leases in the database, parents yield while waiting so budgets cannot deadlock parent-child), and project authorization must distinguish read/edit/approve/execute/admin with default-deny for unconfigured actions.
Evidence: full suite green on SQLite and against real PostgreSQL 18.6 + Redis (isolated instance); new tests - admission_pg_test (enforcement, per-user, concurrent no-oversell), runmgr admission tests (saturated-budget sync spawn progresses, background children share the budget, bounded queue 429), server action-matrix tests (default deny, legacy adapter read-only, action-aware authoritative + fail-closed).
Impact: admission_leases (v21) with atomic lease accounting under an advisory lock; runtime.execBudgetGlobal/PerUser/execQueueMax settings (all zero = off, historical behavior preserved); conversation turns, background and sync subagents share the pool, parents yield around synchronous children and HITL waits; queue-full rejects 429 before writing; server gains ProjectAction matrix + SetProjectActionAuthorizer with legacy EXT-8 read fallback.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U3 R8/KTD13; docs/guides/team-storage.md section 9.
Why: the approved plan U3 (KTD3/KTD7/KTD10 plus minimal recovery isolation) requires concurrent submits to not silently overwrite, unknown-side-effect runs to never auto-resume, and the shared rate limiter to degrade conservatively instead of failing open.
Evidence: full backend suite green twice-relevant paths against real PostgreSQL 18.6 and Redis (isolated instance) and on SQLite; new tests - card_version_test (concurrent single winner), recovery_hold_test + recovery_holds_test (skip/release/fail-closed CRUD), ratelimit atomic-window and degraded-fallback tests.
Impact: AppendMessageWithRequest now allocates seq under the conversation advisory lock in one transaction; activity_cards gain version (v19) with SaveCardVersioned/GetCardVersion/CardVersion and cardsvc user mutations (status/pin/action/resolve-by-ref) return 409-mapped conflicts while system mirrors keep blind writes; recovery_holds (v20) with admin list/release endpoints and fail-closed resume skip; Redis limiter uses one Lua INCR+PEXPIRE script and falls back to a per-instance limit with a Degraded counter.
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U3 KTD3/KTD7/KTD10; R3/R6/R7/R8.
Why: the connection-open phase (DSN pragmas incl. journal_mode on first open) can return SQLITE_BUSY immediately - the busy handler does not cover that phase - so four racing migrators failed in 0.03s on slow CI disks regardless of the 30s timeout (jobs 2293/2285).
Evidence: local -count=30 green (fast disk) vs CI deterministic failure at 0.03s (no timeout wait occurred); fix verified with -count=5 locally plus full store suite.
Impact: bounded retry (5 attempts, backoff) on Begin BUSY only - statements inside the IMMEDIATE transaction remain lock-protected; the racing test now materializes the file/WAL first, matching the KTD8 scenario (concurrent migrators on a provisioned database).
Refs: Dev PR #325; plan KTD8.
Why: with _txlock=immediate the whole migration pass holds the write lock; four racing migrators on a slow CI disk exceeded the 5s busy timeout (job 2285: SQLITE_BUSY in TestMigrateRacingSQLite) although the local tier passes.
Evidence: go test -count=3 on the migration atomicity suite green locally; CI failure log cited in the Dev PR.
Impact: local SQLite tier only - lock contention waits up to 30s instead of erroring; no schema or API change.
Refs: Dev PR #325; plan KTD8.
Why: the approved collaboration plan U2 (KTD1/KTD8/KTD12) requires an explicit fail-closed team deployment tier, DDL-and-ledger-atomic migrations serialized across migrators, and a configurable PG connection budget with sanitized effective values.
Evidence: real PostgreSQL 18.6 + pgvector + Redis 8.10.2 local gate - full backend suite green twice with AGENT_PG_TEST_DSN/AGENT_REDIS_TEST_ADDR (isolated svc.sh instance collabtest:8897, redis 6399); SQLite suite green without env.
Impact: config tier+pool keys and validation; store PoolOptions + statement_timeout DSN param + single-transaction migration with PG advisory lock and SQLite _txlock=immediate + unknown-schema-version refusal; app tier guard fail-closed (team requires DSN+auth, no SQLite fallback); CLI flags and explain rows; tests for tier/pool parsing, atomicity fault-injection, racing migrators, old-schema upgrade; fixed pre-existing real-PG test defects (blind &search_path= appends in runmgr/pgbus helpers, pg_trgm per-schema install race, exhausted seq-conflict retries).
Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U2, KTD1/KTD8/KTD12; R2/R3/R7.
Why: relaxAndRemove cleared the per-exec id directory but left the -ws
parent behind, so every conversation accumulated an empty husk - the
live probe caught it in the workspace listing.
Evidence: go test ./... green; TestOverlayCleansShellDir pins the
behavior with a real bwrap roundtrip.
Impact: One extra os.Remove of the (now empty) parent; failures are
best-effort as before.
Refs: Dev task feature/agent-s0-workspace-overlay (fix-up of #135).
Why: The sandbox tier wrote the session directory directly, so a buggy
or runaway script could leave the workspace half-mutated and nothing
bounded what a timeout had already written. The plan's first landing
shape (section 4.4) swaps the session bind for a frozen-base overlay:
each exec freezes the session dir (hardlink clone + sha256 baseline),
runs on a private upper, and only a clean exit promotes the change set
back via CAS (baseline hash match -> atomic replace/remove; externally
changed targets double-store as *.conflict-* instead of clobbering).
Timeouts and kills discard the upper entirely - half-written state
never lands.
Evidence: go test ./... green including bwrap integration roundtrips
(TestSandboxWorkspaceOverlayRoundtrip: add/modify/delete through a real
overlayfs upper with whiteout handling and ws cleanup;
TestSandboxWorkspaceOverlayTimeoutDiscards: partial writes discarded on
timeout) and unit anchors for freeze manifest (.agentx excluded), CAS
add/modify, conflict double-store, and the change summary.
Impact: Opt-in via --exec-workspace overlay / AGENT_SYSTEM_EXEC_WORKSPACE
(only with --exec-backend sandbox; fail-closed on unknown modes). The
model sees a one-line change summary appended to the tool output, so
discards and conflict files are visible. Default behavior is unchanged.
Refs: proposal 2026-10-10-agent-delegation-routing-sandbox.md section
4.4; Dev task feature/agent-s0-workspace-overlay.
Why: Reminders are attached to the request copy and never reach the
session journal, so a transcript grep proves nothing about whether the
R4 trigger fired - the first verification attempt was invalid evidence.
The dual-speed nudge already logs for exactly this reason.
Evidence: go test ./internal/runtime green; the follow-up live probe
must show the [runtime] r4 escalation reminder injected line in the
instance log.
Impact: Two log lines, nothing else.
Refs: Dev task feature/agent-e4-escalation-ledger (third fix-up).
Why: The consecutive-failure counter never fired in the live suite (0
reminders despite 20 failures) because real failures interleave with
successes - each success reset the streak. The pattern to catch is
"spinning overall", not "N in a row".
Evidence: go test ./... green; the anchor test now covers scattered
failures (3 in the last 6 outcomes escalates, 2 does not, once per run);
the next live run must show the reminder actually appearing in the
transcript.
Impact: recentOutcomes ring (last 6) replaces consecToolFailures; the
zero-hit search trigger keeps its own threshold (2); message wording
matches the window semantics.
Refs: Dev task feature/agent-e4-escalation-ledger (second fix-up).
Why: The first E4 live comparison regressed badly (toolFailures 10->24,
toolCalls 63->166) and the instance transcript shows the loop: tagging
kernel read_file/grep results put a trailing [src_N] line on the
model's own workspace files, the model treated it as external
tampering, and re-read/re-edit cycles cascaded into the read-before-
write and file-changed guards.
Why: The ledger only ever needed SOURCES - searches always, and
workspace reads only under inputs/ (the upload material area). Grep
cannot distinguish materials from self-produced files, so it stays
untagged.
Evidence: go test ./... green; new anchor TestEvidenceLedger
OnlyTagsMaterialReads pins the inputs/-only rule; the re-run live
comparison follows on the Dev side.
Impact: evidenceTools loses read_file/grep; isEvidenceRead gates
workspace reads to inputs/; everything else (search tags, escalation
reminders, prompts) unchanged.
Refs: Dev task feature/agent-e4-escalation-ledger (fix-up of #131).
Why: The plan's E4 remainder under the MiniMax-only constraint: the
harness must detect the model going in circles and inject a
change-of-strategy instruction (R4 - the model cannot be trusted to
notice its own repeat-fail loop), and number sourcing needs addressable
evidence: tool results carry [src_N|tool] tags so "found in materials"
becomes "found in source src_N" for both the model and the fact-check
notice.
Why: Failure counting lives in one place (onEvent end, keyed by the
slot status) because tool execution is parallel; the ledger and tag
append are serialized by the same mutex, and escalation reminders fire
once per condition streak - following the dual-speed nudge finding that
MiniMax ignores vague reminders but follows concrete instructions.
Evidence: go test ./... green; new anchors TestEscalationOnConsecutive
ToolFailures, TestEscalationOnZeroHitSearches, TestEvidenceLedger
TagsSource, TestEvidenceLedgerSkipsNonEvidenceTools. Live before/
after comparison runs on the Dev side per ruling 6.
Impact: knowledge/web/tavily searches, fs_read_file and the workspace
read_file/grep results now end with a source tag and enter the run
ledger (2k-char head); two consecutive tool failures or two empty
search results inject a concrete change-of-strategy reminder;
base.deliverables v7 and agent-sub-research v3 explain the tags;
fact-check notice points at src results. Non-evidence tools and
failures are unchanged.
Refs: proposal 2026-10-10-agent-delegation-routing-sandbox.md (E4,
R4); Dev task feature/agent-e4-escalation-ledger.
Why: The E1 live comparison was the only metric that got worse
(unverifiedFacts 7 -> 12): the gate gave one correction round and then
shipped bare numbers. The design (E3) calls for number sourcing with a
mechanically checkable marking escape: numbers labeled as
targets/assumptions are honest planning quantities, not fabricated
facts, so the gate must let them pass - and the model must be told that
marking is the sanctioned way out.
Why: The marking decision is clause-scoped, not a fixed window - a
window wrongly blessed "3,500" because a neighboring clause said
"target 85%" (caught by the existing test). The second round only
fires when the unverified set actually changed, because the
continuation budget is shared across all final gates and a repeat
block on identical tokens starves the downstream gates (also caught by
an existing test).
Evidence: go test ./... green; new anchors TestMarkedAsEstimate,
TestUnverifiedFactsMarksPass, TestFactCheckGateRounds; the live eval
gains an unverifiedUnmarkedFacts field sharing the same marking rule.
Impact: base.deliverables v6 adds the hard number-sourcing rule;
research/explore/general sub-agent profiles v+1 require citations for
key numbers; fact gate: marked-in-clause numbers pass, round 2
escalates to "last interception" with the marking instruction, then
passes through with the check line. Eval report keeps the strict
series and adds the E3-series field.
Refs: proposal 2026-10-10-agent-delegation-routing-sandbox.md (E3,
D4); Dev task feature/agent-e3-evidence-gate.
Why: the round-6 real-model dual-speed run showed MiniMax writing its
reasoning as inline <think> blocks; finishChild's partsText fed them into
the background notice turn and the inbox result card verbatim.
Evidence: new runmgr test covers closed-block stripping with body preserved
and conservative handling of unclosed blocks; go test ./... 32 pkgs green.
Impact: partsText removes closed <think>...</think> spans before summary,
notice, and card assembly; unclosed blocks are kept as-is.
Refs: Dev task feature/dual-speed-real-path.
Why: the dual-speed slow lane shipped as an observable capability plus soft
prompt guidance, but on the real (non-mock) path models routinely grind
through heavy document tasks synchronously for minutes with zero user-visible
output - the pattern only engages when the model happens to think of it.
Evidence: five new unit tests over an injected clock cover fire-after-threshold,
quiet-before-threshold, disabled-by-setting, and the replied/delegated guard
paths (reminders observed in the captured kernel requests); full go test
./... 32 pkgs green. Real-model run recorded in the Dev task card.
Impact: runDirect tracks run start, user-reply, and background-delegation
state; reminders() injects a one-shot dual-speed nudge (SR-B10) once elapsed
time crosses runtime.dualSpeedNudgeSeconds (default 75s, 0 disables) while
the user has neither a reply nor a background lane. New setting registered.
Refs: Dev task feature/dual-speed-real-path; SR-B10 (RDM requirements/01 V2.1).
Why: Real runs were fully serial: the direct path never set the kernel's
parallel tool mode, delegate_task handed the parent only the child's
160-char summary (the full text was dropped), MiniMax streamed its
reasoning inside content <think> blocks, and the EXT-16 sandbox hid
~/.local so python-docx/openpyxl/pptx could not even import. All four
were measured defects (F1/F2/F3/F5) in the delegation/routing proposal.
Why: Serial-only mutual exclusion is not enough for order-sensitive
tools - the card-stream test failed with out-of-order appends - so the
ordering moved into the kernel (SerialTools partition, Agent-Native
#44); process events pair by CallID (#42) and tool results write back
through the injected call context (#43).
Evidence: go test ./... green, including new regression anchors:
TestDelegateToolReturnsFullText, TestToolEventsPairByCallID,
TestToolExecModeAndSerialSet, TestOpenLLMClientReasoningSplitAuto,
TestSandboxArgvDepsLayer, TestExecEnvJoinsPythonPath.
Impact: Direct runs now execute independent same-turn calls
(searches, reads, delegate_task, review_findings) concurrently by
default (runtime.parallelTools=false restores sequential); card flow,
plans, workflows, exec primitives and file edits keep model-declared
order. delegate_task returns the capped full child text. MiniMax-*
models get reasoning_split=true so thinking streams via
reasoning_content. --exec-deps-dir/AGENT_SYSTEM_EXEC_DEPS_DIR mounts a
read-only dependency layer into the sandbox and merges PYTHONPATH.
Bumps deps/agent-native to c348441.
Refs: proposal 2026-10-10-agent-delegation-routing-sandbox.md (E1,
F1-F5); Agent-Native #42/#43/#44; Dev task feature/agent-e1-parallel-fixes.
Why: real run run_b520bf2e3037 showed the user-facing reply being displaced by
internal bookkeeping - the fact-check correction round's closing text plus the
same-source verifier skip notice became the visible answer while the actual
summary was folded into the process timeline (frontend splits at the last
process part). Same tail pollution reproduced on every same-model run since.
Evidence: go test ./... -count=1 green (32 pkgs); new regression tests cover
the demotion of the pre-check summary and the skip notice becoming a check
part; three old-contract tests updated to assert notices land as process
checks instead of final text.
Impact: stop() fact-check branch now demotes the standing summary (aligned
with the gate and cap branches) and asks the model for a fresh full reply;
verifier errors record as process checks (pass-state for same-source skip,
fail-state for real errors) instead of StopDecision.Notice text; the cap
notice likewise becomes a failed check line.
Refs: Dev task feature/final-reply-pollution-fix; diagnosis in
.task/feature-dual-speed-tasks.md lineage and session analysis 2026-10-10.
Why: SR-B10 (RDM requirements/01 V2.1, ruling J-03) defines the task-level
dual-speed pattern: a fast lane gives immediate feedback while deep work runs
in background with queryable progress and results posted back to the inbox.
The base system had the background spawn/notice machinery but nothing the
user could see while it ran or collect afterwards, so heavy tasks just made
the user wait on a synchronous turn.
Evidence: go test ./... -count=1 green (32 pkgs); new runmgr tests cover the
card lifecycle on success (progress card -> done/ok with result summary and
artifact line, card.created/updated/status events) and on failure
(done/danger with the error), plus the cards==nil no-op path.
Impact: spawnBackground now registers an inbox progress card linked to the
child run and rewrites it with the result on completion; the delegate_task
description and base-method working prompt teach the fast-feedback-first
pattern; a #background mock demo exercises the whole path end to end.
Refs: RDM docs/requirements/01 UR-27/SR-B10; .tmp 03-V2 §任务快慢双速通用模式;
Dev task feature/dual-speed-tasks.
Why: TestSurfaceSnapshot compares the exported-symbol list verbatim;
Registry.AddIntent and IntentSpec (SurfaceVersion 2.7.0-experimental)
are intentional additions.
Evidence: pkg/extension suite green; full go test ./... 32/32 packages
green.
Impact: surface.txt only.
Why: plan G2-2 requires plugin commands reachable via slash aliases
through the same bus and audit envelope (EXT-10②), and a natural-
language routing entry so '帮我写项目建议书' can reach the compile
flow. The agent-command lib now carries a validated Slash field
(lib PR #6, pin bumped to 4f583ca); the bus binds plugin-spec aliases
into the same slash table; intents are declared via the extension
registry and routed deterministically (normalized containment, longest
phrase first) to commands through the bus — the system.intent builtin
(/intent) is the chat-entrance dispatch point with a candidate list on
miss.
Evidence: TestPluginSlashAliasBindsAndDispatches — alias binds, case/
whitespace-normalized resolution, slash and button entrances hit the
same command handler with committed receipts; TestIntentDeclareMatch
Route — declare/validate, miss on unrelated text, normalized hit,
bus-dispatch through the chat entrance, longest-phrase priority;
server suite green with the dual-entry pair registered; full go test
./... 31/31 packages green.
Impact: cmdbus plugin-spec slash binding + IntentStore; extension
Registry.AddIntent + IntentSpec (SurfaceVersion 2.7.0-experimental);
pluginRegistry.AddIntent wires plugin-declared intents; server
builtin system.intent; command-pairs fixture extended.
Refs: plan G2-2 (line 366); EXT-10②; lib PR #6 (4f583ca).
Why: User rulings 4/5 plus the P2-P4 live evidence. Keyword scoring dimensions
(readability, actionability) kept failing valid documents - normal Chinese
section headings flagged as column titles, loops of 4 corrections - because
they grade writing preferences, not verifiable facts. Unverified numbers
(~10-13 per run) remained the largest quality leak through every phase.
And the verifier runs on the same model as the worker (lightweight falls back
to chat), so "independent" verification was self-review. Separately, svc
seeding after boot never registered the tavily tools until a restart because
keys were snapshotted at startup.
Changes: quality.HardFacts (lint actually ran + zero errors) replaces the
full ScoreBytes projection in CheckArtifacts - keyword dimensions remain in
ScoreBytes for eval visibility only. New fact-consistency check
(runtime.factCheck, default on): extract percentages/dates/large numbers from
deliverable text, trace each against the material corpus (attachment text +
tool evidence); misses get exactly one correction round (fix, drop, or mark
as target/estimate), never a loop. runVerifier skips same-source model
verification unless runtime.verifierSameModel is set (hard facts + fact check
still run). Search tools now register lazily, reading tavily keys per call
(settings/seeding take effect immediately).
Evidence: SELF - TestHardFactsIgnoresKeywordDimensions,
TestHardFactsKeepsLintUnavailableBlocking, TestUnverifiedFactsAndNotice,
TestFactCheckContinuationOnce (fact note fires, corrected run passes);
go vet + go test ./... pass; live before/after on the golden task set
recorded on the phase05 task card per the policy clause.
Impact: Gate stops rejecting valid documents on stylistic grounds; invented
figures get one forced correction round; same-model verification no longer
pretends to be independent; tavily config is hot. No contract or storage
changes.
Why: User rulings 4 plus the P3 live evidence. The base library taught layout
standards but not how to work: no planning discipline (plan tool used in 2 of
31 tool runs), no retry-escalation rule (same failing call retried verbatim),
no check-API-first rule (phantom API calls), and nothing about edit
granularity - the P3 after-run showed one turn of 27 single-line edits plus a
self-review spiral (117 tools). The final-reply rewrite layer sees only
160-char summaries and fabricated chapters/promised unfinished work; with the
transcript now authoritative the working model already has full evidence, so
per ruling 4 the rewrite defaults off (kept as fallback when a work loop ends
with no user-facing text at all, plus opt-in setting).
Changes: new base.method module (plan-first for 3+ steps, switch approach
after two failures, check API signatures before scripting, batch edits with
multi_edit, persist to completion - never end on a promise, trace numbers/
dates/names back to source material or mark speculation, honest final
report); tool-description overrides for read_file/edit_file/grep/glob/
run_python/run_shell (dedicated tools over cat/sed, build pipeline for Word);
plan reminder now also nudges once when NO plan exists after several tool
turns (ignorable, once per run); runtime.composeFinal default true->false
with empty-body fallback.
Evidence: SELF - TestPlanlessNudgeFiresOnce (fires once, never repeats),
TestResolveSystemDefaultBase updated for the ninth base block; go vet +
go test ./... pass; live before/after on the golden task set recorded on the
phase04 task card per the policy clause.
Impact: Behavior-surface change only; no contract or storage changes.
Why: User rulings 1 and 3. The direct conversation path exposed only
run_shell/run_python, so any file edit meant rewriting and re-running whole
generation scripts (diagnosis: 24 calls with 9 failures in one turn, 6KB
scripts regenerated 8 times). The kernel already ships line-numbered reads,
exact edit/multi_edit with read-before-write, grep and glob; they were wired
only into the squad path. Deliverables had no editable source: docx output
came from one-shot python scripts, so "make section 3 more detailed" rebuilt
everything.
Now: WorkspaceToolsCheckpointed (sandbox = conversation workspace, checkpoint
hooks wired) is registered into the direct run and its decls join the tool
surface (plan mode keeps the read-only subset). Word deliverables get a
markdown-source pipeline - python3 -m housestyle.build renders .md
deterministically via docx_kit and runs lint + outline on every build
(write-time diagnostics, exit 1 on error so the model fixes the source and
rebuilds). run_python kits remain for xlsx/pptx/images (ruling 3).
base-tools/base-deliverables prompts and the house-style SKILL document the
new loop.
Evidence: SELF - TestDirectRunExposesKernelWorkspaceTools (read->edit->grep
against the real sandbox FS), TestDirectRunWithoutSandboxHidesKernelTools,
three new BuildTests in recipes (renders+lint passes, heading-skip errors
exit 1, CLI entry); go vet + go test ./... pass; live before/after on the
golden task set recorded on the phase03 task card per the policy clause.
Impact: Direct conversations can now read and edit workspace files
surgically; docx deliverables become edit-source + rebuild instead of
regenerate. No contract or storage changes.
Why: Q2 ruled exec tools stay disabled for now but code execution must
return later; the recovery path treats model-generated code as
untrusted. This abstracts the interpreter launch point in
exec_interpreter.go into an ExecBackend port (direct = byte-identical
status quo; sandbox = bwrap isolation) so downstream products can
re-enable execution behind isolation without further base surgery.
Evidence: go build/vet clean; go test ./... 32/32 packages green;
sandbox escape negatives all blocked locally (bwrap 0.11.1): home
secret unreadable (tmpfs /home), sibling session unreadable
(session-only bind), network blocked (--unshare-net), 400-process
spawn storm capped by ulimit -u 256 (fork: retry), 60s sleep killed at
3s timeout, orphans die with the namespace; session-dir write visible
on host; direct backend regression (exit=7 / exit=timeout texts)
unchanged.
Impact: run_shell/run_python route through the backend; workflow
ScriptCommand and the squad kernel ExecConfig stay direct (follow-up
scope); config key execBackend + env AGENT_SYSTEM_EXEC_BACKEND + CLI
--exec-backend; extension.Options.ExecBackend (SurfaceVersion
2.6.0-experimental); DisableExecTools remains the top-priority
fail-closed switch.
Refs: proposal 2026-10-09-ext-g1-b1-b3-exec-backend.md section 4
(EXT-16, G2-8 prototype); Backend #110 (DisableExecTools).
Why: Dev's test-pins requires nested library pins to match Dev's top-level
gitlinks; Dev moved agent-squad to 8bbf850 (nested native pin alignment,
Agent-Squad #49) for the quality P2-I2 combination.
Evidence: SELF - 8bbf850 is Agent-Squad main head (#49 merged); combined
make test recorded on the Dev phase02 task card.
Impact: Pin only.
Why: RDM's capabilities are rdm.* commands; the agent run has no bridge
to them, so the V0.1 end-to-end goal (full-conversation operation) and
the agent-driven two-trees-one-net access are unreachable (Q4 option A).
Evidence: SELF - red tests first: TestCommandToolsProjected and
TestCommandToolDispatchesThroughBus failed on undefined symbols, then
green; TestCommandToolsFiltering and TestCommandToolDispatchEnvelope
cover projection filtering (risk default low, description falls back to
title, unexposed commands absent) and the full dispatch receipt with
Entrance=agent preserved through CanonicalEntrance; go test ./... 32/32
packages ok.
Impact: agent-command pin cc1ad77 -> 5c6dd0c (library PRs #4/#5: optional
ExposeAsTool field + "agent" entrance). cmdbus gains CommandTool view and
Bus.CommandTools; runmgr gains SetCommandSource with projection into
runCtx.Tools (dedup: registry tools win) and routing in ExecuteTool -
model calls dispatch through the bus with Entrance=agent, Actor/Owner =
conversation owner, ConversationID set; high-danger commands keep the
inline-confirm-card flow via the bus; unregistered source = current
registry behavior. Composition root wires a busAdapter. No
pkg/extension change.
Refs: proposal 2026-10-09-ext-g1-b1-b3 section 2; Agent-Command #4/#5