T-QJY7-orchestrate-grants-skill-tool-to-subagents
Status: closed/done · Impact: medium · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
/sdlc:task-work Step 5a invokes /sdlc:task-ensure-ready via the
Skill tool. When /sdlc:task-work is itself dispatched by
/sdlc:orchestrate, the nested Skill tool call is denied — the
dispatching context did not grant Skill to the sub-run. The agent
falls back to inlining the ensure-ready logic (read
implementation-ready.md, evaluate, stamp, commit), which works but
diverges from the canonical implementation and loses the benefit of
re-running a single source of truth.
/sdlc:task-work is in plugin/skills/task-work/SKILL.md, declaring
allowed-tools: that includes Skill. When the user runs
/sdlc:task-work directly, that authorization holds and Step 5a’s
Skill invocation succeeds. When /sdlc:orchestrate dispatches
/sdlc:task-work as part of one tick (see
plugin/skills/orchestrate/SKILL.md), the dispatched run’s Skill
calls return “Permission to use Skill has been denied” — observed on
the 2026-05-20 run of T-UBJK-co-locate-skill-specific-scripts.
Proposed
Section titled “Proposed”/sdlc:orchestrate adds Skill to its dispatched
/sdlc:task-work sub-run’s tool grants. Today’s
plugin/skills/orchestrate/SKILL.md:13-18 declares
allowed-tools: [Bash, Read, Write, Agent, PushNotification] —
no Skill. The dispatched sub-agent inherits the orchestrator’s
grants (not task-work’s own allowed-tools: declaration), which
is why nested Skill calls get denied. Adding Skill to the
orchestrator’s grant list propagates the permission down to the
sub-agent.
Rejected alternative: documenting an inline-fallback path in task-work. Skills compose by calling each other; the orchestrator should not be a permission downgrade.
Approach
Section titled “Approach”- Add
Skilltoplugin/skills/orchestrate/SKILL.md’sallowed-tools:frontmatter list. - Add a forbidden-phrase guard to
plugin/skills/orchestrate/invariants.yamlthat assertsSkillis present in the allowed-tools — prevents silent regression if someone trims the list later. - Add a regression case to
plugin/skills/orchestrate/tests/run_evals.py(or equivalent) that asserts the frontmatterallowed-toolslist containsSkill.
Files to touch
Section titled “Files to touch”plugin/skills/orchestrate/SKILL.md— addSkilltoallowed-tools:.plugin/skills/orchestrate/invariants.yaml— add the presence-guard.plugin/skills/orchestrate/tests/run_evals.py— add the frontmatter-check regression case.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: A fresh
/sdlc:orchestratetick dispatching a ready task into/sdlc:task-workruns/sdlc:task-ensure-readyvia theSkilltool without permission denial. - AC-2: The dispatched run still respects the one-line-verdict contract on completion.
- AC-3:
plugin/skills/orchestrate/tests/run_evals.pyincludes a case that loadsorchestrate/SKILL.md’s frontmatter and assertsSkillis inallowed-tools. Failing to list it fails the test.
Out of scope
Section titled “Out of scope”- Broader allowed-tools propagation semantics (e.g. inheritance
rules for arbitrary nesting depth). This task only adds the
single
Skillgrant the dispatched task-work needs. - Reform of how task-work’s own
allowed-tools:is honored when invoked top-level vs as a sub-agent — see2026-05-21-investigate-skill-prompt-freshnessfor that surface.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-UBJK-co-locate-skill-specific-scripts on 2026-05-20.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-21. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: deferred-user — requires a fresh
/sdlc:orchestratetick dispatching a ready task to confirm the nested/sdlc:task-ensure-readySkill call succeeds end-to-end. The grant is in place (plugin/skills/orchestrate/SKILL.mdallowed-tools:now containsSkill), but verifying the runtime behavior requires live orchestrator dispatch which isn’t reachable from inside this task-work run. - AC-2: deferred-user — same live-dispatch dependency. The one-line-verdict
contract is enforced by orchestrate’s truncation guard, which is
exercised by the existing 8-case eval suite in
run_evals.py; the property doesn’t regress with this change, but the integrated path (orchestrate → task-work → ensure-ready, return verdict) needs a live tick to confirm. - AC-3: auto —
plugin/skills/orchestrate/tests/run_evals.pynow loadsSKILL.md’s frontmatter, parsesallowed-tools:, and assertsSkillis in the list. Verified to PASS as-is, FAIL whenSkillis stripped, and PASS again on restore (regression-test demonstration).
What worked
Section titled “What worked”- The presence-guard property had two independent enforcement paths
available: the existing
invariants.yamllint mechanism and a new check in the eval suite. Both landed cleanly with small diffs. extract_allowed_toolsis dependency-free (noyamlimport) so the PEP-723 self-bootstrapping script header keeps its emptydependencies = []list — no new install cost.
Friction and automation gaps
Section titled “Friction and automation gaps”- The Step 5b rebase surfaced a trivial conflict because Step 5a’s
feat-branch readiness-stamp commit touched the same
last_reviewed:/readiness_verified_at:lines that Step 5b’s main commit then updated. The conflict was mechanical and the resolution obvious (take main’s newer values), but a task-work helper that knows the fixed three-line shape and resolves it automatically would remove a manual touch point from every run. → T-H98A-last-reviewed-rebase-collision - The task spec said “forbidden-phrase guard that asserts
Skillis present” — that phrasing is internally contradictory (forbidden phrases enforce absence; presence is arequired_phrasesconcern). Implementation inferred the intent correctly, but a tighter spec template that distinguishes presence vs. absence guards by name would prevent the same ambiguity on future invariants-touching tasks. → skipped (no concrete template artifact to change; this was a hand-written spec, not output of a generator).
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-H98A-last-reviewed-rebase-collision — linked to existing task that already covers the Step 5b rebase-conflict gap.