You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Shippable feature slice: complete conversational CREATE, READ, UPDATE, and DELETE for cron jobs.
Problem
The tool catalog exposes create/read/delete-oriented cron operations, but current GitHub main does not provide a complete conversational update path. Creation also needs a hard guarantee that a job is bound to the current run's tenant, agent, and conversation so later fires advance the same chat safely.
Independent review of PR #1057 confirmed three release blockers: SQLite reserves job names globally instead of within the immutable ownership scope; the assembled scoped lifecycle is proven only for embedded cron and not the remote HTTP client path; and persistence-first scheduler reconciliation can diverge from the stored job after a registration failure or overlapping update/delete.
Protected user path
A user can say: “Every five minutes check whether the website is deployed,” then ask the agent to inspect, change, pause/resume, or delete that job. The agent must perform each operation and return transcript-readable state. Independent tenant/conversation/agent scopes may reuse a conventional name without collision.
How to fix
Bind cron_create to RunMetadata at the harness boundary. Persist conversation ID, tenant ID, and agent ID from the current run; do not accept ownership overrides from tool arguments.
Add a cron_update model-facing tool and service operation. It may update schedule, prompt, timeout/config, tags, and enabled/paused state, but never job ID or ownership.
Keep explicit read operations for list/status/history and explicit delete/pause/resume operations. Return stable structured payloads containing job ID, schedule, state, next run, last result/run link when available, and scope-safe display metadata.
Register the new operation in builtin catalogs, permission declarations, schemas, and tool documentation.
Use optimistic versioning or an equivalent conflict check so concurrent updates do not silently overwrite one another.
Emit lifecycle events/results suitable for the chat transcript.
Replace SQLite's global name constraint with active-name uniqueness on (tenant_id, conversation_id, agent_id, name). Preserve legacy empty-scope rows and every execution during an atomic, idempotent table-rebuild migration. Model tools remain ID-only; operator name lookup must return a typed ambiguity error rather than choosing an arbitrary scoped row.
Serialize mutations per job across persistence and scheduler reconciliation. Scheduler replacement must register the replacement before removing the old entry, avoid re-registering non-schedule edits, compensate/reload after post-CAS reconciliation failure, and prevent overlapping update/delete from rearming stale or deleted state. Failed create registration must not leave an active stored orphan.
Acceptance criteria
CREATE binds a job to the active conversation scope.
CREATE supports explicit conversational/harness prompts while preserving legacy shell commands; shell commands and harness prompts are non-empty and type-distinct.
Active names are unique only within exact tenant + conversation + agent ownership; independent scopes may use the same name.
Existing databases migrate atomically and idempotently without losing jobs, executions, timestamps, or legacy empty-scope values.
READ lists only jobs visible to the current scope and returns one job's current status/history.
Model-facing CRUD/history remains ID-only. Operator name lookup either returns one unambiguous job or a typed ambiguity error.
The complete scoped model-tool lifecycle is proven over both embedded and remote HTTP client adapters; an out-of-scope job is not found, not mutated, and leaks no history.
UPDATE changes the next invocation's schedule/prompt/config and rejects stale/conflicting writes; model-facing updates require the version returned by cron_get.
Persistence and live scheduler remain aligned after registration failure, overlapping updates, and update/delete races.
Prompt/config/timeout/tag-only edits do not replace the live schedule entry.
PAUSE and RESUME preserve the job and visibly change its next-run state.
DELETE removes/cancels the job and a subsequent read reports not found.
Ownership fields cannot be mutated.
Invalid schedules, incomplete execution configs, and explicitly supplied non-positive timeouts produce actionable errors; omitted create timeouts retain the documented default.
Existing shell-target cron jobs remain compatible.
An assembled stateful model-tool lifecycle reaches the in-process harness starter with the originating conversation and typed run correlation metadata.
Test-first plan
Add failing tests for:
full create -> list/get/history -> update -> pause -> resume -> delete lifecycle in embedded and remote-client modes;
updated prompt and schedule used by the next invocation;
tenant/agent/conversation isolation and same-name reuse across independent scopes;
atomic/idempotent migration preserving legacy jobs and executions;
ambiguous operator name lookup;
forbidden ownership mutation;
stale update conflict;
scheduler registration failure compensation, concurrent update ordering, and update/delete serialization;
non-schedule edits preserving the live entry;
invalid schedule, execution-config, and timeout validation;
tool registration, schemas, and permission/catalog coverage;
transcript-safe result serialization.
Run:
go test ./internal/cron ./internal/harness/tools/deferred ./internal/harness/tools ./cmd/harnessd -count=1
go test -race ./internal/cron ./internal/harness/tools/deferred ./internal/harness/tools ./cmd/harnessd -count=1
./scripts/test-regression.sh
Scope boundaries
In scope: conversational cron CRUD, immutable scope binding, scoped-name schema/migration, unambiguous lookup, embedded and remote model-client lifecycle coverage, persistence/scheduler reconciliation, registrations/schemas, result shape, tests, and docs.
Out of scope: raw remote cronsd authentication and harness dispatch (#1003), overlap control and terminal linkage (#1004), callback changes, and macOS task controls.
Rollout / rollback
Ship additively after a stopped-process SQLite backup. On startup, both embedded harnessd and standalone cronsd run the idempotent migration before scheduler registration. Validate row counts, execution references, scoped-name index shape, and SQLite integrity before accepting traffic.
Existing shell jobs and legacy empty-scope rows must remain readable. Rollback should use a compatibility-capable binary while retaining the migrated schema. A schema downgrade to global name uniqueness is permitted only after a global duplicate-name preflight succeeds; if cross-scope duplicates exist, abort rather than dropping, merging, or silently renaming data.
Definition of done
One PR closes this issue, uses the repository's full change form requirements, includes an impact map and test-first evidence, updates tool/reference docs and indexes, passes focused normal/race and full repository verification, is rebased after dependency gates #1070/#1055 land, and proves persistence/live-scheduler convergence plus multi-turn same-chat CRUD in both embedded and remote-client modes.
Parent epic: #1000
Depends on: #1001.
Work type
Shippable feature slice: complete conversational CREATE, READ, UPDATE, and DELETE for cron jobs.
Problem
The tool catalog exposes create/read/delete-oriented cron operations, but current GitHub main does not provide a complete conversational update path. Creation also needs a hard guarantee that a job is bound to the current run's tenant, agent, and conversation so later fires advance the same chat safely.
Independent review of PR #1057 confirmed three release blockers: SQLite reserves job names globally instead of within the immutable ownership scope; the assembled scoped lifecycle is proven only for embedded cron and not the remote HTTP client path; and persistence-first scheduler reconciliation can diverge from the stored job after a registration failure or overlapping update/delete.
Protected user path
A user can say: “Every five minutes check whether the website is deployed,” then ask the agent to inspect, change, pause/resume, or delete that job. The agent must perform each operation and return transcript-readable state. Independent tenant/conversation/agent scopes may reuse a conventional name without collision.
How to fix
(tenant_id, conversation_id, agent_id, name). Preserve legacy empty-scope rows and every execution during an atomic, idempotent table-rebuild migration. Model tools remain ID-only; operator name lookup must return a typed ambiguity error rather than choosing an arbitrary scoped row.cron.Server -> cron.Client -> cronClientAdapter -> scoped client -> model tools, including owned history and cross-scope not-found behavior. Raw cronsd authentication remains [Bug]: Execute harness jobs through remote cronsd instead of the shell executor #1003.Acceptance criteria
Test-first plan
Add failing tests for:
Run:
Scope boundaries
In scope: conversational cron CRUD, immutable scope binding, scoped-name schema/migration, unambiguous lookup, embedded and remote model-client lifecycle coverage, persistence/scheduler reconciliation, registrations/schemas, result shape, tests, and docs.
Out of scope: raw remote cronsd authentication and harness dispatch (#1003), overlap control and terminal linkage (#1004), callback changes, and macOS task controls.
Rollout / rollback
Ship additively after a stopped-process SQLite backup. On startup, both embedded harnessd and standalone cronsd run the idempotent migration before scheduler registration. Validate row counts, execution references, scoped-name index shape, and SQLite integrity before accepting traffic.
Existing shell jobs and legacy empty-scope rows must remain readable. Rollback should use a compatibility-capable binary while retaining the migrated schema. A schema downgrade to global name uniqueness is permitted only after a global duplicate-name preflight succeeds; if cross-scope duplicates exist, abort rather than dropping, merging, or silently renaming data.
Definition of done
One PR closes this issue, uses the repository's full change form requirements, includes an impact map and test-first evidence, updates tool/reference docs and indexes, passes focused normal/race and full repository verification, is rebased after dependency gates #1070/#1055 land, and proves persistence/live-scheduler convergence plus multi-turn same-chat CRUD in both embedded and remote-client modes.