Skip to content

Prevent turns in noninteractive Cron sessions - #1547

Open
lihongguang-0014 wants to merge 36 commits into
mainfrom
fix/noninteractive-cron-send
Open

Prevent turns in noninteractive Cron sessions#1547
lihongguang-0014 wants to merge 36 commits into
mainfrom
fix/noninteractive-cron-send

Conversation

@lihongguang-0014

@lihongguang-0014 lihongguang-0014 commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Scope

Prevent isolated Cron sessions from accepting follow-up turns through either the Gateway or the Web UI.

  • sessions.pending_inputs.enqueue proves an exact queued-row or dispatch-receipt replay before Cron policy, while fresh canonical and stored legacy Cron requests fail with SESSION_NOT_INTERACTIVE before attachment staging or queue persistence.
  • Cold /chat/new recovery skips policy lookup only for a genuine provisional draft; recovered durable sessions page the authoritative directory even when the route initially has no session, and terminal misses or errors remain fail-closed.
  • sessions.send replays an existing idempotency receipt first, then rejects a canonical cron: key or stored noninteractive Cron session with SESSION_NOT_INTERACTIVE, accepted=false, and retryable=false before annotation, attachment, message, intent, task, or runtime side effects; missing canonical keys cannot use new_chat to create a writable session.
  • sessions.steer.v2 replays an exact durable receipt before policy, then rejects fresh canonical or stored legacy Cron input before prepare/admission; legacy sessions.steer rejects before runtime or transcript persistence, and delegated sessions.pending_inputs.steer retains rejected queue rows.
  • Session classification is shared through a public session-view helper and receives configured channel name-to-type mappings, so Cron-triggered deliveries into normal built-in or plugin channel sessions remain writable; canonical cron: identity remains authoritative over delivery metadata.
  • The Web UI treats authoritative interactive=false or sessionKind=cron directory rows as read-only, with the exact, case-sensitive canonical cron: namespace as the compatibility fallback for old Gateways and deep links. It replaces the composer with a localized read-only status, removes turn-mutating actions, and blocks their handlers plus central send, message-mutation, and hidden attachment paths without capturing uppercase custom keys. Meta inspection, expansion, and dismissal remain available.
  • The session-directory adapter preserves authoritative interactivity and explicit session-kind provenance. Loaded navigation rows refresh same-key policy synchronously, while direct routes page the directory and remain turn-blocked through lookup failures, terminal misses, or invalid pagination until authority is available, without delaying provisional new-chat drafts.
  • Exact queued and hidden-control retries retain their ingress identity and cross only the replay-specific gate. Receipt-only retries skip mutable active-project validation; definite rejection and fresh work remain policy-blocked. Hidden controls persist their first fingerprinted request, and legacy records replay only when the Gateway advertises receipt-first noninteractive rejection.
  • Goal set/edit/resume and initial-routing send validation honor exact durable receipts before live Cron/routing checks. WebUI direct, queued follow-up, real queue drain, and Steer recovery replay immutable acceptance-unknown requests before mutable attachment, image/routing, annotation, or command-capability admission.
  • Hidden-control project preflight rechecks its captured session after yielding, so navigation cannot enqueue, render, or send one session's recovery work through another; durable work remains solely in its owning outbox and unavailable storage fails closed.
  • Fresh policy-blocked queue items park without rearming the pending-drain timer, while attempted receipt recovery still drains once. Late parked-session results cannot cancel the active session's drain.
  • Floating-composer clearance and Plan questionnaire state remain correct when navigating between Cron and ordinary chat sessions.
  • A rejected chat.send follow-up cannot clear a compaction marker owned by an already-running Cron turn. Context shaping belongs to TurnRunner, so the compatibility wrapper does not clear session-keyed compaction state on policy rejection or earlier validation failures.

Root cause

The session projection classified isolated Cron runs as interactive=false, but turn and Goal admission did not consistently enforce that classification after receipt resolution. ChatView also had no selected-session capability seam and retained mutable input paths for a Cron route. Finally, some exact client replays consulted mutable local admission state before sending the original idempotent request back to Gateway.

Non-goals

  • No database, new RPC method, scheduler, or Cron execution refactor.
  • No change to transcript visibility or Cron scheduling semantics.
  • No broad use of interactive=false for CLI, channel, subagent, or unknown sessions.
  • No new ChatView session-list query or domain-state expansion.

Compatibility

  • New Gateway + old Web UI: the server remains authoritative and rejects the mutation.
  • New Web UI + old Gateway: canonical cron: keys are read-only in the client.
  • Nonstandard legacy Cron keys: the Web UI may still show the composer, but the new Gateway rejects them from stored origin provenance.
  • Configured/custom-named built-in and plugin channel sessions triggered by Cron remain writable unless the stored session itself has canonical cron: identity.
  • Existing accepted requests remain replayable because server receipt replay precedes new live policy/config validation and the client preserves exact queued/hidden recovery attempts. A narrow Hello policy bit lets the new Web UI recover ambiguous pre-upgrade hidden records only against a Gateway that proves this ordering; the bit is absent/false on old Gateways.

Platform impact

The report was reproduced on Windows 11, but the affected Gateway admission and browser input paths are platform-neutral. The fix adds no OS-specific behavior.

Tests

  • uv run pytest -q tests/test_gateway/test_rpc_sessions.py tests/test_gateway/test_turn_ingress_rpc.py tests/test_gateway/test_rpc_cron_current_session.py tests/test_gateway/test_websocket_request_concurrency.py tests/test_application/test_session_lifecycle.py tests/test_gateway/test_session_lifecycle_adapter.py tests/contracts/test_sessions_lifecycle_contract.py — 476 passed after merging origin/main at f4e65ef26.
  • npm exec -- vitest run --maxWorkers=1 — 428 files, 5,283 tests passed on fixed head 2a00df133f02279a03a643e82329b9516e719f29.
  • npm exec -- vitest run src/composables/chat/useChatSessionInteractivity.test.ts src/views/ChatView.cron-read-only.test.ts --maxWorkers=1 — 2 files, 23 tests passed on final HEAD.
  • Browser coverage: history hydration 8 passed, assistant activity 17 passed, and Goal mode 6 passed on the preceding CI-fixture correction heads.
  • uv run pytest -q tests/test_gateway/test_goal_rpc.py tests/test_gateway/test_turn_ingress_intents.py — 98 passed on final HEAD; Goal Cron rejection/replay and live-routing-change receipt replay are covered.
  • uv run pytest -q tests/test_gateway/test_turn_ingress_rpc.py — 79 passed on final HEAD.
  • npm run test:unit -- src/composables/chat/useChatSend.attachments.test.ts — 258 passed on final HEAD, including the production queue drain, ordinary Retry routing, and direct composer exact-replay state through changed live admission.
  • npm run typecheck — architecture, RPC architecture, chat security, theme, motion/radius, i18n parity for all six locales, and TypeScript checks passed on final HEAD.
  • Targeted Ruff and mypy checks passed for all changed Python source/test files; git diff --check passed.
  • Earlier final-head verification also passed npm run build, uv build --wheel, and uv run opensquilla --help.

Manual / live limitations

No credentialed provider, live browser, or scheduled wall-clock Cron run was performed. The changed paths are covered with deterministic offline Gateway and Web tests; CI remains authoritative for the repository-wide matrix.

Release note

Release note: NONE

Safety

No secrets, generated Web artifacts, local paths, private prompts/transcripts, or channel identifiers are committed.

Commit Lineage

This is a build-on-existing remediation of the eligible first-time-contributor PR #1528.

  • Source contribution: 8f9a9593333fd6e253dfae15360c76fc4677fef1 by mikemikimike <13286568797@163.com>.
  • Replayed contribution: 909f6777e646e9b2311f4c5f5f6c36ded8166a2d, cherry-picked with -x and preserving author/message.
  • Stable patch ID for both source and replayed commits: 97f6b4dc34e828368709a2f0e2ab282b58ce3329.
  • Separate same-scope refinement: b5bb68691126e9b6c30717e8e0849eab2e7dbaee.
  • First independent-review corrections: 333f14c11889d0b6dedb85e82bcdc9cef976f208.
  • Second independent-review corrections: ce32c22569aed1ea97b32a9314ff50fe49a1aa05.
  • Third independent-review correction: 23a82da7e6a5b6c975d036ff1ffd3fba8be12bfd.
  • Fourth independent-review corrections: e531651c69719831c8d895d602af5590016d723d.
  • Fifth independent-review corrections: 26c431df77edfdd8739de678a47dab5c20259763.
  • Sixth independent-review corrections: 90cfc6aad343e348696690f0d326c09d6d67271b.
  • Latest-main integration: 1164f5961b562aadd2a2a94e18dff6d494360918, including origin/main at f4e65ef26cb2bb190dd9e6910027f571effa4922.
  • Seventh independent-review corrections: 822bacdb2145fd1077e77e7cdbdf414edd8f5971.
  • Eighth independent-review correction: 0d0093eb1b2eb03e80af91af44675e555e8bdbe4.
  • Ninth independent-review correction: 2de8c39b226831f84175d2acf6de304b9346739f.
  • Tenth independent-review correction: 3e749132489257b6babee7f63ae143f0f885b6a4.
  • Eleventh independent-review correction: ddedb4ab5e3c93e012a97e048e841285bb390545.
  • Twelfth independent-review corrections: 285109b1e1c833467498831166f09e6a4f7d25e5.
  • Independent authority-signal test refinement: 33a3d5339aebd7446e8b351e0868b88526e2e44f.
  • Thirteenth independent-review corrections: cd70aa0c40d4b027c4f046cb2676bef9ba1ea3e8.
  • CI startup-order contract correction: a16e1aeccd68a75b4ee9a9f82e025bf1c209768f.
  • Fourteenth independent-review correction: 9be47fe3cd2f88b8aef4a7bbb27fa7470c437757.
  • Fifteenth independent-review corrections: 63493da42c3e897b2339ea5e1fb6fceda5ab6f05.
  • Sixteenth independent-review correction: bdef53fc8cb877655781743268ccd41e7e505c67.
  • Seventeenth independent-review corrections: 896eb34c48a3575b31d65ca6f9538442072c8598.
  • Eighteenth independent-review corrections: bbfa6dd3ee5e6d14060e74c412da268f914278d3.
  • CI recovery-fixture correction: 53c1698bf14e46d5bae8c5e87922cb42296edf0c.
  • Additional CI recovery-fixture correction: fa01e57f118e2dbf4aee8bc07de8812a9a94e9dd.
  • Nineteenth independent-review corrections: ba48697ae06fd5ae23f8f86db38fd4202eb798d5.\n- Twentieth independent-review corrections: 2a00df133f02279a03a643e82329b9516e719f29.\n
    The contributor branch and PR were not modified.

Linked issue

Fixes #1520

mikemikimike and others added 28 commits September 1, 2026 23:41
(cherry picked from commit 8f9a959)
@lihongguang-0014
lihongguang-0014 force-pushed the fix/noninteractive-cron-send branch from 7f64b19 to 2a00df1 Compare September 3, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Web UI allows sending new turns to an interactive=false Cron session

2 participants