TFD-0016: Permission Policy — Factory & Shipped Digital Talents

TFD-0016: Permission Policy — Factory & Shipped Digital Talents

Status: Draft Date: 2026-05-12 Decision maker: CTO (Clara) + CEO (Oscar) Source: RD-0003 hack #30 + factory audit revealing defaultMode: bypassPermissions neutralizes the existing allow list


Context

The Talent Factory currently runs with the following permission posture:

  • .claude/settings.json (committed): defines hooks (terminology guard, Jira naming, AC/DoD enforcement), enables 3 plugins. No permissions block.
  • .claude/settings.local.json (local, gitignored): substantial allow + deny lists, but "defaultMode": "bypassPermissions" — which renders the allow list largely irrelevant: everything is allowed except what's in deny.

Shipped digital talents (DAE-NNNN in OneDrive-STM/agent-ea/, vah-payroll, future deliveries) inherit no standard permission template. Each client's environment runs with whatever the operator configured locally.

This contradicts the foundry delivery model (feedback_delivery-model-foundry-not-hosted): we ship talents that customers run autonomously, so the customer's safety depends on the policy we ship with the talent, not on whatever local override the operator picks.

RD-0003 hack #30 articulates the principle: speed comes from the number of permission prompts, not from bypassing the permission layer. An explicit allow + deny list gives the same throughput as bypassPermissions without the same blast radius.

Decisions

D1 — Factory Dev posture

Decision: Replace defaultMode: bypassPermissions with defaultMode: acceptEdits in factory .claude/settings.local.json.

The existing allow list already covers the tools we use daily. With acceptEdits (or default) instead of bypass, Claude prompts only on commands not in the allow list — which is the desired friction (catches new bash patterns the operator hasn't vetted).

The deny list stays. Anything destructive remains blocked regardless of allow list ordering.

Action — Ivan: edit C:\Projects\talent-factory\.claude\settings.local.json, set defaultMode to "acceptEdits". Operator can override per-session via CLI flags if a legitimate need arises.

D2 — Shipped digital talents posture

Decision: All shipped digital talents must include a permissions block in .claude/settings.json (committed) with the standard allow/deny template below.

.claude/settings.local.json remains gitignored and per-operator; the committed settings.json becomes the safety floor that travels with the talent.

Standard template — to add to talent template (production-lines/digital-talent/templates/):

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Read", "Write", "Edit", "Glob", "Grep",
      "Bash(git status*)", "Bash(git log*)", "Bash(git diff*)", "Bash(git add*)", "Bash(git commit*)",
      "Bash(ls*)", "Bash(cat*)", "Bash(head*)", "Bash(tail*)",
      "Bash(python *)", "Bash(node *)", "Bash(npm run *)", "Bash(npx *)",
      "WebFetch", "WebSearch",
      "AskUserQuestion", "TaskCreate", "TaskUpdate", "TaskList", "TaskGet"
    ],
    "deny": [
      "Bash(git push --force*)", "Bash(git push -f*)",
      "Bash(git reset --hard*)", "Bash(git clean -f*)", "Bash(git checkout -- .)",
      "Bash(git branch -D*)",
      "Bash(rm -rf /*)", "Bash(rm -rf ~*)", "Bash(rm -rf C:*)",
      "Bash(sudo *)", "Bash(su *)", "Bash(runas *)",
      "Bash(npm publish*)", "Bash(pip uninstall*)",
      "Bash(kubectl delete*)", "Bash(docker system prune*)",
      "Read(*.env)", "Read(*credentials*)", "Read(*secret*)", "Read(*.pem)"
    ]
  }
}

Per-talent allow extensions go additive (e.g., DXF cadenassage talent adds Bash(ezdxf*)). Deny list is the universal floor — talents may add to it, never remove.

Deny prime sur allow is the Claude Code default — no special config needed for that.

Action — Pablo + Ivan: create production-lines/digital-talent/templates/permissions-template.json and wire into Stage 6 deployment checklist (06-deployment.md) as a required artifact.

D3 — Retroactive application to deployed talents

Decision: Apply the D2 template to all shipped talents (DAE-0001..0007, vah-payroll-2) as part of their next maintenance window or before the next handover, whichever comes first.

Not a blocker for current STM revenue (project_production-line-strategy — handover #1). Apply opportunistically.

Action — Max (Maintenance): track in factory-backlog as separate CI per talent. Schedule against handover cadence.

D4 — Agent Teams flag

Out of scope but flagged: .claude/settings.json factory currently has "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" in env. This contradicts feedback_no-agent-teams (MEMORY) which explicitly forbids agent teams in factory workflows.

Action — Clara: review whether this flag should be removed. Separate CI item (see CI-0034).

Rationale

Three forces converge:

  1. Foundry delivery model — customers run our talents independently; we own the safety floor we ship with.
  2. Hack #30 evidence — allow/deny matches --dangerously-skip speed in practice without the blast radius.
  3. Existing partial implementation — factory already has 90% of the pieces; the missing 10% (defaultMode, committed template, talent rollout) is low-effort, high-trust-gain.

Non-decisions (parked)

  • Per-client allow extensions (e.g., STM-specific commands): parked until a real client request triggers it. Default = no client-specific extensions.
  • Hook-level confirmation for high-stakes commands (npm publish, etc.): considered, deferred — deny is sufficient for now.