Extend the Orchestra pre-push hook with a new Check 6 that enforces per-task file scoping based on an allowed_paths field in task spec frontmatter. Workers declare intent up front; the hook rejects commits that accidentally drift outside the declared scope.
The 2026-04-11 stale-worktree clobber incident showed that workers can accidentally roll back unrelated files (api.py, etc.) via git add -A on stale worktrees. The pre-push hook (Check 5) already blocks commits that touch critical files without naming them. This task adds precise, per-task file-scope enforcement:
allowed_paths (list of glob patterns)[Layer] prefix--allowed-paths.---
task_id: fc395b8d-c9a7-43d5-bc3a-a9f98892f879
title: "[Atlas] ..."
allowed_paths:
- "atlas/**"
- "wiki/**"
- "docs/atlas/**"
---If allowed_paths is absent, the layer-default applies.
allowed_paths field added to spec frontmatter format (list of glob strings)allowed_paths derived automatically from [Layer] prefix when not specifiedallowed_pathsallowed_paths globsORCHESTRA_SKIP_ALLOWED_PATHS=1 env var bypasses Check 6scripts/validate_allowed_paths.py supports --files arg for direct file list validationorchestra create --allowed-paths "a/ b/" writes globs to spec frontmatterglob_match() utility, integration test of hook reading from commitAdd allowed_paths (list of glob strings) to spec frontmatter. No change to existing spec files that omit it — layer-default applies.
orchestra_cli.py)--allowed-paths flag to cmd_create(): accepts space-separated glob pattern stringallowed_paths list to auto-generated spec file frontmatterLAYER_DEFAULT_PATHS = {
"agora": ["agora/**", "docs/agora/**", "tests/agora/**"],
"exchange": ["exchange/**", "docs/exchange/**", "tests/exchange/**"],
"forge": ["forge/**", "tools/**", "docs/forge/**", "tests/forge/**"],
"atlas": ["atlas/**", "wiki/**", "docs/atlas/**"],
"senate": ["senate/**", "docs/senate/**", "tests/senate/**"],
"ui": ["site/**", "static/**", "docs/ui/**"],
"system": ["orchestra/**", ".claude/**", "docs/system/**"],
}If task has no [Layer] prefix and no explicit allowed_paths, allow all (*).
In _pre_push_hook() (hooks.py), add after Check 5:
# ── Check 6: Per-task allowed_paths enforcement ───────────────
# Extract task IDs from commit messages in the push range.
# For each task, read its spec from the push commit (git show <sha>:<spec_path>)
# and validate that all changed files match the allowed_paths globs.Implementation steps in bash:
$remote_sha..$local_shagrep -oP '\[task:\K[^]]+'git show <sha> --name-only --diff-filter=)git show <sha>:<spec_path>allowed_paths from YAML frontmatter (Python one-liner)fnmatchPython fnmatch is sufficient for shell integration. Example:
from fnmatch import fnmatch
def allowed(path, patterns): return any(fnmatch(path, p) for p in patterns)Set ORCHESTRA_SKIP_ALLOWED_PATHS=1 in environment to bypass Check 6 (same pattern as existing ORCHESTRA_SKIP_CRITICAL_CHECK).
orchestra_cli.py, hooks.py, spec files at docs/planning/specs/orchestra/hooks.py — add Check 6 to _pre_push_hook()scripts/orchestra_cli.py — add --allowed-paths flag to cmd_create()"atlas/" → unquoted atlas/).title: "[Atlas] ...").
--files argument needed by pre-push hook.hooks/pre-push and adding --files arg to validate script.