From 6b970c753b5ba2aa499b6ba0d09bb9715e2607c0 Mon Sep 17 00:00:00 2001 From: SebastianYuSun Date: Fri, 7 Aug 2026 23:03:37 +0000 Subject: [PATCH] feat(taskrunner): surface a spec's declared `approval: auto` before launch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The autonomous task runner prompted for per-action tool approval on every step and ignored the dashboard's "Trust All" control. That trust lives on the interactive chat slot (DashboardState slot `_trust`), while the runner executes each step in its own per-task session (`taskrunner:{task_id}:task{N}`). The gateway's `_interactive_approval("taskrunner")` callback checks the parent slot's `_trust` — not the runner's synthetic slot — so the session-scoped trust signal never reaches the runner's EVENT_PERMISSION_REQUEST loop. A spec may now DECLARE its intended approval mode in leading YAML frontmatter, so a trusted plan can carry that intent in version control: --- approval: auto --- # Task: ... **A declaration is not a grant, and this is the whole design.** The declaration is REPORTED to the human before they press Execute, as a read-only `declared_approval` field on the plan response rendered beside the auto-approve checkbox. The checkbox is NOT pre-checked. Unattended execution is granted only by the launching human's own request body, and both launch paths derive it from `auto_approve` alone: auto_approve = await _gate_auto_approve( request, body.get("auto_approve") is True, ..., endpoint=...) An earlier revision of this change OR-ed the declaration into that grant. Three reviews (GPT 5.6, First Principles, Design) independently named the same defect: a server that derives a grant from spec content lets a spec obtained from an untrusted source disable that human's approval prompts on its own authority, and the launch-provenance gate does not catch it because it checks *who launched*, not *whether the human consented to unattended*. The `or` is gone. Removing the OR removes the reason most of this code existed, so this revision deletes far more than it adds: - `parse_spec_approval_mode` (240 lines) → `spec_declares_auto(text) -> bool`. While a declaration was a grant, every reading of it was an authorization decision, so the scanner had to defend every surface a directive could hide in: a bare top-of-file window, code fences, HTML comments, wrapper elements. Reporting intent carries no such burden. Deleted with it: `SPEC_APPROVAL_MODES`, `_SPEC_APPROVAL_RE`, `_SPEC_FENCE_RE`, `_HTML_TAG_RE`, `_markup_rejected_lines`, `_SPEC_APPROVAL_SCAN_LINES`, and the fence state machine. The markup-splicing hole those defended (`approval:per-task auto` synthesizing `approval: auto`) is now structurally absent: the region is a delimited frontmatter fence, not a prose window. - `_approval_head` / `_SPEC_APPROVAL_READ_CHARS` and the whole snapshot path (`_write_spec_snapshot`, `snap-/`, `_full_spec`). The read/execute TOCTOU they closed was a consequence of authorization flowing from file bytes; with the grant in the request body there is no content-derived decision to pin. These bounds also existed to keep an attacker-sized body off the gateway event loop — a concern that was real (a quadratic rescan in the markup scanner measured 35s on a 120KB body before it was bounded) and that deleting the scanner retires outright rather than bounds. Parsing is consolidated rather than kept separate: `frontmatter.py` gains a fourth dialect, `TASK_SPEC`, which is what retires this feature's bespoke scanner — the module's docstring requires each frontmatter grammar be expressed exactly once, and a fifth caller keeping its own copy is what the reviews objected to. (Fourth dialect, fifth caller: the discover preview deliberately shares `SKILL_LOADER`.) `TASK_SPEC` adds one axis, `reject_duplicate_keys`: a key declared twice is dropped entirely rather than resolved by position. `parse_frontmatter` otherwise resolves duplicates silently, and for a security-relevant declaration the line a human reads first need not be the line a positional rule honors — so two conflicting `approval:` keys read as "nothing was declared". Tests: `spec_declares_auto` unit tests (frontmatter only; a bare directive, an indented occurrence, an unterminated fence, a duplicate-key contradiction, and the four markup payloads that defeated the old scanner all report False), plus handler tests asserting the load-bearing property directly — a spec declaring `auto` with no request flag gets NO auto-approval on either `/start` or `/execute` — and that `/plan` reports the declaration without acting on it. `force_approval` gates and hook deny-lists still block regardless. Refs #2068 --- docs/system-specs/modules/taskrunner.md | 50 ++++++ .../dashboard/handlers/taskrunner.py | 11 +- src/kiro_crew/frontmatter.py | 49 +++++- src/kiro_crew/task_planner.py | 45 +++++ .../projects-declared-approval-detail.png | Bin 0 -> 9456 bytes .../projects-declared-approval.png | Bin 0 -> 67305 bytes test/test_auto_approve.py | 164 ++++++++++++++++++ test/test_frontmatter.py | 36 +++- test/test_spec_approval_mode.py | 154 ++++++++++++++++ website/src/i18n/locales/bn.json | 2 + website/src/i18n/locales/de.json | 2 + website/src/i18n/locales/en-XA.json | 2 + website/src/i18n/locales/en.json | 2 + website/src/i18n/locales/es.json | 2 + website/src/i18n/locales/fr.json | 2 + website/src/i18n/locales/hi.json | 2 + website/src/i18n/locales/it.json | 2 + website/src/i18n/locales/ja.json | 2 + website/src/i18n/locales/ko.json | 2 + website/src/i18n/locales/pt.json | 2 + website/src/i18n/locales/ru.json | 2 + website/src/i18n/locales/zh-CN.json | 2 + website/src/pages/ProjectsPage.tsx | 18 ++ 23 files changed, 546 insertions(+), 7 deletions(-) create mode 100644 temp-screenshots/spec-declared-approval/projects-declared-approval-detail.png create mode 100644 temp-screenshots/spec-declared-approval/projects-declared-approval.png create mode 100644 test/test_spec_approval_mode.py diff --git a/docs/system-specs/modules/taskrunner.md b/docs/system-specs/modules/taskrunner.md index eae3d6e5433..69ffb83ab61 100644 --- a/docs/system-specs/modules/taskrunner.md +++ b/docs/system-specs/modules/taskrunner.md @@ -296,6 +296,56 @@ On gateway restart, any task with `status == "running"` is automatically transit - `cli_server.py` constructs the standalone `kirocrew run TASK.md` runner without an approval callback. Use `force_approval`, not `requires_approval`, for an action that must not execute unattended. - The dashboard supplies the callback and renders Approve/Deny controls in the project detail view. +### Spec-declared approval mode + +A spec may DECLARE its intended approval mode in leading YAML frontmatter, so a +trusted plan carries that intent in version control: + +```markdown +--- +approval: auto +--- +# Task: refactor the widget +``` + +**A declaration is not a grant.** The server derives auto-approval from the +`auto_approve` field of the launching human's request body and from nothing else +— `_gate_auto_approve` is still the only decision point, and the declaration is +never OR-ed into it. So a spec obtained from an untrusted source (repo, chat, +download) cannot disable the launching human's approval prompts, which is what a +content-derived grant did: the provenance gate checks *who launched*, not +*whether that human consented to unattended execution*, so a dashboard launch of +someone else's spec passed the gate on the launcher's own standing. + +What the declaration does instead is reach the person who owns the checkbox: + +- `task_planner.spec_declares_auto()` reports it. `True` only for a literal + `auto` (case-insensitive) as a top-level frontmatter key; the grammar is + `frontmatter.TASK_SPEC`. +- `POST /api/taskrunner/plan` returns it as read-only `declared_approval`, at no + extra disk read — `spec_content` is already in memory at plan time. +- The project detail view renders it beside the existing auto-approve checkbox + and leaves that box **unchecked**. The trust decision stays a deliberate click + at Execute time, matching the existing rule that a planned or resumed run + shows unchecked because its live grant was torn down. + +Because the reading is advisory rather than authorizing, it needs none of the +hardening a content-derived grant needed. Two consequences worth stating: + +- Only frontmatter counts. A bare top-of-file `approval:` line is not a + declaration, so there is no window of arbitrary document text to scan and no + code-fence or HTML-markup class to defend against. +- **Duplicate keys fail closed.** `parse_frontmatter` returns a dict, so two + `approval:` keys would otherwise be resolved silently by position — and + whichever declaration a human reads first need not be the one the parser + honored. `TASK_SPEC` sets `reject_duplicate_keys`, so two or more occurrences + report "not declared". + +Operators who want an unattended *background* source still use the separate +explicit opt-in `hooks.auto_approve_sources` (e.g. add `"taskrunner"`), which is +consent scoped to a source rather than to a file's contents. `force_approval` +gates block regardless. + ## Parallel Execution Parallel groups are throttled to prevent resource exhaustion from simultaneous kiro-cli cold starts. Each kiro-cli cold start spawns MCP server child processes, so concurrent tasks multiply startup pressure. diff --git a/src/kiro_crew/dashboard/handlers/taskrunner.py b/src/kiro_crew/dashboard/handlers/taskrunner.py index 0c3f7a0441a..b143146d32d 100644 --- a/src/kiro_crew/dashboard/handlers/taskrunner.py +++ b/src/kiro_crew/dashboard/handlers/taskrunner.py @@ -12,7 +12,7 @@ from kiro_crew.dashboard.state import DashboardState from kiro_crew.security import is_sensitive_path, redact_credentials, redact_exfiltration_urls -from kiro_crew.task_planner import plan_to_yaml +from kiro_crew.task_planner import plan_to_yaml, spec_declares_auto logger = logging.getLogger(__name__) @@ -565,6 +565,15 @@ async def api_taskrunner_plan(request: web.Request) -> web.Response: [s.index for s in group] for group in state.task_runner._group_parallel_tasks(run.tasks, set()) ], + # READ-ONLY report of what the spec DECLARED, so the reviewing human + # sees it before pressing Execute and can grant unattended execution + # through their OWN request body (the auto-approve checkbox). The + # server never reads this back: `/execute` derives auto-approve from + # `auto_approve` alone, so a spec cannot grant itself anything. + # + # Costs no disk read — `spec_content` was captured in memory at plan + # time and is the same snapshot the run executes from. + "declared_approval": spec_declares_auto(run.spec_content or ""), } ) diff --git a/src/kiro_crew/frontmatter.py b/src/kiro_crew/frontmatter.py index c89d96f7b2d..7021a884230 100644 --- a/src/kiro_crew/frontmatter.py +++ b/src/kiro_crew/frontmatter.py @@ -1,6 +1,6 @@ """Single home for hand-rolled SKILL.md frontmatter parsing. -Four backend callers parse ``key: value`` frontmatter from markdown, and each +Five backend callers parse ``key: value`` frontmatter from markdown, and each historically carried its own copy of the scanner. The copies drifted: they disagree on whether the opening fence may carry trailing text or leading whitespace, whether an indented ``key: value`` line is a field or prose, @@ -18,7 +18,16 @@ :data:`SKILL_LOADER` rather than owning a dialect: what the preview shows must match what the skills loader computes after install. -A FIFTH consumer lives outside Python and cannot be reached from this list by +A fifth caller — a task spec's declared approval mode +(``task_planner.spec_declares_auto``) — arrived with its own scanner and was +folded in as :data:`TASK_SPEC` rather than kept separate, which is what +retired that copy's bespoke code-fence and HTML-markup machinery: a delimited +frontmatter region needs neither. It adds no quirk the mirror below must +track: :data:`TASK_SPEC` is a new dialect, and its one new axis +(``reject_duplicate_keys``) is off for every pre-existing dialect, so +:data:`SKILL_LOADER`'s grammar is byte-for-byte what it was. + +A SIXTH consumer lives outside Python and cannot be reached from this list by import: the skill editor's frontmatter splicer (``website/src/components/SkillForm.tsx``) MIRRORS :data:`SKILL_LOADER`'s grammar in TypeScript, because it must never write a value this reader would @@ -90,6 +99,15 @@ class FrontmatterDialect: # True: the first occurrence of a duplicate key wins (single-value lookup # semantics). False: the last occurrence wins (dict-overwrite semantics). first_key_wins: bool = False + # True: a key declared TWICE OR MORE is dropped from the result entirely, + # so a contradiction reads as absent. Where `first_key_wins` picks a winner + # by position, this refuses to pick one at all — for a caller whose field + # is a security-relevant declaration, whichever line a human reads first + # need not be the line a positional rule honors, so the safe reading of two + # conflicting declarations is "nothing was declared" (GPT 5.6, PR #2129). + # Orthogonal to `first_key_wins`: rejection is decided over the raw + # occurrence count, so it holds under either positional rule. + reject_duplicate_keys: bool = False # Resolve a bare block-scalar indicator value (see # BLOCK_SCALAR_INDICATORS) from the blank-or-indented lines that follow # it, via fold_block_scalar. Dialects without this store the indicator @@ -135,6 +153,23 @@ class FrontmatterDialect: resolve_block_scalars=True, ) +# ``task_planner.spec_declares_auto`` — a task spec's declared approval mode. +# Leading whitespace before the opener is tolerated (specs are hand-authored, so +# a stray blank first line must not silently mean "declared nothing"); indented +# keys are prose, so an ``approval:`` nested under another mapping is not a +# top-level declaration; quotes are stripped so ``approval: "auto"`` reads the +# same as bare. Block scalars are deliberately NOT resolved: resolving them would +# let ``approval: |`` fold an indented continuation line into a declaration, and +# for this caller the indicator character is simply not the declared word. +# Duplicate keys are REJECTED rather than resolved by position — see +# `reject_duplicate_keys`. +TASK_SPEC = FrontmatterDialect( + extraction="leading_ws_fence", + indent_policy="reject_indented", + strip_quotes=True, + reject_duplicate_keys=True, +) + def fold_block_scalar(indicator: str, block: list[str]) -> str: """Resolve a YAML block scalar's indented lines into a single value. @@ -266,6 +301,10 @@ def _extract_block( def _parse_block_lines(lines: list[str], dialect: FrontmatterDialect) -> dict[str, str]: """Scan block lines into a field dict under *dialect*'s line rules.""" fields: dict[str, str] = {} + # Keys seen more than once, collected only when the dialect rejects them. + # Recorded during the scan and applied after it, because the second + # occurrence is what makes the FIRST one unusable too. + duplicated: set[str] = set() i = 0 while i < len(lines): line = lines[i] @@ -294,7 +333,13 @@ def _parse_block_lines(lines: list[str], dialect: FrontmatterDialect) -> dict[st value = fold_block_scalar(value, block) elif dialect.strip_quotes: value = value.strip("\"'") + # Checked BEFORE the positional rule below, which would otherwise skip + # the line that proves the key was declared twice. + if dialect.reject_duplicate_keys and key in fields: + duplicated.add(key) if dialect.first_key_wins and key in fields: continue fields[key] = value + for key in duplicated: + del fields[key] return fields diff --git a/src/kiro_crew/task_planner.py b/src/kiro_crew/task_planner.py index cc978412fe5..ade4cdb5805 100644 --- a/src/kiro_crew/task_planner.py +++ b/src/kiro_crew/task_planner.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any from kiro_crew.executors import run_in_embed_pool +from kiro_crew.frontmatter import TASK_SPEC, parse_frontmatter from kiro_crew.hooks import TOOL_DENY from kiro_crew.llm_helpers import _extract_json_of_type from kiro_crew.platform.context import redact_log_via_context @@ -52,6 +53,50 @@ def auto_name(spec_content: str, spec_path: str = "") -> str: return "" +# ── Spec-declared approval mode ── + + +def spec_declares_auto(spec_content: str) -> bool: + """Report whether a spec's frontmatter DECLARES ``approval: auto``. + + A spec may state its intended approval mode in a leading YAML frontmatter + fence, so a trusted plan can carry that intent in version control:: + + --- + approval: auto + --- + # Task: refactor the widget + + This REPORTS a declaration and GRANTS nothing. Unattended execution is + granted only by the launching human's own request body — the dashboard's + auto-approve checkbox, routed through ``_gate_auto_approve`` — and the server + never derives that grant from spec bytes. The declaration is surfaced + pre-launch so the human can act on it; a spec obtained from an untrusted + source therefore cannot disable that human's approval prompts on its own + authority (GPT 5.6 / First-Principles / Design review, PR #2129). + + That separation is also what keeps this function small. While a declaration + was a grant, every reading of it was an authorization decision, so the + scanner had to defend the whole surface a directive could hide in — code + fences, HTML comments, wrapper elements, a bounded top-of-file window. + Reporting intent carries no such burden: the reading is advisory, the region + is delimited, and the grammar is :data:`~kiro_crew.frontmatter.TASK_SPEC`. + + ``True`` only for a literal ``auto`` under a top-level ``approval`` key. + Anything else is ``False``: no frontmatter, an unterminated fence, another + value, an indented occurrence, or TWO conflicting ``approval:`` keys + (rejected, never resolved by position). + + The VALUE is case-insensitive; the KEY is not, so ``Approval: auto`` declares + nothing. That asymmetry is the whole module's behavior — no dialect folds key + case (:func:`~kiro_crew.frontmatter.parse_frontmatter` keeps ``Name`` verbatim + for ``SKILL_LOADER`` too) — and it errs toward per-action prompting, so it is + left as-is rather than special-cased here. + """ + declared = parse_frontmatter(spec_content or "", TASK_SPEC).get("approval", "") + return declared.strip().lower() == "auto" + + # ── Parallel Task Grouping ── diff --git a/temp-screenshots/spec-declared-approval/projects-declared-approval-detail.png b/temp-screenshots/spec-declared-approval/projects-declared-approval-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6222001477b7fe86fc2b0c0ecc89bff0a83696 GIT binary patch literal 9456 zcmbW7Wl&vBlYj{X3GM`U2=49#cbALH#odDjhv328-QC?GxLeSRYjC|w-uK(us{OUK zRa2*Wy5>yv>FJrCIrD@oDM%v03a)W#y?o_ z_$=gX!iL~Q4?7uadg^SLHgRE=TvxP{Tx)wC1@fm(Gx?fX?kD{wTb+l8rsz^g-~#@Z zu-h8aen2`nJ+PpG_r0g{h?&|Fl(K#{t0C6qvF1DEQT_;4|k_Fme zm0YEf^fY{F*wzDDYux5pv8S4sj=+D+b`c`5;fH+|+#EI`P3|Y(_*@9^ZFC!MJ+7uG zTSd-1G(0#wKAu_rqE@OdQz)9h5Zg|~8L>fz+G@vm-WLKWtyuYmaWOas2pN<&g^-bZ z|KtKb^mBeYz_KUNa5xFW$bx+64~;zzA`Z5$&dz*+DUoHRujktQx}6lP;XLSN2x^P0 zzwGmx95M*>{xyiW!88Rt7T!y|Qq$geH49-8rPYj!m&a($i^6C$_g#AG_jy*bu1e~_ z;3$%t8f{^?gzB}WL6Ai;+Q0)E1HniRsBR2B4apTzm@qKWQByO%fBPaS6)Z}`N95MKlQ^E|# z8&yly3(9j;=xl9qi{)Hu-<@dFy$TFc3{WjIHDES%aM@#A5;E` zvm?eEhjbCt>oBKGG1NWmmgN2Xo8t{SAtnQ|xY+ir_S_OdK|wO*cr{NHcAS89$7P$l z`J?z#!qd*qC~jD2NPjglffga@{G+-E26v2((_N|Ia6FMA0mY!gy&G(mIO(=ezQ~{U z7HjJ3(>bi|JhVQl1_kuJ2za@TgIEI{%*}DAsj2;Q0HvpwqC6uTP`46~bk$v_TZdY) z2Ez2>LeQF;OXWw&pCU4Z?on!q!t*hjFP8BL&@)^pq=ootd5>+5Q$wDf8X8Y)cTD8zzw4Glpqg3QqaLqqDCnsIBHzr)va;hG65`^) zp`ns;HfjoPSu=Blgpd6pNbb(AM;rdnpH-XB^C_HRD?R8zgPy zVrnkKI=$SB3k^8hdLNh)r9N^%ZqVe=6MJuME0asYLc_!mvgwyEBqS-T16p@LzkVUM zuCH^YdEKAqYgqr{x=AUCOHC}?c52nn(w~^%iZT=YE$9^7xAmdXKol%V?BldIBDM^J zLWGVn*T1;f)X@>y5J@eY?W>@qw=r#D*M)t3*(WKyv$U|#h+qHpO!zC$7!HH4$MlxS zd$8PMt`;(PU-ktfl$uYWmCxc*aut_kt{Mw?-UNZIt*yo6$s}PVXB}Zs@I>tp7$+w? zcf8`)qLD(q?=@~3X3jy7-*RHkk``E$McfMJE^oIK@b(pWN1)<6q?&sHi2^mGlPfbn zABB*kk+rq8smOYi!}?v=t0XhJ4SHG}F(y*08f;eJYtCb<0tOda6CEYo?Iba3Z7N-> zph`zb%-v%zu$6^~U$bVT2^dXYPBBb3j~5w$e60c$zbHgB2<7d4^gIPOOWVro3JOvz z1M^9WZxmbY#~N1K(i#nXpI*UTD=m(;Q!Ni+mr}EiJ6?BpHyu!4OWM6 zKR-3o0$=#Y$H$x8U<6`BM8vW2skQrI-_=E=nIgGVOkAu5db=)%s~wPR@@)jG>ETwb z$&k(RWg^4W-BEE?Q0(5GNzOtNqfc8?)!kO_SKK39EMP)UXmL>KT#rFX5)?l{6+&ql{ihq%QOOv+htMBFP6L;5`4UyHPJ|CfVYp)A;y8N;JbwrS14Omx zA$?LvY{L-i+#Af9o)iTwAA^E5WQ!slrd~?}NZc`*8Kwx%YQaHDWbGSo7EDalI^h_& zxIUm`Amb7zJv~1YQ!=jdN7RKo4h4iE}`Ha{C1^6^7`@yVa5vdp{C1?piJ}rh!t#2e&Nddy0C++zij0h0^qXO^? zxuMQ|9ZjvIaAQw|O#k}g89Pucb&e~8nlANN6+B#zfP@5oiHKRSI=;mVS4&Gx#3dw* zH<&&afI~vMzDM~8>#47gOxR~!OGZZ4+3C|$rlOTntyU(i9YB*{ZhmkELb_{R#S@8t zxU+1u5?OeI@{>#F{hGg^+n$6?y!qTkGe;_>q-wiEUt+1DL6tsuEM&L^qD#h<5tkV| zdV2EY9x!qi6&2<0?1+f?LE^YUvsI3Gi4vqcHYU?EAqv}Miv)*=jBIIRXmmvta?#*a*J`NV;4Bg$?sq=Y`xv2Kd zHXrxen~^k|%mN)BL*DUZ4n)GmFiZI-rQFM>o?Z|`2<8uGdARg^f2;1kBZhl#HMWLhEfjcjYw`a10G;}ceI4#Yqxq6#e*U&tcKg4=`a@bd;YgMW%#DcDVNsyF`N;=)3}FJ(+aJ$abj z5oTx<#`8MJJ?`}TL|WsXup1EpWk|8NQpae@?(?`S58voV03Hm5O0cqZ%_L(u2$>rQ zC@_n;P2uXrc!Lk{1QqLy(*nji-WD!HsBTk47*V=@LN_v}od!r}g+u9T)7xs~-kccF zV$oI}v8R?zVcIE$bg>|Vo4KVbXI9s0r}WW87|f#Gnrn^D1SMJx3C@BobIUsC)(Y~8ya68sXRDxEQF|o0x zXCfCn8K7Vge}qQ^YnRm%L4KLs3f7p`y;iSFGU}WwR|MhNVnYIwkGH73$3PoPUtK_HeMkfl1k4@(MnI~Ef*Z<0s-^M7<|;M#-CQezU7?3+*k48Ezu4( zBbSmRt=ZCJoU{tvD`FHDi79UaCD{kQWaqsDLdbj2jyE}Ov~L`Ve>M9&4EuARvr_PO6!B4P3I_C7s3 z-AaueG#fRoTcct5pp>N6TQvE-Ypz+b&p+__FxCxiajCtv5s&X+G8NoCvH3$RDuVc1 zMM+6cM~C-7nDUMpi;q1ar^`T&e;@+<9}(jKTw0n(2MHKg`3760N(}_*@IiSX2#AY& zH0?7KiM!OjDLlUi3x;pA>H$AH##`)Q<<^Oz^~`$~V2- z?=rM6y6Sn!$c)E)R0W?7cZoT{5NK{^Z?~nzS@X#$h8GkGZ*Y)3-?Q8vtvu8y_#?jm zVxVm!72Da}6)`w}JG*b*^n~^8x;|fHshv^tn99nzYqDLt8ckE&IvB6+_!tmV6*uF( zmb4C`2TWBl98(UBX^yF`o(j3e=dydSTCT}u#!k0j(R170-X0)xc|y<7Z~z|cnKgUK zh>58=J6|u_rf<)fKh3A^4#_WfgHbk=rOfv(2eSQElvP!se7u%x&_Yp&N#>nRJ>+dC zn$FTiB7_Af(NO`9Y{(Bp2nG*5R=RL$qxh1M5(eLlsI=iY$YII@R9ClCQ!l5!0bx~)I z0?WL-EJwJyH3)e1E{+FQ8ffd3ag}7^k0-xhx%Vl zR#($`ADkFREqc_sB8>&B`j|0x7cJE3drbR@37NM=Q_Zpid@#13fZu7mpKT7Gi0R(= zH@D7>>n+vAjFX%ty7pdyxeWsZLIaJ;6#+0@X&e)Y8T;l!Bb^zPiyKo>*ooHtKakqD z>yiKLE<~tpp5|p;^eVsYm_O#3A-!r^=oDb!h!3m|hz$zkDA6Qt#c6d|jBb;|egf?n zK0iSp?zFUfeP25n810sdFn>%oaPP~E5oCAXUpMii3PkGfopGG`8X-F_qq5<~{@?!s zoSf{AJLCl7O2Mq}TYK@-->>6uuU=bKChVI(#{R6-AIzZFOUAS$B|~dVc(Ld)Uzg9> z5lTmVFgu>dZFdI)G&QYFOzJOl2bGms3JV7%PVBySb_#?&AmK0dy)D=7_LgL1+R9%? zYFJZT0?;}5oNrXfLygf(qxKJz)qeG7$3AAqh0}#AvUBMDxv($&O2XAU?XjcdWQlTn z-Ro*P?JWc68KV5edWQPw47Gq|)*+3{1b$3pcKpM4492;O>zLcCwwEjPPB!V^wy?81 zDZ)N{{qd(RBDo|2V5ja&H6b+M%Yh$=QKJx23p%XQP z{3my#OV2>*Sveo+5~jnFTwWM|!-!YMVT~GVZ0!BPA(i^AEh;|K!zKPqxVY>^z`k+H^AwQ*9o~8kEm;CvV7+$V{LG8)x3-hFZ_cGZ z?|+1XQBH+~Lj&F4;wU3@cADdvFxtjg3wsjsqx16eDs`F|y|y0@viz{+ehR(ROk|wW z_X``O9hC+0$vgdtTg)_(%<^*MA?2c=T4(cJZ$+wLb#%O%RG6HctlY#4HjjxfE*X{_ zscL8$5h8&&ueOESzCs&{kb1z7P#M!(|wfN%Zj0NyghVh8vhO6@- z!w(@lc|D&P2dm)Yo1vJ^UwvKol`bBKxS#s||AQd~l&sQM$ z;Oc5W^~6>mT>95(@s_%4oXfcxr*E0y16@Ny!!`Yi_!Kh@SR(SM#hESL&n-RVVw-5&MBiQ>ls}WD zNRUEdIzfMK7Dh5RH}Bm@&>}RUvkbt(IE_fY$K^@z30SrG$k5EhY^uL39ztJm=jWYO z0{9KMvP(6EtFm3TIBX8{rm6olDrNU+R;BD}-YhQx?b=A@kx{ZxquyS>5%cYkpO1*& z5cN0-AO#yRF}{+@I#I%L{L7E6NHKfxKox`c%mZ_e5#nXnu=_n;!O4kg)JsS+$`Rnbdyl&SsIzgJbSV`S zCYjSdXQ*pv>>pg&2Jqn z&}kY`)6)kbb5R@2G5?(a$c)M98Q!OtaRt4Buv4vmcSY4)lNo|^bmfG*d1ZwGTMBu8 zAwfYu@4n~a?p3Ii9>%^0aYQL?m0yD}V={h6>DI*s3RNr{O))gbg1-K5M#id27A&WQ zTf3Y^s}+XqCtG|Alm6hfKs(kir}LjqI`}*Ge&CZA?kS`&e{kp+66fxCmX~f+dh8Zh z_*OXk4WmOizI1+d|D+yb5FJTtd<(s4w>F(3Z9KJ7k*Y>jw-64cMwP~%-!>TmljPJz zi13tqViv!PXf;T>L~wJvV27OWw+E}miJ*vGI0gU6@2UFRKBW_ZV|;n+J!rKW$&~)E zj|T@tm*DM~vVsJdIZM1s!F9dVKBO^{q~BlM;>B$3B{yh)PQg*5xR+Nb2f?mpcCCLV zKC)o9ri#SIDl^M>Fa2{C)os+TMo|u>@fB4VgIXciPeM{Wa9~~q#l$nXmd*Dd&@Qjr z7{LWsN5^V1bc%0q~yE-;#$~llJws)6x=K)0azONuyWQ zhv~^FUc8Q7?q%xn_ zlRF~q8$&Y7g&@`!Yc|f$^juNVbZ&&;vHnAmN_?1 zn{7*xy04!wZQ8e`d&OQOpV#!vgT_QuYyb zym_{^#5ts}K|pfGT})tCkdM=bmmQwH{pL_e`o8~n%kCOxA%RyJB_$~brOVwIrq_#R z1OLaE{#NM@N(w}XgTT_o7T?>l1w7>Yq7QRJMg0h6=_ni@t=Lz4Ai!4`L;kW)FRjk3 zsosNQSn*VfX{^9MY*T_zP`XxDd>4)<#XXp?6)w!nl+i@$>e@(^O~xRnRiaWwOXBP|LRmGipuaypv>oZQweCM&>i@J3s+npu zQY8BAiPCOgpo84_3zbelbLVWR(o(6YC_;5E9p=s@GQy)kS_M!eQ*dz z`uO4nJu%-%)Fjxm?iXoqShl(@2}*}(TTJd2q; zeyl}Thnl*~CK~xAcDg1V{@aEc6tab)qN3)~A{|AQq~fX?RFbbJl;z<3dw!@s^0npN zEZ3|a7#O-eUZ5tAPg?~x;4}}qxTgGJ&8cfc|5z9b3%r2U+*;?k&dWERAP5c!2-yFX znj9bR`Y_VFXEG{ayeoasb6>E}J^7rX604KNT*V|k{w&Y{jn?E_UmB~dh?*@`2ZnM2 z6612md~B}Z*7i*FOsBRXp(M`z6v|Yz0|C^eVLRw~oDWi%&fiT|E&c*ZaI(ezNn}#O zuyz zBHydNn1Q6s6dkv+q_2eQ!}^WTMgcy&9kt-^uDRj~eisj+N}6rI5My3IHPa@0#3+x$G^L+(zm+q1P$PyY=U>*;y8G9G#b z7nhUPOX|x%cEyIxkd#48x*g1J>srf1T9>FPFq=BBzjtGV+I;(y3xEMzt4xcW?CjIm z(5cgkA7IGx9fhf?9vvoj75TfoP&mMZk>9+0H?2{NTMAsRc0xaa*ry<6NxxK%s!|mf z8_dP1Dl0!;r733VKg~CtC0OuzA37*Gpq_>e?x>gl_IAHrGpI74i<4TcToZn7ls~Xm zx<^jTpphi>BjWRAe>{99?tsOA;dkZ@UYsb>K_pVr_X6E23O$?l54kF>yl>=m8Z1bS zLN4|`W_da+^xEd+kYu}jOw}Da;9~$Ze2k1{#yov{EgBU})yDOCy7O@v#V7#!uUoiJ z8TCsP6=bloIo4R8ue4h){%SRB3evGL+viXoUk`_Z1*f=!A;ab1?^bty8snJbIIVqH z_;b`$7o!?ACgTk2Wxuf^t7nFY?3aViT4_HGMlQOnFqn%)++~0 z!LkKb%JsaUF{Dj*$6)WdvJpSlLF?vZHmq>1#FE1l8-Z+l5s}}Kxb;2gP$r^Iv)joh=jT{T)5|K}iupG6ar8p-h3a+b_t7c19mug(Wwao5-Rdz_$HadboX zBSj7Mp%0EQVg&Tm^>B&Mz7+s@E3o1?RmxaOP(Vj8DBNMPj+LbkZ;ta4P>fE@lvKY8 z27ualw`T522CR?3x>sCU?V3R3mTdlptRLDsQmlaAf#^$dy9UbW&O^CTTXe2x7Y zit;Oq!yfb_S;V8Iy*-=%wmJM6U?lQ35|^Lld8|!y!xDb;e%q8fXC4TI{dEZ`!lBz- zWBX#Iw-(WKjk>sG`}>7~ALmGPcD71GQ~eWu5Dd#Rz*&sT;+ZVrw{>CsTv zi^(}PgtEFiu-?y)%)Td7O&9z#S;>JH5nWAe1mM};}aChRZa>yErvyNHc+-R2NVNnQH zr+ZrVX!!3V2IaBmPX=)e9(N)Xw`M{I&G0h~292CcM%CrXa%)~)wNo!|Z|AJopY)>P zZ?1w-D`&KCn&S>py}F?I7>Zj9vwxaw-r<4OJi~}%`i-y>a20%hAk{BfNGa6LlLYU` zr0NG$R?7BIH393|s9*qDgzi5-SdbPM-mL`D2XtfGoC19iLJ>42!eTtHo2 z-#|}YoqZMbxB$!@S^XyUZQ);E`iiGnQ>QcH6=W+`YXwM$4&EyNL z(oYj@q$TG}%eqqGnc7=29~+CxO)lF|j-+lG7||glhL@MMFv_d@XB1v3D&Bs$+p^63 z&EKgp!9~{#Mh!Ub?AmPCtf+PGF1|50l$)!laNLIeiL8>O!1;?JQxYqXuH`&Q)3tnz z6nn0sjI>$esnI1Xb0Zi5>~g~wYv|6tdE?>LCGHNJyExB&SIoRBX4VAr3ZriB%;b~b zp%)~&jU{02{%2;7JTzX)d{&xgadGjO+KYiaKfY_u^L$;eJ8bX_R7{@vV7IY>qI%o@ zwVq%-o89%P-~$v)%9{jFYr5fa_j0v7NvtV=gr%KY^r#Y$)&oxeX&nBHe1yl$JN6gI zD2SNZJh`eBvBwxd{3)xUSGi$yGfj9;sFT^gqU@V&D8i@phsEG8xzUP4gZV0tdd)ne zVbz#6aXv6;XUC?5(+C)JX^2o?WR>GW+tvQtP^1l3^qX();YX2G@@7Hpf-fe#Wol!s z>&V`)+l<3W*5x`fO$>S!?~FoQc#3d%_&Y-4LLQyml|QT1l_l*Cv3j7jf=@*@lIVo5 zu=FiCu)kLM3ocXm|3U!J3`h6@bw5acQ6zUzX{33+c1y1x9-k_-!7JgEpv}$vck_3U z;-aT-=`Ty+RE#QBI>->18G>GbWKO%JU$(Mt$DSwy`sJQDb{=e>oj$O!&@R26qml|} zDjfeyVpx*p{|7lBEZkA^-vhLaYawVDUzELb9DbPe^5tum3VYbSy-xe>(!hzmtik>X z9a@a$a>90rcOK#w6)XII7!a}q98>uFmX+YvwcvO=Mc>`pjc>ALg_ir-qUnv^{I898 zNa+(Z7LD#Nfwub);_q0+l?U=~qAoJWz|@^1qRI-O)}okguj^l6Ce}{=`)&^Efzdm7 zz}R6(T~*C69OnXsdAv^rbj|N*QE@8Naw+O~#1ob%^3MRh>$PzI@5*%HZl!g4Z8OV-8J|fR|11MO+UN83ow5dCMXDP%7o9{IUdmO8VG4|1+gko H!+`$+``K=5 literal 0 HcmV?d00001 diff --git a/temp-screenshots/spec-declared-approval/projects-declared-approval.png b/temp-screenshots/spec-declared-approval/projects-declared-approval.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa3dbc75f7cb3f15769c2feef9d7acf5f8e5c2f GIT binary patch literal 67305 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 a4m2-OUrA=17J None: + call = await self._start_auto_approve( + tmp_path, {"spec": "__inline__:" + self.DECLARING, "source": "dashboard"} + ) + assert call.kwargs["auto_approve"] is False + + @pytest.mark.asyncio + async def test_inline_declaring_spec_with_the_flag_false_gets_nothing( + self, tmp_path: Path + ) -> None: + # An explicit `false` is the STRICT default the dashboard now sends: the + # checkbox is unchecked even when the spec declares auto. + call = await self._start_auto_approve( + tmp_path, + { + "spec": "__inline__:" + self.DECLARING, + "source": "dashboard", + "auto_approve": False, + }, + ) + assert call.kwargs["auto_approve"] is False + + @pytest.mark.asyncio + async def test_the_human_flag_is_what_grants(self, tmp_path: Path) -> None: + # Control: the same declaring spec DOES run unattended once the human + # asks for it, so the tests above are measuring the OR's absence and not + # a gate that simply refuses everything. + call = await self._start_auto_approve( + tmp_path, + { + "spec": "__inline__:" + self.DECLARING, + "source": "dashboard", + "auto_approve": True, + }, + ) + assert call.kwargs["auto_approve"] is True + + @pytest.mark.asyncio + async def test_a_file_backed_declaring_spec_executes_from_its_own_path( + self, tmp_path: Path + ) -> None: + # No content-derived grant means no content-derived TOCTOU to close, so + # the approval snapshot is gone: the run executes from the caller's own + # file and the work dir gains no `snap-/` directory to leak. + spec = tmp_path / "declaring.md" + spec.write_text(self.DECLARING, encoding="utf-8") + work = tmp_path / "work" + work.mkdir() + call = await self._start_auto_approve( + str(work), {"spec": str(spec), "source": "dashboard", "auto_approve": True} + ) + assert call.kwargs["auto_approve"] is True + assert call.args[0] == str(spec.resolve()) + assert list(work.iterdir()) == [] + + async def _execute_auto_approve(self, body: dict, spec_content: str): + runner = MagicMock() + runner.execute_plan = AsyncMock(return_value=None) + runner._runs = { + "t1": Project( + spec_path="s.md", spec_content=spec_content, status="planned", task_id="t1" + ) + } + app = web.Application() + app["state"] = SimpleNamespace(task_runner=runner) + req = make_mocked_request( + "POST", "/api/taskrunner/t1/execute", app=app, match_info={"task_id": "t1"}, + headers={"Content-Length": "32"}, + ) + req["app"] = "" + req.json = AsyncMock(return_value=body) + await api_taskrunner_execute_plan(req) + return runner.execute_plan.call_args.kwargs["auto_approve"] + + @pytest.mark.asyncio + async def test_execute_declaring_spec_without_the_flag_gets_nothing(self) -> None: + assert await self._execute_auto_approve({}, self.DECLARING) is False + + @pytest.mark.asyncio + async def test_execute_declaring_spec_with_the_flag_false_gets_nothing(self) -> None: + assert await self._execute_auto_approve({"auto_approve": False}, self.DECLARING) is False + + @pytest.mark.asyncio + async def test_execute_honors_the_human_flag_on_the_same_spec(self) -> None: + assert await self._execute_auto_approve({"auto_approve": True}, self.DECLARING) is True + + +class TestPlanReportsTheDeclarationWithoutActingOnIt: + """`/plan` surfaces `declared_approval` so the human can decide. + + This is the whole replacement for the deleted server-side OR: the + declaration reaches the person who owns the checkbox, and reaches nothing + else. + """ + + async def _plan(self, spec_content: str): + runner = MagicMock() + run = Project( + spec_path="s.md", spec_content=spec_content, status="planned", task_id="t1" + ) + runner.plan = AsyncMock(return_value=run) + runner._group_parallel_tasks = MagicMock(return_value=[]) + app = web.Application() + app["state"] = SimpleNamespace(task_runner=runner) + req = make_mocked_request("POST", "/api/taskrunner/plan", app=app) + req.json = AsyncMock(return_value={"input": "do the thing", "source": "text"}) + resp = await api_taskrunner_plan(req) + assert resp.status == 200, resp.text + return json.loads(resp.text) + + @pytest.mark.asyncio + async def test_a_declaring_spec_is_reported(self) -> None: + payload = await self._plan("---\napproval: auto\n---\n# Task: t\n") + assert payload["declared_approval"] is True + + @pytest.mark.asyncio + async def test_a_silent_spec_is_reported_as_false(self) -> None: + payload = await self._plan("# Task: t\n") + assert payload["declared_approval"] is False + + @pytest.mark.asyncio + async def test_an_empty_spec_content_is_reported_as_false(self) -> None: + # `plan(source="yaml")` leaves spec_content empty; the field must still + # serialize rather than raising on None/"". + payload = await self._plan("") + assert payload["declared_approval"] is False diff --git a/test/test_frontmatter.py b/test/test_frontmatter.py index 01694e52920..e66b64676be 100644 --- a/test/test_frontmatter.py +++ b/test/test_frontmatter.py @@ -23,6 +23,7 @@ ONBOARDING_IMPORT, SKILL_LOADER, SKILL_UPDATE, + TASK_SPEC, FrontmatterDialect, frontmatter_value, parse_frontmatter, @@ -295,14 +296,14 @@ def test_none_and_empty(self) -> None: class TestDialectContracts: - """The three dialects stay distinct — collapsing any two axes silently + """The four dialects stay distinct — collapsing any two axes silently changes some caller's accepted-input surface.""" def test_presets_are_distinct(self) -> None: - presets = [SKILL_LOADER, ONBOARDING_IMPORT, SKILL_UPDATE] + presets = [SKILL_LOADER, ONBOARDING_IMPORT, SKILL_UPDATE, TASK_SPEC] keys = { (p.extraction, p.indent_policy, p.strip_quotes, p.first_key_wins, - p.resolve_block_scalars) + p.resolve_block_scalars, p.reject_duplicate_keys) for p in presets } assert len(keys) == len(presets) @@ -316,6 +317,33 @@ def test_first_key_wins_vs_last(self) -> None: assert frontmatter_value(text, "k", SKILL_UPDATE) == "first" assert parse_frontmatter(text, SKILL_LOADER)["k"] == "second" + def test_duplicate_rejection_is_per_dialect(self) -> None: + # The third position on the duplicate axis: neither occurrence wins, so + # the key is ABSENT. A caller whose field is a security-relevant + # declaration reads absent as "not declared" and denies. + text = "---\nk: first\nk: second\n---\n" + assert "k" not in parse_frontmatter(text, TASK_SPEC) + assert frontmatter_value(text, "k", TASK_SPEC) == "" + # Other keys in the same block are untouched — rejection is per key. + mixed = "---\nk: first\nother: kept\nk: second\n---\n" + assert parse_frontmatter(mixed, TASK_SPEC) == {"other": "kept"} + + def test_duplicate_rejection_is_decided_before_the_positional_rule(self) -> None: + # `first_key_wins` skips the second occurrence, which is the line that + # PROVES the key was declared twice. Counting first is what makes the two + # axes independent rather than order-dependent. + both = FrontmatterDialect( + extraction="column0_fence", + indent_policy="reject_indented", + strip_quotes=True, + first_key_wins=True, + reject_duplicate_keys=True, + ) + assert parse_frontmatter("---\nk: first\nk: second\n---\n", both) == {} + + def test_a_single_key_survives_a_rejecting_dialect(self) -> None: + assert parse_frontmatter("---\nk: only\n---\n", TASK_SPEC) == {"k": "only"} + def test_quote_stripping_is_per_dialect(self) -> None: text = '---\nk: "v"\n---\n' assert parse_frontmatter(text, SKILL_LOADER)["k"] == "v" @@ -334,7 +362,7 @@ def test_quote_strip_never_applies_to_a_resolved_scalar(self) -> None: assert parse_frontmatter(text, SKILL_LOADER)["k"] == '"quoted"' def test_split_returns_text_unchanged_without_block(self) -> None: - for dialect in (SKILL_LOADER, ONBOARDING_IMPORT, SKILL_UPDATE): + for dialect in (SKILL_LOADER, ONBOARDING_IMPORT, SKILL_UPDATE, TASK_SPEC): assert split_frontmatter("plain prose", dialect) == ({}, "plain prose") def test_custom_dialect_axes_compose(self) -> None: diff --git a/test/test_spec_approval_mode.py b/test/test_spec_approval_mode.py new file mode 100644 index 00000000000..cab98c46360 --- /dev/null +++ b/test/test_spec_approval_mode.py @@ -0,0 +1,154 @@ +"""Unit tests for the spec-declared approval mode. + +`task_planner.spec_declares_auto()` reports whether a spec's leading YAML +frontmatter DECLARES `approval: auto`. It grants nothing: the grant comes from +the launching human's request body (`auto_approve`), so these tests are about +what is REPORTED, never about what is honored. The gating tests live in +`test_auto_approve.py`. + +Deny-by-default throughout: anything other than an unambiguous top-level +`approval: auto` reports False. +""" + +from __future__ import annotations + +from kiro_crew.task_planner import spec_declares_auto + + +class TestDeclared: + def test_frontmatter_auto(self): + assert spec_declares_auto("---\napproval: auto\n---\n# Task: do the thing\n") is True + + def test_auto_alongside_other_keys(self): + spec = "---\nname: Widget refactor\napproval: auto\nowner: me\n---\n# Task: x\n" + assert spec_declares_auto(spec) is True + + def test_quoted_value(self): + assert spec_declares_auto('---\napproval: "auto"\n---\n') is True + assert spec_declares_auto("---\napproval: 'auto'\n---\n") is True + + def test_value_is_case_insensitive_but_the_key_is_not(self): + assert spec_declares_auto("---\napproval: AUTO\n---\n") is True + assert spec_declares_auto("---\nApproval: auto\n---\n") is False + + def test_leading_blank_lines_tolerated(self): + # A spec is hand-authored; a stray blank first line must not silently + # read as "declared nothing" (frontmatter.TASK_SPEC: leading_ws_fence). + assert spec_declares_auto("\n\n---\napproval: auto\n---\n") is True + + +class TestDenyByDefault: + def test_empty_and_none_safe(self): + assert spec_declares_auto("") is False + assert spec_declares_auto(None) is False # type: ignore[arg-type] + + def test_no_frontmatter(self): + assert spec_declares_auto("# Task: do the thing\n1. step\n") is False + + def test_other_modes_are_not_auto(self): + # `per-task`/`per-action` are simply not `auto`. Nothing enumerates them + # any more: only the literal `auto` is meaningful, so there is no mode + # table left for a typo to land next to. + assert spec_declares_auto("---\napproval: per-task\n---\n") is False + assert spec_declares_auto("---\napproval: per-action\n---\n") is False + + def test_unknown_value(self): + assert spec_declares_auto("---\napproval: yolo\n---\n") is False + + def test_prose_value_never_matches(self): + assert spec_declares_auto("---\napproval: pending review of the design\n---\n") is False + + def test_substring_is_not_the_value(self): + assert spec_declares_auto("---\napproval: automatic\n---\n") is False + assert spec_declares_auto("---\napproval_mode: auto\n---\n") is False + + def test_indented_key_is_not_top_level(self): + # An indented occurrence belongs to an enclosing mapping or block + # scalar, not to the document's top level. + assert spec_declares_auto("---\n approval: auto\n---\n") is False + assert spec_declares_auto("---\nsteps:\n approval: auto\n---\n") is False + assert spec_declares_auto("---\napproval:\n auto\n---\n") is False + + def test_block_scalar_cannot_fold_into_a_declaration(self): + # TASK_SPEC does not resolve block scalars, so the indicator character + # is the value and the indented continuation stays prose. + assert spec_declares_auto("---\napproval: |\n auto\n---\n") is False + assert spec_declares_auto("---\napproval: >\n auto\n---\n") is False + + def test_unterminated_frontmatter_is_not_frontmatter(self): + assert spec_declares_auto("---\napproval: auto\n# Task: x\n") is False + + def test_body_directive_is_outside_the_region(self): + # Everything after the closing fence is body text, at any depth or + # distance — including a fenced example. + assert spec_declares_auto("---\nname: x\n---\napproval: auto\n") is False + assert spec_declares_auto("---\nname: x\n---\n```\napproval: auto\n```\n") is False + + def test_bare_top_of_file_directive_is_no_longer_honored(self): + # Deliberate narrowing: a declaration must live in frontmatter. The bare + # form required scanning arbitrary document text, which is what forced + # the code-fence and HTML-markup defenses this refactor deleted. + assert spec_declares_auto("approval: auto\n# Task: x\n") is False + assert spec_declares_auto("# Task: x\napproval: auto\n") is False + + def test_bom_prefixed_spec_fails_closed(self): + # A BOM is not whitespace, so this is not frontmatter and the + # declaration is not reported. Failing closed costs a UI hint, never a + # grant — the human's checkbox is unaffected either way. + assert spec_declares_auto("---\napproval: auto\n---\n") is False + + +class TestMarkupCannotSynthesizeADeclaration: + """The whole value must BE the word, so no deletion of characters can + manufacture one out of two harmless fragments.""" + + def test_element_span_between_key_and_value(self): + # GPT 5.6's original finding against a splicing parser: removing the + # element span from this line leaves `approval: auto`, which a splicing + # reader honors even though the author wrote `per-task`. Nothing is + # removed here, so the value is the whole markup-bearing string. + assert spec_declares_auto("---\napproval:per-task auto\n---\n") is False + + def test_html_comment_on_the_line(self): + assert spec_declares_auto("---\napproval: \n---\n") is False + assert spec_declares_auto("---\napproval: auto \n---\n") is False + + def test_trailing_yaml_comment_is_not_a_declaration_of_auto(self): + # Not a YAML parser: a trailing `#` comment is part of the value, so the + # line does not read as a bare `auto`. Fails closed, not open. + assert spec_declares_auto("---\napproval: auto # trusted plan\n---\n") is False + + +class TestConflictingDeclarationsFailClosed: + """Two `approval:` keys report NOT DECLARED. + + `parse_frontmatter` returns a dict, so without this a duplicate would be + resolved silently by position — and whichever declaration a human reads + first need not be the one the parser honored (GPT 5.6, PR #2129). The guard + is the `reject_duplicate_keys` axis on `frontmatter.TASK_SPEC`. + """ + + def test_auto_then_per_action(self): + assert spec_declares_auto("---\napproval: auto\napproval: per-action\n---\n") is False + + def test_per_action_then_auto(self): + # Both orders, because a first-wins rule and a last-wins rule disagree + # about exactly this input and neither is safe. + assert spec_declares_auto("---\napproval: per-action\napproval: auto\n---\n") is False + + def test_auto_twice(self): + # Even a non-contradictory repeat is refused: the rule is over the + # occurrence count, not over whether the values happen to agree. + assert spec_declares_auto("---\napproval: auto\napproval: auto\n---\n") is False + + def test_second_declaration_separated_by_other_keys(self): + spec = "---\napproval: auto\nname: x\nowner: me\napproval: yolo\n---\n" + assert spec_declares_auto(spec) is False + + def test_an_indented_second_occurrence_is_not_a_duplicate(self): + # Indented lines are prose under this dialect, so they never reached the + # field dict and cannot suppress a real declaration. + assert spec_declares_auto("---\napproval: auto\n approval: per-action\n---\n") is True + + def test_a_body_occurrence_is_not_a_duplicate(self): + assert spec_declares_auto("---\napproval: auto\n---\napproval: per-action\n") is True diff --git a/website/src/i18n/locales/bn.json b/website/src/i18n/locales/bn.json index 3eef5d6a9aa..588bb73ad2a 100644 --- a/website/src/i18n/locales/bn.json +++ b/website/src/i18n/locales/bn.json @@ -11104,6 +11104,8 @@ "running": "চলছে", "schedule": "সময়সূচি", "scheduled_as_daily_cron_job": "দৈনিক cron জব হিসেবে সময়সূচি করা হয়েছে", + "spec_declares_auto_approval": "স্পেক approval: auto ঘোষণা করে", + "spec_declares_auto_approval_hint": "এই স্পেক `approval: auto` ঘোষণা করে। এটি কেবল একটি ঘোষণা — আপনি নিজে বক্সে টিক না দিলে টুল কল একটি একটি করেই অনুমোদিত হয়।", "task_runner": "Task Runner", "upload_a_file": "একটি ফাইল আপলোড করুন", "workspace": "ওয়ার্কস্পেস:", diff --git a/website/src/i18n/locales/de.json b/website/src/i18n/locales/de.json index 28564d6e1fe..17736385953 100644 --- a/website/src/i18n/locales/de.json +++ b/website/src/i18n/locales/de.json @@ -11104,6 +11104,8 @@ "running": "Läuft", "schedule": "Einplanen", "scheduled_as_daily_cron_job": "Als täglicher Cron-Job geplant", + "spec_declares_auto_approval": "Spezifikation deklariert approval: auto", + "spec_declares_auto_approval_hint": "Diese Spezifikation deklariert `approval: auto`. Das ist nur eine Deklaration — Tool-Aufrufe werden weiterhin einzeln genehmigt, sofern Sie das Kästchen nicht selbst aktivieren.", "task_runner": "Task Runner", "upload_a_file": "Eine Datei hochladen", "workspace": "Workspace:", diff --git a/website/src/i18n/locales/en-XA.json b/website/src/i18n/locales/en-XA.json index 7ea6d98e5e7..4afe5eec88f 100644 --- a/website/src/i18n/locales/en-XA.json +++ b/website/src/i18n/locales/en-XA.json @@ -10825,6 +10825,8 @@ "running": "[Ŕùññìñğ ···········]", "schedule": "[Şçĥèðùĺè ············]", "scheduled_as_daily_cron_job": "[Şçĥèðùĺèð àş ðàìĺý çŕøñ ĵøƀ ···················]", + "spec_declares_auto_approval": "[Şþèç ðèçĺàŕèş àþþŕøṽàĺ: àùţø ····················]", + "spec_declares_auto_approval_hint": "[Ţĥìş şþèç ðèçĺàŕèş `àþþŕøṽàĺ: àùţø`. Ìţ ìş øñĺý à ðèçĺàŕàţìøñ — ţøøĺ çàĺĺş àŕè şţìĺĺ àþþŕøṽèð øñè àţ à ţìɱè ùñĺèşş ýøù ţìçķ ţĥè ƀøẋ ýøùŕşèĺƒ. ··········································]", "task_runner": "[Ţàşķ Ŕùññèŕ ··········]", "upload_a_file": "[Ùþĺøàð à ƒìĺè ············]", "workspace": "[Ẁøŕķşþàçè: ···············]", diff --git a/website/src/i18n/locales/en.json b/website/src/i18n/locales/en.json index 6c28d6833be..db676e78ca5 100644 --- a/website/src/i18n/locales/en.json +++ b/website/src/i18n/locales/en.json @@ -7238,6 +7238,8 @@ "running": "Running", "schedule": "Schedule", "scheduled_as_daily_cron_job": "Scheduled as daily cron job", + "spec_declares_auto_approval": "Spec declares approval: auto", + "spec_declares_auto_approval_hint": "This spec declares `approval: auto`. It is only a declaration \u2014 tool calls are still approved one at a time unless you tick the box yourself.", "task_runner": "Task Runner", "upload_a_file": "Upload a file", "workspace": "Workspace:", diff --git a/website/src/i18n/locales/es.json b/website/src/i18n/locales/es.json index ce83016db36..bf5035eb013 100644 --- a/website/src/i18n/locales/es.json +++ b/website/src/i18n/locales/es.json @@ -11263,6 +11263,8 @@ "running": "En ejecución", "schedule": "Programar", "scheduled_as_daily_cron_job": "Programado como trabajo cron diario", + "spec_declares_auto_approval": "La especificación declara approval: auto", + "spec_declares_auto_approval_hint": "Esta especificación declara `approval: auto`. Es solo una declaración: las llamadas a herramientas se siguen aprobando una por una a menos que marques la casilla tú mismo.", "task_runner": "Task Runner", "upload_a_file": "Subir un archivo", "workspace": "Espacio de trabajo:", diff --git a/website/src/i18n/locales/fr.json b/website/src/i18n/locales/fr.json index 8cf9438edc8..d524bbd7e6f 100644 --- a/website/src/i18n/locales/fr.json +++ b/website/src/i18n/locales/fr.json @@ -11263,6 +11263,8 @@ "running": "En cours", "schedule": "Planifier", "scheduled_as_daily_cron_job": "Planifié comme tâche cron quotidienne", + "spec_declares_auto_approval": "La spécification déclare approval: auto", + "spec_declares_auto_approval_hint": "Cette spécification déclare `approval: auto`. Ce n'est qu'une déclaration — les appels d'outils sont toujours approuvés un par un, sauf si vous cochez la case vous-même.", "task_runner": "Task Runner", "upload_a_file": "Importer un fichier", "workspace": "Espace de travail :", diff --git a/website/src/i18n/locales/hi.json b/website/src/i18n/locales/hi.json index 0c876087ee8..a31d3ddbced 100644 --- a/website/src/i18n/locales/hi.json +++ b/website/src/i18n/locales/hi.json @@ -11104,6 +11104,8 @@ "running": "चल रहा है", "schedule": "शेड्यूल", "scheduled_as_daily_cron_job": "दैनिक cron जॉब के रूप में शेड्यूल किया गया", + "spec_declares_auto_approval": "स्पेक approval: auto घोषित करता है", + "spec_declares_auto_approval_hint": "यह स्पेक `approval: auto` घोषित करता है। यह केवल एक घोषणा है — जब तक तुम खुद बॉक्स पर टिक नहीं करते, टूल कॉल एक-एक करके ही स्वीकृत होते हैं।", "task_runner": "Task Runner", "upload_a_file": "फ़ाइल अपलोड करें", "workspace": "वर्कस्पेस:", diff --git a/website/src/i18n/locales/it.json b/website/src/i18n/locales/it.json index b21c867279a..4d9ddac691c 100644 --- a/website/src/i18n/locales/it.json +++ b/website/src/i18n/locales/it.json @@ -11263,6 +11263,8 @@ "running": "In esecuzione", "schedule": "Programma", "scheduled_as_daily_cron_job": "Pianificato come job cron giornaliero", + "spec_declares_auto_approval": "La specifica dichiara approval: auto", + "spec_declares_auto_approval_hint": "Questa specifica dichiara `approval: auto`. È solo una dichiarazione: le chiamate agli strumenti vengono ancora approvate una alla volta, a meno che non selezioni tu stesso la casella.", "task_runner": "Task Runner", "upload_a_file": "Carica un file", "workspace": "Workspace:", diff --git a/website/src/i18n/locales/ja.json b/website/src/i18n/locales/ja.json index 8f8a13faa98..56343324eac 100644 --- a/website/src/i18n/locales/ja.json +++ b/website/src/i18n/locales/ja.json @@ -10945,6 +10945,8 @@ "running": "実行中", "schedule": "スケジュール", "scheduled_as_daily_cron_job": "日次cronジョブとしてスケジュール済み", + "spec_declares_auto_approval": "仕様が approval: auto を宣言", + "spec_declares_auto_approval_hint": "この仕様は `approval: auto` を宣言しています。これは宣言にすぎません。自分でチェックボックスをオンにしない限り、ツール呼び出しは 1 件ずつ承認されます。", "task_runner": "Task Runner", "upload_a_file": "ファイルをアップロード", "workspace": "ワークスペース:", diff --git a/website/src/i18n/locales/ko.json b/website/src/i18n/locales/ko.json index e8ba86e6dad..58025fdac13 100644 --- a/website/src/i18n/locales/ko.json +++ b/website/src/i18n/locales/ko.json @@ -10945,6 +10945,8 @@ "running": "실행 중", "schedule": "정기 실행", "scheduled_as_daily_cron_job": "매일 실행되는 cron 작업으로 등록했습니다", + "spec_declares_auto_approval": "명세가 approval: auto를 선언함", + "spec_declares_auto_approval_hint": "이 명세는 `approval: auto`를 선언합니다. 선언일 뿐이며, 직접 확인란을 선택하지 않으면 도구 호출은 계속 하나씩 승인됩니다.", "task_runner": "Task Runner", "upload_a_file": "파일 업로드", "workspace": "워크스페이스:", diff --git a/website/src/i18n/locales/pt.json b/website/src/i18n/locales/pt.json index a92b1797eb9..39ffd7a54f4 100644 --- a/website/src/i18n/locales/pt.json +++ b/website/src/i18n/locales/pt.json @@ -11263,6 +11263,8 @@ "running": "Em execução", "schedule": "Agendar", "scheduled_as_daily_cron_job": "Agendado como job de cron diário", + "spec_declares_auto_approval": "A especificação declara approval: auto", + "spec_declares_auto_approval_hint": "Esta especificação declara `approval: auto`. É apenas uma declaração — as chamadas de ferramentas continuam a ser aprovadas uma a uma, a menos que você mesmo marque a caixa.", "task_runner": "Task Runner", "upload_a_file": "Enviar um arquivo", "workspace": "Workspace:", diff --git a/website/src/i18n/locales/ru.json b/website/src/i18n/locales/ru.json index 7ba11c34b73..bd15f48a3e6 100644 --- a/website/src/i18n/locales/ru.json +++ b/website/src/i18n/locales/ru.json @@ -11422,6 +11422,8 @@ "running": "Выполняется", "schedule": "Запланировать", "scheduled_as_daily_cron_job": "Запланировано как ежедневное cron-задание", + "spec_declares_auto_approval": "Спецификация объявляет approval: auto", + "spec_declares_auto_approval_hint": "Эта спецификация объявляет `approval: auto`. Это лишь объявление — вызовы инструментов по-прежнему утверждаются по одному, если вы сами не установите флажок.", "task_runner": "Task Runner", "upload_a_file": "Загрузить файл", "workspace": "Рабочая область:", diff --git a/website/src/i18n/locales/zh-CN.json b/website/src/i18n/locales/zh-CN.json index 9bcf4d1b3f2..739bd32dccc 100644 --- a/website/src/i18n/locales/zh-CN.json +++ b/website/src/i18n/locales/zh-CN.json @@ -10945,6 +10945,8 @@ "running": "运行中", "schedule": "计划", "scheduled_as_daily_cron_job": "已排期为每日定时任务", + "spec_declares_auto_approval": "规范声明 approval: auto", + "spec_declares_auto_approval_hint": "此规范声明了 `approval: auto`。这只是一个声明——除非你自己勾选该复选框,工具调用仍会逐个请求批准。", "task_runner": "Task Runner", "upload_a_file": "上传文件", "workspace": "工作区:", diff --git a/website/src/pages/ProjectsPage.tsx b/website/src/pages/ProjectsPage.tsx index cd3b8861ede..14de0ba6f7a 100644 --- a/website/src/pages/ProjectsPage.tsx +++ b/website/src/pages/ProjectsPage.tsx @@ -87,6 +87,16 @@ export default function ProjectsPage() { // ref captured at click time (see `pendingAutoApproveRef`) so the sync effect // cannot clobber it, and it never leaks across runs. const [composeAutoApprove, setComposeAutoApprove] = useState(false) + // What the planned run's spec DECLARED (`approval: auto` in its frontmatter), + // as reported by /plan. Read-only: it labels the checkbox below and never sets + // it — the server derives the grant from `auto_approve` alone, so pre-checking + // here would only re-open the content-derived grant in the browser. + // + // Keyed by task_id so a stale declaration cannot describe a different run. + // Scope is this page's session: /plan is the only carrier of the field, so + // selecting an already-planned run after a reload shows no label — a missing + // hint, never a wrong one. + const [declaredApproval, setDeclaredApproval] = useState<{ taskId: string; declared: boolean } | null>(null) const [refineStatus, setRefineStatus] = useState('idle') const [refineError, setRefineError] = useState('') const mountedRef = useRef(true) @@ -250,6 +260,9 @@ export default function ProjectsPage() { try { const r = await api.planTask(input, source, spec, agent, workspaceDir) if (r.ok) { + // Record the spec's declaration for the label beside the Execute + // checkbox. `=== true` because the field is absent on an older gateway. + setDeclaredApproval(r.task_id ? { taskId: r.task_id, declared: r.declared_approval === true } : null) if (autoRun && r.task_id) { autoRunRef.current = r.task_id // Guard the ref-write with the SAME planTask-success branch as @@ -588,6 +601,11 @@ export default function ProjectsPage() { {selectedRun.status === 'planning' ? <> {i18nT('pages.projectsPage.planning')} : selectedRun.running ? <> {i18nT('pages.projectsPage.running')} : selectedRun.status}
{selectedRun.status === 'planned' && <> + {declaredApproval?.taskId === selectedRun.task_id && declaredApproval.declared && ( + + {i18nT('pages.projectsPage.spec_declares_auto_approval')} + + )}