feat(skills): auto-apply prose-only skill updates when approval off - #2713
feat(skills): auto-apply prose-only skill updates when approval off#2713rubencu wants to merge 1 commit into
Conversation
13f60bc to
e9df788
Compare
e9df788 to
62d92e7
Compare
62d92e7 to
ac1fd40
Compare
Design Review (Fable 5, fork) — 🟡 CONCERNSAdvisory design-level review of Design-Verdict: CONCERNS Sound shape — promotes through the existing approval chokepoint — but two real behaviors shipped undocumented: the refine veto and a new lock on the human path. Watch
Suggestions
[DESIGN-REVIEWED] 56f44ab |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsI've verified the key facts. The candidate concerns the notification The candidate's claimed outcome (c) — markdown/HTML injection — does not hold:
There is no observable wrong outcome; the item is benign-by-design and its own confidence line rates it low. It fails criterion (c) and is dropped. I traced the rest of the diff (stage-token ownership check, the deferred-re-stage / notify-suppression / re-fire logic, the per-target promotion lock, the No findings. [OPUS-REVIEWED] 56f44ab |
GPT 5.6 Review (fork) — 🔴 changes requested (blocking)Reviewed BLOCKING -- src/kiro_crew/skills.py:3427 -- Late script injection deletes an unreviewed candidate |
ac1fd40 to
ff63edb
Compare
bda5b3e to
1557935
Compare
1557935 to
46f1c66
Compare
Disposition: GPT review of 1557935 — all three findings fixed in 46f1c66Same-target promotion is now serialized with a per-target cross-process file lock: 1. skills.py — late 2. history.py — concurrent auto-applies last-write-win (BLOCKING) 3. history.py — suppressed staging notification on Verification on 46f1c66: |
46f1c66 to
79d83bc
Compare
|
Disposition for GPT round on Finding accepted: |
|
Disposition for GPT round on Finding accepted: the staging flow verified the candidate's Fix (as suggested): New regression test pins both directions: a swapped candidate with a stale expected token is refused (live version untouched, impostor stays pending), and a matching token still promotes. 19 targeted tests pass; isort/flake8 clean. |
|
Disposition for GPT round on Finding accepted (span 78ebcb5cb0e3, history.py auto-apply): when one consolidation result carried both a Fix (as suggested — leave the update staged): Two new regression tests: Verification on |
First Principles Review (Fable 5, fork) — 🟡 CONCERNSAdvisory premise-level review of All evidence gathered. I verified the base tree: the update-staging branch ( First-Principles-Verdict: CONCERNS The cross-process promotion lock and the refine-mode veto ship undeclared — and the veto contradicts the unconditional auto-apply semantics this PR writes into the docs. What this change shipsIntent: let prose-only skill updates go live without review on instances that disabled the approval queue — a FIX for an asymmetry in an existing flag's semantics.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 56f44ab |
|
Round 9 BLOCKING (history.py — concurrent refinement can overwrite an auto-applied update): fixed in 56f44ab. Applied the reviewer's literal fix: auto-apply is now disabled whenever auto-refine is enabled ( |
|
Closing per the escalation rule after GPT round 10 on the promotion subsystem (rounds 6-10 all found real check-to-use interleavings in non-atomic candidate consumption; the latest: scripts/ injected between the refuse_scripts check and rmtree deletes an unreviewed candidate while its prose goes live). The convergent fix is structural — claim the candidate atomically via rename before inspecting it — and is written up with the full design history in #3795. Branch preserved at 56f44ab with 262 targeted tests green; the per-target lock and stage-token work remain valid on top of the atomic claim. |
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
Problem
skills.approval_required=falseis documented as letting prose-only auto-skill candidates go live without review — but that only ever applied to new candidates. UPDATE proposals against already-live auto-skills were staged unconditionally: the update branch inHistoryConsolidatorcalled_stage_skill_updatewithout ever consulting the flag.The result is an incoherent asymmetry:
approve_pending_updatesnapshots the current liveSKILL.mdtoauto/<target>/.versions/v<N>-SKILL.mdbefore overwriting, so every applied update is restorable — new candidates have no such rollback.auto/.pending/for days: the user disabled the queue, so nobody is reading it.Change
When
approval_requiredis false and an update candidate is prose-only, the staged candidate is now immediately promoted through the existingapprove_pending_updatepath — no parallel write path. All of its guards apply unchanged: base-version staleness refusal, symlink/layout guards, redaction, live version snapshot,MAX_SKILL_VERSIONSpruning, and audit logging.Details:
SkillsLoader.auto_apply_pending_update(slug)— thin wrapper overapprove_pending_updatethat refuses script-bearing candidates (checks both the.meta.jsonflag and the physicalscripts/dir) and emits an informational "auto-updated to vN" notification on success.HistoryConsolidator._stage_skill_updatedecides eligibility: flag off and the candidate supplied no scripts at all. A candidate that supplied scripts never auto-applies, even if the validator rejected every script — mirroring the existing new-candidate rule.stage_skill_candidate(notify=False)); if promotion then fails, the review request is re-fired viaemit_pending_stagedso the still-pending candidate never sits invisible. On success an informational notification ("skill auto-updated to vN", prior version restorable) is raised instead — newset_update_auto_applied_hook, wired in the gateway next to the existing pending-staged hook.auto_applied_updateoutcome alongside the existingstaged_updateevent.stage_skill_candidate(which can return a name without staging when the collision family is exhausted) so auto-apply can never promote a different, previously-staged candidate.Fail-safe: any refusal or error during promotion leaves the candidate staged in the pending queue exactly as before.
Unchanged: script-bearing updates always stage;
approval_required=truealways stages (default behavior untouched).Also fixes the config documentation gap: the
skills.approval_requireddescription previously mentioned the script exception but said nothing about updates. It now states the full semantics.Tests
12 new tests (plus 7 unit tests on the routing logic in the existing update-flow module):
.versions/snapshot exists, queue empty, informational notification fired, review request not firedscripts/dir (defense in depth against direct-write candidates with missing metadata), stale-base refusal inherited fromapprove_pending_updatenotify=Falsesuppression +emit_pending_stagedre-fire payload contractVerified locally: targeted skill test modules (118 passed),
isort/flake8clean,mypyclean (877 files).