Smoke-10..14: every agent (developers included) got "Edit exists but is
not enabled in this context" and fell back to destructive bash
redirection (a 207-line README rewritten to a 3-line stub, which QA
correctly failed). Two coordinated defects in _generate_agent_settings /
_get_role_permissions:
1. base_deny carried a GLOBAL Write(*)/Edit(*). Claude Code evaluates
permission rules deny -> ask -> allow, first match wins — a deny
ALWAYS beats a more-specific allow and the glob syntax has no
negation. So the global deny unconditionally shadowed every per-role
workspace-scoped Write/Edit allow. Removed it; the security denies
that legitimately rely on deny-always-wins (Bash(git:*), credential
Read denies, curl github, env) stay. Roles that must not author
(qa, cell_pm, main_pm, auditor) keep their OWN Write(*)/Edit(*) deny.
2. The workspace allow used a single leading slash (Write(/data/...)).
Claude Code resolves a single / against the settings.json project
root, not the container filesystem root, so the allow silently never
matched even without defect #1. Emit the // absolute-filesystem form.
defaultMode stays bypassPermissions (switching to dontAsk would require
re-deriving the full allow-list and risks wedging agents elsewhere —
out of scope). Verified against Claude Code 2.1.114 permission docs.
Fixes 2 important + 1 minor issue from the code-quality review of 5adb4ff:
1. Formula duplication: the workspace path string was inlined at two
sites in orchestrator.py (the canonical _prepare_agent_spawn and the
new _build_mount_args -w logic). Extracted to module-level helpers
_agent_workspace_path(project, team, agent_id) and
_cell_workspace_path(project, team) so both callers share the same
formula. Future path changes only land in one place.
Also extracted _resolve_project_slug_from_git_context() as the
module-level counterpart to the instance method, called by the static
_build_mount_args site that cannot access self.
2. Test consistency: test_workdir_matches_edit_allowlist_path now
extracts the Edit(<prefix>/**) value from _get_role_permissions and
asserts the spawn cmd's -w value equals that prefix. The test would
actually catch a drift where _build_mount_args and _get_role_permissions
use different formulas — previously it just compared two copies of
the same string.
3. Test coverage: added test cases for product_owner and head_marketing
spawns (both share the per-agent workspace path), so all roles that
_get_role_permissions distinguishes are covered.
Spec ref: docs/superpowers/specs/2026-05-12-post-smoke-3-fixes-design.md
A2+A3 (re-scoped 2026-05-12).
Smoke run 3 surfaced two bugs that share a root cause:
- Edit(/app/README.md) → 'Edit exists but is not enabled in this context'
- commit(files=['/app/README.md']) → 'outside repository at <workspace>'
Both happened because the container's WORKDIR is /app (roboco package
source) while the agent's task workspace is bind-mounted at
/data/workspaces/<project>/<team>/<agent>/. The Dev role's
Edit/Write permission allowlist scopes to the workspace, so any Edit
call from /app fails the path match.
Adds '-w {workspace_path}' to the docker run command so the container
starts with cwd = task workspace. Edit(README.md) and git add README.md
now resolve inside the workspace clone.
Mirrors _get_role_permissions path selection exactly:
- developer / product_owner / head_marketing: per-agent workspace
- documenter: cell workspace (matches its Write/Edit allowlist)
- qa / cell_pm / main_pm / auditor: omit -w, fall back to /app
Spec ref: docs/superpowers/specs/2026-05-12-post-smoke-3-fixes-design.md
sections A2 + A3 (re-scoped per investigation 2026-05-12).