feat(cron): bind scheduled jobs to a project's agents - #9201
Conversation
|
Intent: Let a scheduled cron job optionally bind to a project directory, so both the Schedule page's agent picker and fire-time execution can use that project's own |
Design Review (Fable 5, fork) — 🟡 CONCERNSDesign-level review of Design-Verdict: CONCERNS Sound, root-cause design mirroring chat's project-agent resolution — but the PR ships stray binary screenshots and describes a Watch
Suggestions
[DESIGN-REVIEWED] 7b37afb |
UX Review (Fable 5, fork) — 🟡 CONCERNSUX-level review of All evidence reviewed: the frontend hunks, backend error strings, sibling vocabulary, and the (non-materialized) screenshots. Emitting the review. UX-Verdict: CONCERNS One concept ships under three names — "Working directory" (form), "Operating folder" (fire-time errors), "project folder" (rest of the product) — and no first-time reader has seen the new field. Watch
Evidence gaps
Suggestions
[UX-REVIEWED] 7b37afb |
First Principles Review (Fable 5, fork) — 🟡 CONCERNSPremise-level review of First-Principles-Verdict: CONCERNS New Not justified as shipped
What this change shipsInventory (10 items) — 7 justifiedIntent: let a scheduled job run one of a project folder's own agents instead of only global ones — an ADDITION (closes #8952), with one declared rider fix.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 7b37afb |
GPT 5.6 Review (fork) — 🔴 changes requested (blocking)Reviewed 2 of 3 blocking finding(s) are security-class and were withheld from adjudication, so the blocking verdict stands. BLOCKING -- docs/feature-map/README.md:149 -- Schedule ownership omits the project-agent handler (origin: validation) BLOCKING -- src/kiro_crew/cron.py:1549 -- Normalize persisted project paths before use BLOCKING -- website/src/components/JobForm.tsx:578 -- Clearing a folder retains its project-only agent FINDING -- website/src/components/JobForm.tsx:315 -- FINDING -- .github/black-baseline.txt:313 -- removing [BLOCK-MERGE] 7b37afb Adjudication (Opus 4.8) — is blocking on each finding proportionate?F1 is anchored to the F2: F3: The clear-folder path leaves |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsBased on my analysis of the diff and base code: Candidate 2 — The sequential missing-folder skip omits Candidate 3 — Owner-gated ( Candidate 1 — Confirmed: the new No blocking issues; one advisory finding. FINDING — src/kiro_crew/slack/gateway.py:1811 — [OPUS-REVIEWED] 7b37afb |
d27f62b to
d48d067
Compare
|
|
|
|
|
|
|
|
|
d48d067 to
b1147c7
Compare
|
|
|
|
8da4342 to
e82f80f
Compare
Re-affirming the earlier rebuttal on |
e82f80f to
5941f07
Compare
|
5941f07 to
85dfa03
Compare
Fixed: pass |
85dfa03 to
23cd410
Compare
Fixed: merged the working-directory block into the same |
23cd410 to
5b5502e
Compare
Fixed exactly as recommended: deleted
All other items in the review's inventory (owner gate, precedence fix, roster fallback, error-surfacing fix) were already marked justified — untouched here. Verified: 690 backend tests across the touched files + full |
5b5502e to
cfe8076
Compare
|
Full local gate re-run on this head: comment-history/isort/flake8/mypy (1382 files) clean; 944 backend tests (all touched files) + 158 frontend tests (JobForm + i18n catalog parity) + |
cfe8076 to
23e969c
Compare
Verified this round: black/isort/flake8/mypy(1382 files)/comment-history (re-baselined: this round's removals shrank |
23e969c to
ef1bd87
Compare
ef1bd87 to
7d62453
Compare
|
AI-review override not recorded: only a repository writer may override an AI finding. |
Status: rebased onto current
|
Adds an optional operating folder to a cron job, so its agent picker and fire-time execution can use project-scoped agents from <project>/.kiro/agents/*.json instead of only global agents. Fire-time behavior when the folder existed at save time but is gone by the time the job runs: the run is skipped and recorded as a failure, since executing it against the wrong (global-fallback) agent would silently do the wrong thing. Closes kirodotdev#8952
Problem / Motivation
Cron jobs can only run global agents from
~/.kiro/agents/. A project that defines its own agents in<project>/.kiro/agents/*.jsoncannot use them from a scheduled job — the Schedule page's agent picker only ever lists global agents, and even naming a project agent by hand at fire time silently falls back to the default global agent.Why it matters
Project-scoped agents are how a repo customizes tool access, prompts, and permissions for its own workflows. Anyone who wants a cron job to act as one of those agents (e.g. a nightly job that runs a repo's own release-checklist agent) currently cannot — the job always runs as a global agent instead, with no error or warning that anything was substituted.
What changed (motivation → approach → change)
The gap has two parts: the dashboard's agent picker only reads global agents, and cron fire-time execution only resolves agent names against the global registry. Both needed the same missing piece — a way to ask "what agents does this folder define" — so a scheduled job's
project_pathbecame the one new field threading through both.On the backend,
CronJobgains an optionalproject_path(validated the same way project directories are validated elsewhere: absolute path, not a sensitive path, must exist).GET /api/agentsgained aproject_pathquery-param fallback for callers with no live chat session behind them (the Schedule page's job form has no session to key off of). Cron fire-time execution resolvesjob.project_paththrough the existing project-agent-discovery path before launching, so the job's agent behaves exactly like a chat session opened in that folder.A folder that existed when the job was saved but is gone by fire time is treated as a normal failure, not run against the wrong (global) agent: the job is skipped, marked
last_status: errorwith a message naming the missing folder, and no auto-pause strike is spent — mirroring how an "overlapping run" refusal is already handled elsewhere in this file.On the frontend, the job form gained an "Operating folder" field reusing the same folder picker component already used for new-chat and folder settings, so the interaction is consistent across the app. Selecting a folder merges its project-scoped agents into the picker (global agents win on a name collision), and the field's own validation errors are shown as a field-scoped message rather than a raw backend string.
Tests
test/test_cron.py: default-empty, valid-path persists across a reload, nonexistent/relative/sensitive-path rejection, update sets/clears the path, an invalid update leaves the existing path unchanged.test/test_dashboard_cron_approval.py:project_path/project_path_missingpresent (and correctly typed) in the job list response, andNonewhen absent.test/test_cron_gateway_integration.py: a missing operating folder at fire time skips the run and records an error (both the single-agent and sequential-agent paths), the flag persists, and a job whose folder still exists runs normally (control case).test/test_api_agents_order.py/test/test_agents_roster_contract.py: existing tests updated for a session-precedence fix found while building this (see below).website/src/test/JobForm.saveError.test.tsx: a rejected save surfaces the real backend message (not a generic "failed to save"), including the three project-path-specific error remaps.While building this, live testing surfaced a real precedence bug in the existing
GET /api/agentshandler: it resolved project scope through a helper whose "single project shared by every open slot" fallback fired for any session key, including the sentinel every request without a real session sends. That let an unrelated open chat tab's project silently override an explicitproject_pathquery param. Fixed by switching to the narrower session-scoped resolver that returnsNonefor a sentinel key with no cross-slot fallback — unrelated to this feature's own code path, but found and fixed in the same diff since the new fallback depends on it behaving correctly.Manual verification
Live-tested end to end against a dev gateway: created a cron job with an operating folder set, confirmed the agent picker showed that folder's project agents merged with globals, saved and re-opened the job to confirm the folder persisted and round-tripped correctly, and fired the job to confirm it ran the resolved project agent in the correct working directory. Also verified the missing-folder path by deleting a job's folder after save and confirming the next fire skipped with a normal error rather than running against the wrong agent.
Screenshots / video
Related Issues
Closes #8952
Pattern harvest
Not generalizable: the session-precedence fix (
requesting_slot_projectvs. the shared-fallback resolver) is specific to this one call site's existing behavior, not a repeated pattern elsewhere in the diff.Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)