Skip to content

feat(server): run scheduled tasks until their checks pass - #132

Merged
lukemaj merged 11 commits into
mainfrom
feat/123-scheduled-tasks
Oct 2, 2026
Merged

lukemaj merged 11 commits into
mainfrom
feat/123-scheduled-tasks

Conversation

@lukemaj

@lukemaj lukemaj commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What: Chromeria gains scheduled tasks that continue until a pinned server outcome check passes.
Why: Starting a turn or finishing it does not prove recurring or timed work is complete.
So what: This PR remains draft until the native preview host returns and root completes client acceptance; installed-build provider proof remains user-owned after merge.

Durable blocked-draft handoff.

Closes #123. Targets main including Spectrum #118 and people #121. No merge, installation or real-provider live proof performed. No versioning by design.

The fork-owned scheduler uses the existing append-only orchestration log, serialized atomic claims and durable send receipts. Required checks must fail at creation. Each run pins its check version, definition, working directory and creator owner; other authors' check edits and reverts apply only to future runs. A passing check waits for pending parent work, Drafters and queued reports. Recovery walks the Prism Colors, continues the same thread after work or ambiguous restart, and resumes the same pinned run after ladder exhaustion. Retired threads require explicit recovery.

Settings and agent tools provide creation, health, history, editing, pause, deletion and run now. Fixed times default to a symmetric ±30-minute window; 0 pins the exact minute, with chosen minutes and neighbours returned. Settings requires one named environment. Quiescent exhausted tasks can be tombstoned without declaring their unfinished work done.

Exact-head proof and independent review

Final head: ca9e5375da9ca4600c57985b6017cc9902e6a695; base: 7f0a8ff68a429202d89619aecdd8b64289fd3934 (Spectrum #118 and people #121 included). Clean task branch equals its pushed head. No migration-list edits.

  • Node v24.18.0, PATH prefix /opt/homebrew/opt/node@24/bin: 493 server tests and 93 UI tests passed. All 14 targeted commands exited 0: tests, server/contracts/web typechecks, targeted lint, scoped server/web knip exports, fork registry, fork check and diff checks. No repository-wide local gate is claimed.
  • Exact commands, exits, timestamps and logs: /tmp/promachos-coord/evidence/123/final-proof.json, final-ci-server-proof.json, and final-ci-ui-proof.json. Existing unrelated lint warnings remain; no knip ignores were added.
  • CI run 36943557708: all completed non-skipped jobs passed, including Check, Test and all three server test jobs. Preview/mobile jobs skipped by workflow conditions.
  • Independent exact-head verdict: no code-blocking findings; newest review/independent is success on this exact head, created by lukemaj. Reviewer: visible T3 thread sub.sub.sub.46b03420-8643-4703-8756-8602f14b39fb.dispatcher-2274c9005036.dispatcher-50e6f2dd4945.reviewer-145b3d1ce0f8, GPT-6-Luna via Codex. This is a code verdict, not readiness approval.
  • Closed review findings: actual MCP toolkit registration accepts the empty object input schema; provider stopped sessions remain recoverable separately from durable retirement; retained completed verdicts cannot be rewritten. Real SQLite/engine/sender/provider-reactor tests use receipts, controlled clocks, durable restart and side-effect counters. Real provider live proof is not claimed.

Blocked isolated client acceptance

Root's sanitized partial record is /tmp/promachos-coord/evidence/123/root-client-proof.json. Actual runtime at fdc7e2f9ea7436b695380d6222efa8d64d5ccaa1 started successfully on Node 24; discovery returned HTTP 200 in 0.31 seconds. Later commits add three test mock methods and remove six unused export modifiers only. Root and coordinator inspected that exact delta; function bodies/rendering are unchanged, but source is not byte-identical and the browser pass was not rerun at the final head.

Passed in the native client: All-environments notice with no task management, named environment management, missing-check validation, passing-check creation refusal, failing-check creation, pause disabling Run now, and authored future-check v2 history retaining v1.

External blocker: PreviewAutomationNoAvailableHostError: no connected desktop host. Root stopped browser verification when the host disappeared during revert submission. Revert/v3 author and reason, default/exact fixed-time windows and neighbours, task health/history screenshot, Settings search, and fixture deletion remain unverified. Revert may have reached the server: inspect history before retrying. These are unfinished dev-client checks, separate from deferred installed-provider proof below.

Before image: /tmp/promachos-coord/evidence/123/settings-before.png. Captured All-environments notice: /tmp/promachos-coord/evidence/123/settings-all-environments.png. No task-health after image is claimed; GitHub upload was unavailable, so permitted local evidence paths are retained.

Root owns retained isolated dev session 71083, ports 13832/5792, and paused fixture Scheduler proof: interval (1440 minutes; no provider run requested). Retain the task worktree and isolated state for resumption. Only a read-only database VACUUM snapshot was copied; no settings or secret store was copied. Generated isolated credentials had zero nonempty live-secret matches, settings had no populated relay/cloud fields, and startup had no relay registration. No alternate browser was used. Root resumes these checks when the native host returns; do not mark ready, merge or install now.

Agent cost

Agent Observer publication was republished for this final candidate. Attribution gap: ownership remains unbound, so displayed $0.00 is not a measured task total; total cost remains unknown. No bookkeeping capture or unrelated observer repair was introduced.

Assumptions and limits

  • Only done is settled. The next slot after exhaustion consumes the newest slot and resumes the same unfinished pinned run; it never starts the prior worked outcome from scratch.
  • Creator people ownership is resolved server-side from SessionStore or the MCP caller and defaults to Luke for legacy omissions. It is distinct from immutable check audit authorship.
  • Checks execute in the target project root or existing thread worktree, pinned per run. Use absolute paths for external markers; relative paths are relative to that pinned folder, never assumed installed process cwd.
  • Live health carries recent 20 runs/check versions plus unfinished pinned judges; full check audit stays in the log and is pageable/revertible. Check output is bounded to 16 KiB. The client rereads while the page is visible.
  • No numbered migration-list edit or new scheduler table. No mobile screen or push notification. Website impact: none, this changes the installed/local/remote application and its user guide.

Installed-build proof, user after merge

  1. In a test project create /Users/lukaszmaj/Downloads/chromeria-scheduler-proof/repeating and /Users/lukaszmaj/Downloads/chromeria-scheduler-proof/once. Use a Prism role with an available provider. Do not reuse existing marker files.
  2. Create a repeating task every 1 minute, targeting a new thread in that project. Prompt: “For the run id stated in this turn, write that id once into /Users/lukaszmaj/Downloads/chromeria-scheduler-proof/repeating/<run_id>.done. Check whether it already exists before writing; continue existing work without repeating it.” Check command: test -f '/Users/lukaszmaj/Downloads/chromeria-scheduler-proof/repeating/{run_id}.done'. Reason: “The run-specific marker proves this run completed.” Creation must succeed because its creation marker is absent.
  3. Observe two scheduled slots: each check starts failing and ends passing, each run becomes Verified done only after the check, and each has one marker. Pause the task. Run now must be disabled while paused; resume it before further runs.
  4. Create a one-shot task at least 3 minutes ahead with window 0 and the same prompt/check using the once directory. Verify the selected minute equals the requested minute, the check begins failing and then passes, and exactly one run/marker is recorded. Subsequent reconciliation must not create another one-shot run.
  5. For continuation proof, create another future one-shot with a check that also requires a separate allow-pass file. Let the agent finish its marker while that file is absent: it must remain unfinished and continue the same thread/run/check. Create allow-pass, then verify done and one marker. Do not edit the active check to force success.
  6. Record run IDs, thread IDs, pinned check versions, chosen minutes and final verdicts on this PR. Delete quiescent fixtures through Settings after checking history; remove only your test marker directories. Provider execution, installed desktop runtime and exhausted-run visual controls remain unverified until these user-owned checks.

Elon record

Requirements and who asked: #123's settled user outcome/check/recovery/history/window requirements, explicit sequential server and Opus UI routing, root's Spectrum/people/retirement/lease safeguards, one PR against main and no merge.
Deleted: V2 service backport, duplicate turn sender, hash chain, new scheduler tables/numbered migrations, unbounded live history, fresh worked-run retries, notification endpoint, mobile screen, cancel state, version-policy adoption and extra PRs.
Bottleneck: Code proof, independent review and CI are complete; the missing native preview host blocks the remaining isolated client acceptance.
Checked myself: Whole live #123 and comments plus parent #113, current rules, actual main auth/people mapping, preserved rebases, exact final source delta, 14 command manifests/logs, live main/PR head, authenticated exact-head review status and completed CI. Root client evidence inspected and explicitly partial.

Implemented through visible T3 threads: GPT-6.1-Sol on Codex for server work and coordination; Claude Opus 5.5 on Claude Code for every UI write. Independent review used GPT-6-Luna via Codex; Agent Observer publication has the explicit attribution gap above.

lukemaj and others added 6 commits October 2, 2026 00:30
Settings → Scheduled tasks lists each task's health (last run, pinned check
version and verdict, failure streak, last error, chosen minute and
neighbours) and manages tasks: create with a required check and reason,
edit, pause and resume, run now, delete, and revert check versions with a
reason. A finished turn with a failing check is shown as continuing, never
as done. Adds a user guide.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ou rules

The Scheduled tasks page now requires one selected environment and asks for
one otherwise, instead of listing a stand-in environment's tasks. Search uses
the environment scope. Only a verified run counts as settled: on a task that
needs you, Run now resumes the same run and Delete is left to the server's
quiescence check, while work in progress blocks both. Guide updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 1, 2026
@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

What: Independent review of candidate a650c231c27dca2bbb9b3faea77e88c479626773 is paused without a code verdict.
Why: The isolated server reports routesReady timeout, but the active failure is not yet attributed to this candidate or cleared by runtime evidence.
So what: Keep review/independent non-success until the startup failure is resolved and the exact candidate is re-reviewed.

Review evidence

  • Confirmed live PR feat(server): run scheduled tasks until their checks pass #132 remains open against main, base 7f0a8ff68a429202d89619aecdd8b64289fd3934, head a650c231c27dca2bbb9b3faea77e88c479626773; worktree feat/123-scheduled-tasks was clean.
  • Read candidate server startup assembly and scheduler service acquisition at apps/server/src/server.ts and apps/server/src/scheduler/Service.ts. Scheduler.start subscribes to the engine and settings streams, then uses Effect.forkScoped for consumers and a reconciliation loop. The only awaitCommandReady is inside that forked loop. This source path does not establish that Scheduler layer acquisition waits on command readiness.
  • Runtime evidence supplied to this review reports PID 78462, port 13832, and /.well-known timeouts at routesReady; the trace ends at server.startup.http.wait, not auxiliary-roots.parked. I did not launch a server or browser. No exact-head combined proof manifest was present at inspection.
  • This is not a finding that Scheduler is cleared: source inspection cannot attribute the live startup timeout. Do not use this comment as approval or merge readiness. If the head changes, this review does not apply.

Elon record

Requirements and who asked: User requested read-only independent exact-head review and explicitly instructed not to post success while the root startup blocker remains active.
Deleted: A causal claim that the scheduler caused routesReady timeout; source evidence does not support it.
Bottleneck: Attribution and resolution of the isolated startup timeout.
Checked myself: Verified live PR head/base and clean worktree; traced route readiness, scheduler layer wiring, stream subscription acquisition, and the location of awaitCommandReady without running a server or browser.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.5 KiB 13.5 KiB +18 B (+0.1%) 15.1 KiB ✅
Codex Thread snapshot wire 7.1 KiB 7.1 KiB −1 B (−0.0%) 7.3 KiB ✅
Codex Live turn WebSocket wire 6.4 KiB 6.4 KiB +19 B (+0.3%) 7.8 KiB ✅
Codex Live turn WebSocket decoded 56.2 KiB 56.2 KiB 0 B (0.0%) 66.4 KiB ✅
Codex Live turn messages 9 9 0 (0.0%) 21 ✅
Claude Total thread wire 13.5 KiB 13.5 KiB −6 B (−0.0%) 15.1 KiB ✅
Claude Thread snapshot wire 7.1 KiB 7.1 KiB +1 B (+0.0%) 7.3 KiB ✅
Claude Live turn WebSocket wire 6.4 KiB 6.4 KiB −7 B (−0.1%) 7.8 KiB ✅
Claude Live turn WebSocket decoded 57.0 KiB 57.0 KiB 0 B (0.0%) 66.4 KiB ✅
Claude Live turn messages 9 9 0 (0.0%) 21 ✅

Baseline: 7f0a8ff · PR result: ca9e537 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 114.0 KiB
  • Claude decoded thread snapshot: 114.7 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

What: Candidate a650c231c27dca2bbb9b3faea77e88c479626773 has a blocking continuation defect for stopped provider sessions.
Why: The scheduler equates a stopped provider session with explicit thread retirement and refuses to send the same-run continuation.
So what: Fix and prove this in the bounded server repair, then review the new exact head; this finding does not clear the separate startup blocker.

Finding: stopped provider sessions are incorrectly treated as retired

Location: apps/server/src/scheduler/Service.ts:243-246 and apps/server/src/scheduler/Service.ts:55; recovery path apps/server/src/scheduler/Scheduler.ts:208-218.

When an existing scheduled thread's provider session transitions to stopped but the explicit engine.getThreadRetirement(thread.id) state is not retired, observe still returns retired: true. drive immediately converts the active run to needs-you and returns, and runNow refuses it as requiring explicit recovery. Even removing that classification alone is insufficient: schedulerIdle also rejects session.status === "stopped", so the normal same-thread send path cannot proceed.

This is reachable without retirement: the provider runtime marks sessions stopped on session closure and stop operations, while the orchestration model stores retirement separately. The existing ProviderCommandReactor.ensureSessionForThread explicitly treats a stopped session as inactive and can start a new provider session for a later turn. Thus a scheduled turn that has produced work, whose provider session then stops before its outcome check passes, becomes stranded rather than following #123's same-thread continuation ladder. Keep the explicit retirement/unavailability check, but allow a stopped provider session to be reinitialized if the thread itself is not retired; cover observation, idle/admission and continuation in a focused test.

Review scope and evidence

  • Live PR remains open at the exact head above; base is 7f0a8ff68a429202d89619aecdd8b64289fd3934; worktree was clean.
  • Read candidate scheduler observation/recovery, ProviderCommandReactor.ensureSessionForThread (apps/server/src/orchestration/Layers/ProviderCommandReactor.ts), and provider session stop semantics (apps/server/src/provider/Layers/ProviderService.ts). No tests or runtime were run for this finding.
  • Startup remains a separate unresolved runtime blocker. Source inspection of Scheduler.start shows its awaitCommandReady only inside a forked loop, but that does not clear the observed routesReady timeout.

Elon record

Requirements and who asked: #123 requires same-thread continuation after work and failures; the user explicitly requires retired-thread recovery only for actually retired threads.
Deleted: Treating every stopped provider session as equivalent to retirement.
Bottleneck: The current retirement predicate blocks the recovery ladder before the existing provider reactor can reopen the same thread.
Checked myself: Traced the exact scheduler observation-to-needs-you path and verified the existing provider reactor handles stopped sessions by treating them as inactive before starting a later turn.

@lukemaj

lukemaj commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Agent work on this PR

Estimated cost $0.00 · 0 responses · 54 sessions · 4.6 h wall time

Model Responses Tokens Estimated cost

Flags: 6 human corrections · 110 large tool outputs · 54 repeated commands · 15 repeated failures · 36 repeated reads · 11 repeated skill loads · 54 sessions with usage bound to no task

Details: snapshot, prices, coverage, counters
  • Task toolboxmd/chromeria#123: outcome unknown (recorded acceptance only; a finished process never implies it).
  • Proof: Scheduler: tasks run until a verifiable check passes #123
  • Snapshot 6774c9f5fd058c412df7e80f54537c4db8790e2a4ac437e5ead8c7887ae9d8da, records up to 2026-10-02 00:00 UTC.
  • 54 sessions on claude, codex; AgentsMD 14.6.0.
  • Not counted: 3,195 responses (at least $29.96) in sessions shared with other PRs that worked in no single PR's checkout.
  • 3,184 responses in these sessions worked on other PRs and are counted there.
  • Totals reconcile with the measured sessions: yes. Evidence complete: yes.
  • Prices: list-price estimate from T3 local rate table (path withheld) as of 2026-09-28, schedule 696aae45933d0a684a015d3f08cfb6f1aa2fef02e7d272b4689e3a692ea04fff. Unknown prices stay unknown, never zero.
    • T3 LiteLLM rate table when present; bundled schedule covers the rest.
  • Harness-reported cost: none reported.
  • Usage totals are not billing. Subscription spending is separate and is never posted as spend.
  • Crashed runs are counted separately: 0.

Token counters by model (native counter semantics; never added across semantics):

Selected rates (USD per million tokens). These rates value the report at the selected schedule date; they do not establish historical prices or subscription spending.

Model Input tier Input Cache read Cache write Other output Reasoning

Other output and reasoning are priced without double counting inclusive native output. Missing rates remain unknown.

Local measurement from native records; usage totals are not billing. Updated in place by agent-observer publish.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

What: Candidate a650c231c27dca2bbb9b3faea77e88c479626773 also permits retained completed run evidence to be rewritten by the scheduler decider.
Why: The immutability guard explicitly skips prior done runs, allowing a later state command to change their pinned judge or erase the passing result while preserving done.
So what: Preserve immutable evidence for retained runs and prove the rejection in the bounded server repair; exact-head review remains failed pending repairs and startup clearance.

Finding: done runs can lose their pinned judge and verified result

Location: apps/server/src/orchestration/decider.ts:281-295 (and check-presence exemption at lines 243-246).

When a scheduler.state.set command carries a prior run still retained in previous.runs with status done, changedJudge returns false without comparing any of its fields. The decider therefore accepts a next task state that changes that run's checkVersion, pinned definition/cwd/slot, check from passed to failed/null, or even removes the done status, despite the append-only history claiming the run was verified under its original check. SchedulerStateCommand is excluded from public dispatch, but the decider is the durable invariant boundary for internal producers too; the requirement is that a done run remains a truthful record of the outcome that settled it. Keep pruning of old completed rows if intended, but reject mutation of any retained completed row and require its done state to have a passing pinned verdict.

Startup investigation update

Coordinator evidence now shows Scheduler.layer acquired through layer.ready at 01:08:10.886, before runtimeStateParked and the http.wait timeout. That falsifies the suspected synchronous Scheduler acquisition cycle. routes.ready and cloudLinkParked remain absent in the supplied trace, so the isolated startup failure is still unresolved and outside a confirmed scheduler cause.

Review evidence and Elon record

Requirements and who asked: #123 requires only a passing immutable pinned check to settle a run; the user requires cumulative exact-head review and prohibits success until startup is cleared.
Deleted: Accepting mutable done audit rows on the assumption that only current service code writes state.
Bottleneck: The decider's retained-history guard must protect completed as well as unfinished evidence.
Checked myself: Read the candidate decider checks and engine public-command rejection path, confirmed old done rows are retained in recent task state, and reviewed the coordinator's updated startup trace. No tests or runtime were run.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Measurement gap: Agent Observer publication succeeded, but its task lookup exited 3 and the published report attributes zero responses while flagging 54 sessions with usage bound to no task. The displayed $0.00 is not a measured total cost for #123. No missing usage has been treated as zero; task cost remains unknown pending attribution repair.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Clarification to the stopped-session finding on this exact candidate:

  • ThreadRetirement is explicit durable state derived from thread.subtree-retire-requested; a raw provider/session stop intent is not that state. The Stopping a thread does not stop its children; report-backs revive it #122 retirement tests require the real retirement event and its matching provider-stop acknowledgement before declaring the subtree quiescent.
  • A completed provider stop or closed-session status can leave thread.session.status === "stopped" while engine.getThreadRetirement(thread.id)?.retired is false. ensureSessionForThread treats that stopped session as inactive and the thread.turn.start reactor can establish a new session on the same thread.
  • The scheduled continuation path instead marks this observation retired at apps/server/src/scheduler/Service.ts:238-241; drive changes it to needs-you at apps/server/src/scheduler/Scheduler.ts:209-217. Independently, schedulerIdle rejects stopped status at Service.ts:48-57, preventing a later send even if the retirement classification is removed.
  • The meaningful case is a scheduled run with work and a failed check on a still-live, non-retired target thread whose provider session has stopped/closed. It must continue the same pinned run/thread/check via the normal provider restart path. Preserve the distinct refusal for a durably retired thread, including after its stop acknowledgement; do not infer retirement from a raw intent or provider session status.

No runtime or test was run by this reviewer. Candidate remains a650c231c27dca2bbb9b3faea77e88c479626773; this clarification does not change the existing failure status.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

What: Candidate a650c231c27dca2bbb9b3faea77e88c479626773 fails MCP route registration because the scheduled-task list tool has an invalid empty input JSON schema.
Why: Schema.Struct({}) emits an object schema without the required type, and this registration error aborts route readiness during startup.
So what: Fix the list tool's input schema and prove toolkit registration on the new exact head before requesting review again.

Finding: list tool schema aborts MCP/routes startup

Location: apps/server/src/mcp/toolkits/scheduler/tools.ts:23-29, registered by apps/server/src/mcp/McpHttpServer.ts:687-691.

When SchedulerToolkit registers, list_scheduled_tasks declares parameters: Schema.Struct({}). That empty schema is serialized with root value not rather than a typed object, causing the registration validation error Missing type; because this toolkit is built into the MCP HTTP route layer, the error aborts route acquisition and leaves routesReady unresolved. The other five scheduler tools use nonempty object input schemas, and the array success schema is not the cause. This matches the coordinator's startup trace and registration test result. Use an explicit valid empty-object JSON Schema/input contract and add the focused registration regression; avoid general startup lifecycle changes.

Elon record

Requirements and who asked: #123 exposes scheduler tools through MCP; the user requires the actual startup cause be verified and fixed in the narrow server phase.
Deleted: Treating the routesReady timeout as an unresolved scheduler acquisition or cloud-link issue after this registration failure was identified.
Bottleneck: MCP schema validation fails while building the routes layer.
Checked myself: Read candidate SchedulerToolkit and McpHttpServer registration lines, confirmed this is the only empty Schema.Struct({}) parameter declaration in MCP toolkits, and matched the root cause to the coordinator's Missing type registration evidence. Candidate remains unchanged.

@lukemaj

lukemaj commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

What: Independent exact-head review found no code-blocking findings at 0a1ea1a98b1e820859ad8d4a5e9d3f19e6c4fc41.
Why: The three findings on a650c231 are repaired, and the final runtime delta after fdc7e2f9 changes only server test fixtures.
So what: The PR remains draft while the root's integrated client pass completes or is explicitly deferred; real installed-provider runs remain post-merge proof.

Review

Reviewed the full cumulative diff from base 7f0a8ff68a429202d89619aecdd8b64289fd3934 through 0a1ea1a98b1e820859ad8d4a5e9d3f19e6c4fc41, relevant scheduler, send/reactor, Spectrum, ownership and Settings consumers, and the complete fdc7..0a1 delta. That final delta is only apps/server/src/server.test.ts: mocked settings/engine fixtures now provide empty subscriptions and scheduled tasks. Runtime and UI source are unchanged from the cleared Node 24 startup probe at fdc7.

The three prior findings are closed:

  1. The scheduler MCP list tool now uses an object-shaped empty input schema; the added test registers the toolkit with the real McpServer layer.
  2. A provider session's stopped state no longer implies durable thread retirement. Only the engine's retirement state blocks recovery. The integration test exercises the real stop acknowledgement and confirms the same scheduled run resumes through the provider reactor without repeating its side effect, while explicit retirement still blocks it.
  3. The decider now rejects a done run without a passing verdict from its pinned check version and rejects changes to a retained completed run. The integration test rejects verdict/judge mutation while allowing bounded eviction.

Proof reviewed

The exact-head combined manifest is /tmp/promachos-coord/evidence/123/final-proof.json, with server details in server-startup-final-proof.json and UI details in final-ui-proof.json. On Node v24.18.0, 18 server test files passed (493 tests), 3 UI test files passed (93 tests), and all 12 recorded test, typecheck, lint, registry, fork-check and diff-check commands exited 0. Lint reported existing warnings in server.test.ts and SettingsSidebarNav.tsx; no repository-wide gate is claimed.

The root's integrated client pass verified All scope, missing-check validation, refusal when the check exits 0, creation/pause with exit 1, Run Now disabled state, and authored version 2 check history. Revert submission then failed because the T3 preview host disconnected with PreviewAutomationNoAvailableHostError. Revert, window behavior and the after-image remain unverified; this is an evidence gap, not a confirmed application defect. The paused 1440-minute fixture has not run. No live provider run is claimed.

Requirements and who asked: #123 check-gated scheduled work and the user's exact-head independent review; the coordinator requires one draft PR and final client evidence before changing draft state.
Deleted: MCP schema registration failure, stopped-session/retirement conflation, and mutable retained done-run verdicts.
Bottleneck: The remaining delivery gap is root-owned client evidence after preview-host loss; source and targeted proof are settled.
Checked myself: Read the complete current #123 and parent #113 context and comments; reviewed the cumulative candidate and final fixture-only delta; verified PR head/base and the combined Node 24 manifests/logs. No code-blocking findings remain at this SHA.

lukemaj and others added 2 commits October 2, 2026 01:51
Knip reported five exports used only inside ScheduledTasksSettings.logic.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@lukemaj

lukemaj commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

What: Independent exact-head review found no code-blocking findings at ca9e5375da9ca4600c57985b6017cc9902e6a695.
Why: The three earlier scheduler findings are repaired, and the six export removals at this head are file-local cleanup with no consumer or behavior change.
So what: The PR remains draft: root client acceptance is partial and the current GitHub CI run is still pending.

Review

Reviewed the complete cumulative candidate against base 7f0a8ff68a429202d89619aecdd8b64289fd3934, the relevant scheduler, send/reactor, Spectrum, ownership and Settings consumers, and the final deltas. From 0a1ea1a to this head, the only changes are removal of six export keywords from schedulerIdle and five Settings logic helpers. Function bodies and rendering are unchanged; repository-wide searches found no external consumers. Compared with root's runtime-proven fdc7, the other changes are three empty server test-fixture methods; production blobs match after accounting for the six export modifiers.

The earlier findings remain closed: scheduler MCP list registration uses an object-shaped empty schema; provider stopped does not substitute for durable retirement and same-run recovery is covered; done runs require their pinned passing verdict and retained records cannot be changed.

Proof reviewed

The exact-head manifests are /tmp/promachos-coord/evidence/123/final-proof.json, /tmp/promachos-coord/evidence/123/final-ci-server-proof.json, and /tmp/promachos-coord/evidence/123/final-ci-ui-proof.json. Their logs show Node v24.18.0, 493 server tests across 18 files, 93 UI tests across 3 files, and all 14 recorded targeted test, typecheck, lint, registry, fork-check, diff-check and scoped Knip commands exiting 0. This is targeted proof, not a repository-wide gate.

Root's /tmp/promachos-coord/evidence/123/root-client-proof.json records passing scope, validation, create/pause and authored check-history observations. Revert, fixed-time window/neighbour behavior, task health screenshot, Settings search and fixture deletion remain unverified because T3 preview reported PreviewAutomationNoAvailableHostError. No complete UI acceptance or live installed-provider run is claimed. The current GitHub CI run still has Check and test jobs pending; local Knip passes do not substitute for those pending jobs. PR #132 remains draft.

Requirements and who asked: #123's check-gated, recoverable scheduled tasks and the user's exact-head independent review.
Deleted: The three a650 code findings and six genuinely unused public exports; no production behavior was changed by this cleanup.
Bottleneck: The remaining delivery gaps are root-owned client acceptance and the in-progress GitHub CI run, not a source finding in this candidate.
Checked myself: Read the full current #123 body/comments and parent #113, inspected the cumulative exact-head source and final deltas, verified PR head/base and all 14 manifest entries against their logs. No code-blocking findings remain at this SHA.

@lukemaj
lukemaj marked this pull request as ready for review October 2, 2026 00:09
@lukemaj
lukemaj merged commit 86d3269 into main Oct 2, 2026
20 checks passed
@lukemaj

lukemaj commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Merged by the coordinator as 86d3269 (squash) at verified head ca9e537: diff read (scheduler state lives in the orchestration log, no numbered migration; the startup scan takes about 0.1 s on the maintainer's 255k-event database), CI green on that head, review/independent success by lukemaj on that head. Coordinator decision: the unfinished dev-client checks (revert history, window and neighbour display, task-history screenshot, Settings search) were blocked by the preview host disconnecting, not by a code finding. They move to the installed-build pass, together with the live repeating and one-shot runs.

@lukemaj
lukemaj deleted the feat/123-scheduled-tasks branch October 2, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scheduler: tasks run until a verifiable check passes

1 participant