Skip to content

T-NUML-task-work-preflight-permissions-probe-extension-for-skill-internal-scripts

Status: closed/superseded · Impact: low · Complexity: medium

The pre-flight permissions probe shipped via [T-Z8VC-task-work-preflight-permissions-probe](/planning/tasks/task-work-preflight-permissions-probe/) checks package-manager grants, body-text tool-family signals, and Write/Edit coverage of the would-be worktree. It does NOT check the plugin’s own shell-out surface: the unified sdlc CLI launcher (${CLAUDE_PLUGIN_ROOT}cli/sdlc, which nearly every skill calls) and the handful of co-located skill scripts. When the sandbox denies one of those, the failure surfaces mid-skill — not at pre-flight — as an ERROR verdict from a sub-agent that couldn’t run the shell-out. Close the gap so the plugin’s shell-out paths are checked alongside the tool-family signals.

apps/sdlc/skills/task-work/preflight_permissions.ts (task-work Step 3b) resolves the project’s package managers into hard gaps, demotes body-text-only package-manager mentions to warnings, keeps body-text hard gaps for node/npx/pytest, and probes Write/Edit against the worktree path Step 4 will create. It has no knowledge of the plugin’s own shell-outs: a dispatch can pass pre-flight and still die at execution time when the sandbox lacks a grant covering the skill’s shell-out, returning ERROR reason="permission ... denied by the harness".

The current shell-out surface is small — nearly everything goes through one entry point:

LocationRole today
apps/sdlc/skills/task-work/preflight_permissions.tsThe probe Step 3b shells out to; covers package managers, non-PM body signals, and Write/Edit — not the plugin surface below
apps/sdlc/cli/sdlcLauncher shim for the unified CLI; the dominant shell-out (${CLAUDE_PLUGIN_ROOT}cli/sdlc <noun> <verb>, ~125 call sites across skill prose); needs a Bash(<plugin-root>/cli/sdlc:*) grant or covering glob
apps/sdlc/skills/task-work/start_task.tsExample of the co-located .ts scripts skills run via bun run; covered only when Bash(bun:*) is granted
apps/sdlc/skills/pr-check/post_self_comment.shShell wrapper invoked by path; needs its own grant or covering glob
apps/sdlc/skills/orchestrate/watch_loop.shShell wrapper invoked by path; same gap

Extend the probe with a plugin-surface check: verify the resolved sandbox permissions cover each entry of the plugin’s own shell-out surface (directly or via a covering glob) and report gaps in the same exit-1, one-line-per-gap shape as the existing checks. The surface is a short static list — the cli/sdlc launcher, the bun verb for co-located .ts scripts, and the two .sh wrappers — so encode it in the probe directly. Per-skill permission manifests or dynamic SKILL.md scanning would be machinery without a payload: the single CLI entry point is where the surface lives, and it is stable.

/sdlc:setup’s onboarding should also name the recommended grants, so they’re in place at session start rather than added after a denial. This matters because the harness does not reload settings.local.json mid-session: a grant added in response to a denial doesn’t reach sub-agents dispatched later in the same session.

  1. Encode the surface: add the plugin shell-out list (launcher path, bun verb, .sh wrapper paths) to preflight_permissions.ts as a static table resolved against CLAUDE_PLUGIN_ROOT.
  2. Extend the probe: for each surface entry, verify the resolved sandbox grants cover it (reusing the existing verb/path matching); report gaps with the existing exit-1-stdout-lines shape.
  3. Document the grants in setup: extend apps/sdlc/skills/setup/SKILL.md so first-run onboarding names the recommended grant set; re-running setup is a no-op.
  4. Test fixtures: probe against a settings fixture lacking the CLI grant — expect exit 1 and a line naming the uncovered path; with the grants present — expect exit 0.
LocationKindChange
apps/sdlc/skills/task-work/preflight_permissions.tsmodifyadd the plugin shell-out surface check; report gaps with the existing exit-1 format
apps/sdlc/skills/setup/SKILL.mdmodifyname the recommended plugin-surface grants in first-run onboarding; idempotent
apps/sdlc/skills/task-work/tests/preflight_permissions.test.tsmodifyfixtures exercising the plugin-surface check with and without the grants present
  • AC-1: Running the probe against a sandbox with no plugin-surface grants exits 1 with at least one stdout line naming an uncovered plugin shell-out path.
  • AC-2: Running the probe against a sandbox granting the surface (e.g. Bash(<plugin-root>/cli/sdlc:*) plus Bash(bun:*)) exits 0 with no plugin-surface gaps.
  • AC-3: /sdlc:setup first-run onboarding names the recommended grants; a second run is a no-op.
  • AC-4: A dispatch whose skill shells out to ${CLAUDE_PLUGIN_ROOT}cli/sdlc against a sandbox lacking that grant is caught at pre-flight by the extended probe, not at execution time inside the sub-agent.
  • AC-5: The project quality checks (bunx tsc --noEmit, bun test apps/sdlc) pass.
  • Per-skill permission manifests and dynamic SKILL.md scanning — the surface is a handful of stable paths behind one CLI entry point; enumeration machinery isn’t warranted (see Proposed).
  • Auto-granting permissions — the probe REPORTS gaps; it doesn’t write to sandbox config. The only install path is the documented /sdlc:setup onboarding guidance.
  • Cross-plugin permission concerns — this task is about the sdlc plugin’s own shell-outs. Other plugins handle their own permission story.
  • none. Builds on the existing pre-flight probe ([T-Z8VC-task-work-preflight-permissions-probe](/planning/tasks/task-work-preflight-permissions-probe/)).
  • Surfaced during a /sdlc:orchestrate session on 2026-05-24: a dispatched /sdlc:pr-check sub-agent immediately returned ERROR reason="permission ... denied by the harness" because the sandbox lacked a grant covering the skill’s shell-out. The orchestrator fell back to direct gh queries, losing the per-PR-cursor advance the skill provides. The probe should have caught the gap at pre-flight instead of spending a dispatch on it.
  • Same session, a second failure mode that still holds: after the user granted the missing permissions mid-session, a re-dispatched sub-agent still saw the stale pre-grant settings — the harness doesn’t reload settings.local.json mid-session. Grants must be present at session start, which is why the recommended grants belong in /sdlc:setup’s onboarding (Approach step 3).
  • Companion to [T-NP7H-task-work-sub-agent-verdict-contract-escape-recurrence](/planning/tasks/task-work-sub-agent-verdict-contract-escape-recurrence/) — both surfaced in the same orchestrate session.

← Back to Tasks