[System] Task-spec allowed_paths schema: per-task file-scope enforcement done

← Mission Control
Follow-up to the 2026-04-11 stale-worktree clobber incident. The pre-push hook (Check 5 in orchestra/hooks.py) already blocks critical-file commits that don't mention the file in the commit message. This task extends that to per-task file scoping. Add an 'allowed_paths' field to task spec frontmatter (list of glob patterns). orchestra create --allowed-paths sets it. The pre-push hook reads the current task's allowed_paths from the task's spec file and rejects commits whose diff touches files outside the allowlist. Default scope: [Layer] prefix → reasonable default (e.g. [Atlas] → atlas/**, wiki/**, docs/atlas/**). Tasks about core infrastructure can opt into broader scope via explicit --allowed-paths. This forces workers to declare intent up front and makes enforcement precise rather than just heuristic. ## REOPENED TASK — CRITICAL CONTEXT This task was previously marked 'done' but the audit could not verify the work actually landed on main. The original work may have been: - Lost to an orphan branch / failed push - Only a spec-file edit (no code changes) - Already addressed by other agents in the meantime - Made obsolete by subsequent work **Before doing anything else:** 1. **Re-evaluate the task in light of CURRENT main state.** Read the spec and the relevant files on origin/main NOW. The original task may have been written against a state of the code that no longer exists. 2. **Verify the task still advances SciDEX's aims.** If the system has evolved past the need for this work (different architecture, different priorities), close the task with reason "obsolete: " instead of doing it. 3. **Check if it's already done.** Run `git log --grep=''` and read the related commits. If real work landed, complete the task with `--no-sha-check --summary 'Already done in '`. 4. **Make sure your changes don't regress recent functionality.** Many agents have been working on this codebase. Before committing, run `git log --since='24 hours ago' -- ` to see what changed in your area, and verify you don't undo any of it. 5. **Stay scoped.** Only do what this specific task asks for. Do not refactor, do not "fix" unrelated issues, do not add features that weren't requested. Scope creep at this point is regression risk. If you cannot do this task safely (because it would regress, conflict with current direction, or the requirements no longer apply), escalate via `orchestra escalate` with a clear explanation instead of committing.

Git Commits (20)

[System] Work log update: tests done, bugs fixed [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-17
[System] Update spec: mark test acceptance criteria done [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-17
[System] allowed_paths: add unit+integration tests; fix quote-stripping + layer-prefix regex [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-17
[System] Add pre-push hook Check 6: allowed_paths enforcement [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-16
[System] allowed_paths: validate script + spec [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Spec work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Add hook reference to work log entry [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
[System] Work log update [task:fc395b8d-c9a7-43d5-bc3a-a9f98892f879]2026-04-11
Spec File

Goal

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.

Background

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:

  • Each task spec declares allowed_paths (list of glob patterns)
  • Default scope derived from [Layer] prefix
  • Hook reads the task's spec from the push commit itself and validates the diff

Default Scope by Layer

Layer prefixDefault allowed_paths globs
[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/**
Tasks about core infrastructure can opt into broader scope via explicit --allowed-paths.

Spec Frontmatter Schema

---
task_id: fc395b8d-c9a7-43d5-bc3a-a9f98892f879
title: "[Atlas] ..."
allowed_paths:
  - "atlas/**"
  - "wiki/**"
  - "docs/atlas/**"
---

If allowed_paths is absent, the layer-default applies.

Acceptance Criteria

allowed_paths field added to spec frontmatter format (list of glob strings)
☑ Default allowed_paths derived automatically from [Layer] prefix when not specified
☑ Pre-push hook Check 6: reads task spec from push-commit (not worktree), extracts allowed_paths
☑ Check 6 rejects push if any file in the diff falls outside allowed_paths globs
☑ Override mechanism: ORCHESTRA_SKIP_ALLOWED_PATHS=1 env var bypasses Check 6
scripts/validate_allowed_paths.py supports --files arg for direct file list validation
orchestra create --allowed-paths "a/ b/" writes globs to spec frontmatter
☑ Tests: unit test glob_match() utility, integration test of hook reading from commit

Implementation Plan

1. Spec file format

Add allowed_paths (list of glob strings) to spec frontmatter. No change to existing spec files that omit it — layer-default applies.

2. CLI changes (orchestra_cli.py)

  • Add --allowed-paths flag to cmd_create(): accepts space-separated glob pattern string
  • Write allowed_paths list to auto-generated spec file frontmatter
  • No DB schema change needed (spec_path column already links task ↔ spec file)

3. Layer-default map

LAYER_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 (*).

4. Pre-push hook Check 6

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:

  • Loop over commits in $remote_sha..$local_sha
  • Extract task IDs via grep -oP '\[task:\K[^]]+'
  • For each task ID, find the spec file path from the commit's files (git show <sha> --name-only --diff-filter=)
  • Read spec content via git show <sha>:<spec_path>
  • Parse allowed_paths from YAML frontmatter (Python one-liner)
  • For each file in the commit's diff, check against globs via Python fnmatch
  • If any file is outside all globs → BLOCK
  • 5. Glob matching utility

    Python fnmatch is sufficient for shell integration. Example:

    from fnmatch import fnmatch
    def allowed(path, patterns): return any(fnmatch(path, p) for p in patterns)

    6. Override

    Set ORCHESTRA_SKIP_ALLOWED_PATHS=1 in environment to bypass Check 6 (same pattern as existing ORCHESTRA_SKIP_CRITICAL_CHECK).

    Dependencies

    • Pre-existing: orchestra_cli.py, hooks.py, spec files at docs/planning/specs/
    • No new dependencies

    Files to Modify

  • orchestra/hooks.py — add Check 6 to _pre_push_hook()
  • scripts/orchestra_cli.py — add --allowed-paths flag to cmd_create()
  • No migration needed (spec files are git-tracked, no DB schema change)
  • Work Log

    2026-04-17 10:50 PT — Slot minimax:65 (reopen — completed)

    • Confirmed Check 6 and validate_allowed_paths.py are on origin/main (main == d7754ecc0).
    • Two acceptance criteria remained incomplete: unit tests + integration tests.
    • Added unit tests (32 tests covering path_allowed, LAYER_DEFAULT_PATHS, extract_layer_prefix, extract_allowed_paths).
    • Added integration tests (4 tests exercising full hook flow with temp git repos).
    • Fixed two bugs found while writing tests:
    1. extract_allowed_paths didn't strip quotes around glob patterns (e.g. "atlas/" → unquoted atlas/).
    2. extract_layer_prefix regex didn't handle quoted title values (title: "[Atlas] ...").
    • All 36 tests pass. Committed and pushed: 681b2ed58.
    • Investigated prior orphan branch situation. The e91daa7be commit (Check 6) was never on main due to failed push. The validate_allowed_paths.py and spec file DID land on main (dbd4a8c46, c7ccb4cf1).
    • Main hooks/pre-push is currently Check 5 only (model provenance) — missing Check 6.
    • scripts/validate_allowed_paths.py exists on main but lacks --files argument needed by pre-push hook.
    • Implemented Check 6 by updating hooks/pre-push and adding --files arg to validate script.
    • Updated spec acceptance criteria to reflect completed work.

    2026-04-12 — Slot minimax:61

    • Read AGENTS.md, Orchestra/AGENTS.md, hooks.py, orchestra_cli.py, services.py
    • Understood task scope: add allowed_paths to spec frontmatter, CLI flag, and pre-push hook Check 6
    • Creating spec file at docs/planning/specs/fc395b8d_c9a_spec.md

    Payload JSON
    {
      "completion_shas": [
        "dbd4a8c4614ad3614fdb36607887fc4b68d40304"
      ],
      "completion_shas_checked_at": "2026-04-12T03:16:18.812915+00:00",
      "completion_shas_missing": [
        "e50d680c83b7acc2b1cc06f8ef0447ac632fd6d4",
        "7a596c39d8f392242e40bd909736d1373e4e4466",
        "69b653a86f9877702f998839c8cc9bf623297b07",
        "d02b25cc2c5db651f44a2f82f81ca935e3c7e102",
        "e463abea09e104961085a72e236fda352abd0efe",
        "808b30fb9c7a164c9d82320cb4f8ff75524bb70e",
        "5162dc3c3bfd9cbc92df644f511f6f5c8fd1d75b",
        "a7d01a7f4a7aff1edb49285dbfdae13a6bfd6775",
        "50c6670099672b49f36e340ce9264515d8f2ca40",
        "4469815ed81ea1d4e487640ea5edc10f4acbc169",
        "27c1f8f91c22fe36fa253bf5515715a75d6bcc25",
        "7b62abb5954c261b2cccb5d260342e021cc67b94",
        "64a8af2af91fb11a60e093a73ae762224c7938a8",
        "9877ce80340fcafbf4c97f216382438cfb86c2c3",
        "2f56e46b717c2443dc1f9e1fbad41e1a1a5387df",
        "13076e428fa134f4edff04fe03833156ec32d1da",
        "7fbbbea43ebcda3168164e3fcfb21a62f3d15a5e",
        "16cc125679b599a7363f55fe8af4d1b925f1f4d9",
        "104d057eee7df059026e9efc540bf6d91d3abc7c",
        "0493b3577608244f8a782daa1ceb1c66fda6cb7b",
        "7f8cdde5bb3f1d7a4249108ec0616fdb6fcb8acf",
        "30ecd41c2f87a139225e156831ff1b53b7f56493",
        "7da278da69d8638df7ad9217b9d9976849f4767b",
        "1454ef7719ee561c9df9e5b7ec0ddbb303fbcfa4",
        "6c2f5009407723972214d530401665f41df37b1e",
        "edf03348d9df07779cc9708cf5baa31ff9f16ff0",
        "fd69e3a874ff8ac663c8dcb917fb566b2d5cc226",
        "d44dc816b17bb0a6661876be709c814e8571f969",
        "6978f46081b8523e0cdecbd6e8cb5584653565b8",
        "e74e301fab6d60c83539fd0cab539e0c63e09edf",
        "5c761db9467b4b9b49904026b7b4b690c7129d05",
        "0cc90428bffaec2ce979c0143ce738672316558f",
        "b98b1f99b4774d0a7b359ca07554d0395d9af643",
        "80d7f6757284b9766e495d6ed6026f4b5fe8dc16",
        "05011da9f827ef8181abef77ffbc5a003999f1c9",
        "b0cac486103b62cee0d1e75ae11947a7240f31bc",
        "e257660990936f0e721565e8fa7c1a3b7de01ba4",
        "887384430d043fb5c36974d870ebe5cb257bfd77",
        "11c229a8b493802dcf528ede4b3cdcc2573c9579",
        "b19fa26a95e35e603fd94a22a16b2b08065d1c04",
        "4b9bc5c9b07f2997f98b0d72f85e728fcdf30415",
        "32f14580b54776fcc2c500edae3b158bc3d305e7",
        "91013b0fa259629396695e161eb0308a5836e5a0",
        "7ac2f0cf49ccaad2d87551e64abb8fa8d1bf9e16",
        "a95ba4c77ba83bcb6fa60295dca40a01129dab9d",
        "ed0ef7687375153ba12785017e747c07f9f90bc0",
        "cad4ca0297aca4a4acf1540897b41149c9aaedf4",
        "3c5d18bf22fb668ebd9978fbfe39ea4c9d7bb97b",
        "71614fdab64a3bd9d86e0185e644cc3fa2847673",
        "8e00871e3c08491c0f87f7c949bc52755b178acb",
        "95f6621dce4b849761f5d579ca46e75f446941d9",
        "cb5c05499da8365677bf59d1a1e9ebc06cdb8ede",
        "df8efbb124d6fa8ae001919df516236c1041e9ef",
        "92e4f4bff488851688708f578f6624d110823995",
        "2048896aa8075aec14132a6a8a0b1dbb0a26973d",
        "b061dd2d89b3d072eae646ae1e0ad71a28e60d69",
        "c6f0fa46fddeb531de2ef15d6e6c9aa0c8c4212f",
        "cd8d868f6ae5ce408e68e52959f0263060703120",
        "b875d0ea6663e5e63c9abd492e46f768a8eaac58",
        "cf8f7bbb2ca3955051a44e5ba2416cb18d176226",
        "821edfb2f1202be769da0de8b128275982a624e0",
        "5627c9e5fec7de02ba432f8f2f9e3b4e0c301444",
        "6a4d9457a2e739320cc76fea775bc2d6dab5b3fd",
        "e7862bfaec0bff5fd6c50eda4b99798f5d8f76bc",
        "bfabbe157bf15e9dab98d07fb72235438d853c49",
        "9d3d0568f9c69d317107f57505704e8ead3c6bae",
        "ca595fc2aa6f0799833c37d3756357e67c1ffedd",
        "b5f94ea5eca7b6eb5bc86bd40862aa1a2cf55d7d",
        "d15b1d008906cb4fd47e454a8ce1f05a9bec0bc3",
        "ffc2db292c5037fbf284932a2c853f592d75c71c",
        "6d62e68582ac7f54f2db36a1d8b6d7276e7252c9",
        "77d549890f01bf9a5436e98e69eba2f8227d5b64",
        "16a9847809f3fe61cd88131941a5b9f91b51bede",
        "2fb26cd9e385809edab716d2e816f198ede7dcfe",
        "41035f4ea8d3a81b86075a1f70c328cde6d1bb9d",
        "a66c2b1b868cfb84ee65a655c4f28d1306b25209",
        "e573c558fe8c61a2164bfe9856117b5ff6b502fc",
        "56a029d1588544a2cba6f16f8e885535dcd8a6e0",
        "a2a07900003ccd36eccaaa4ceb53ea791cef69fe",
        "bf05549b5a2b33fbfa82a18fff597db353eaa821",
        "845fc7e9363601953bb6e6dd051c566949d34316",
        "b48fb4082bc537f361c9075dde61fcb17ea9d0b8",
        "891e8f845f44312824e8f12d31c9614975ab95c3",
        "7589aacb2da749d7a607563f1669a24ec7eec44b",
        "f3dea81912538795084f3745aaf2e0d155754394",
        "96ab8dd40077197dec53ffebca95e14263dfd706",
        "57cc57cea6237db397ef2499905172d9c23ec762",
        "5e408ef9cdb005977d637c391625f7967d93f6fb",
        "a54dfc2dbbd154e01eaee3e80aaf204957427dbc",
        "c2a3de3f1a6a562dfc75990c5bd10c3a28fd5c0c",
        "dcc1f2a567e6511e691af7017c433b301cfb4d0a",
        "df39ba8e6754763e2b8527805dc845ab21a33c2f",
        "b1a480a1dc1516f23ae218a07d7869a692f49b6a",
        "ce8b4a26f5d42aea4d51ef76a38bc44383689150",
        "c2e465d22f56016061c42e7f77ab2d1dee707818",
        "8af8e3090c3b35db439f3dc91dedeb27986ef95b",
        "868243314cb18a3d149a55379623afae67d8ee78",
        "8d259872653ee058c5676aec5ba52b14d8666645",
        "e29445dab0c0f60b2612330f5a727c393c4f9ae0",
        "ac8a7da71fc7449e0e320bf067661e228bf49b0f",
        "58a3624caa4a32d160c971e9449c8fa8d2dbe890",
        "57fd7fca54faccae58fcd6a60c628bfb43ee3541",
        "3d5e55ea53fe25de3b85257988cf2118b50146a1",
        "6c6070296a3108c188cddfd959f851b12a01fcba",
        "0fb24c4b1bfcd4f32b7aa4f036b5cd73e7037665",
        "1423f2a32d80afdb371c1341c13ffb59ed5bf997",
        "a05163ffb2b1d02f716e7a3626ab377f9a7f34e0",
        "e251f9ce8ee760939606d1d6d968943e0fb27178",
        "56a9b165f8892aa99196398a80fba8f1bb8b3323",
        "c5da14db6dc56b2ab826ee2f9c8bbc5a5057eb6b",
        "9d3349af339bf55829e8e64c899a3ff90a1a6663",
        "d52ee5f5e3dc8a54b78d23dfee1448ca08535971",
        "491b1ee44b6c03283dd998069200d098d8aef7aa",
        "d31a966b7658962bb84c7dc92b3b00f649af11ef",
        "9e736dd4606b0c46111991762b4e5612c236506d",
        "c5e30ebaf482bccba1bd89df2c478cba84731a45",
        "20a43de3db12e2422bfdd9334b4d133e13da15e4",
        "dc46250349f2be02233ea08be995520b8ed4710f",
        "d031fdb23041711e56d9c6492105e15e48f6d2c4",
        "96cbd4ad5228fa7df7a5731347ef032d22119029",
        "b8c9e56eddbc365d8745245ba81ec3e428c48a0b",
        "956fb359957ba6b00a6cfea589e6b05797d33b77",
        "a4ce0128cc903f88054bf5f7e904b33d4b83b243",
        "1a0bd92a36d2442577083a832b8e4a1755eaa405",
        "8298f74daf4306d812b584cbb3e206d225ffa1b3",
        "b38fcb80ba3e351d09f8f0e7ec8ea51d4e7d2de3",
        "5da214254acef57c066421540793daebdfa2b183",
        "cd5d26c79c442cf991b1075b6fcebba0964e548a",
        "7236808eeae85a4103c2a110a4bdba64200b8898",
        "daff96e6c7f559c7ed7e28f2191df18e833ff377",
        "8d665cd97884f4c4f3bb452e49291d4c74bdcdd5",
        "e46cfcf09a576fcab91fccd70f2bcf6938339d57",
        "d2dd64f6c3764470b74ddb42b6b3a989f707a828",
        "002e6d5826727a9a46d95e4d7ac04c92630c016e",
        "c7ccb4cf18acbffc9452a457c8c369ebb94c8db1",
        "e91daa7beb0f30d757632be11bd8c8281d59cd2d",
        "193555d2fd4c35e7d59b12f4ce1ac64bbf27b0b8",
        "2035b9fa9071f8a64290efaaa7976be8e61c0dd3",
        "4386c0859f1a9efd7a140f1893f55fe1985ee778",
        "754f068e214c3dabb07ab9df3352bef7a266a194",
        "58d37f75224babd3592282b937fca7dd64ea3d09",
        "5e04909acdfd18c39d54893008b930557218962c",
        "c3fa1e46b190732c74c3eb84ef448031fe924677",
        "64e55a29b5e5294229a448481628228030d537a0",
        "758c5a592c1e09376e26e81c6260c68451c31d37",
        "0a120bbd5bda21c2126be13f5bb7a00f8b4be60b",
        "d3b855ba78a9344f0964cbbc42710fe039ca4005",
        "fb2f00630869c38d6adbfb8cd38cbdb7c6827bb4",
        "81aead8e8b8eec57502cecea2995dfaea317c42c",
        "93f01e5c339f327ea0aa63d8be48919b9402b829",
        "068e7e4c8661372e9a7f308882c962c567ac233c",
        "2c87ded6380ba2719984e269e0983a20356f0ba1",
        "0b43bb05bc744815501ccebc9c365a702c6c235e",
        "92c9ac83490392a5402f053d560202b3b355b8db",
        "9efd4592ea08e43a2d89c6cae1ca7197aee864bc",
        "e9c7a15439842be62a7be18165982950f8608d61",
        "1c1f909608d2d26cc9ccf79c97777dba0ca8754b",
        "79dea076e30b88e0a5f203a6375b56f9d178906c"
      ],
      "_reset_note": "This task was reset after a database incident on 2026-04-17.\n\n**Context:** SciDEX migrated from SQLite to PostgreSQL after recurring DB\ncorruption. Some work done during Apr 16-17 may have been lost.\n\n**Before starting work:**\n1. Check if the task's goal is ALREADY satisfied (run the relevant checks)\n2. Check `git log --all --grep=task:YOUR_TASK_ID` for prior commits\n3. If complete, verify and mark done. If partial, continue. If not done, proceed.\n\n**DB change:** SciDEX now uses PostgreSQL. `get_db()` auto-detects via\nSCIDEX_DB_BACKEND=postgres env var.",
      "_reset_at": "2026-04-18T06:29:22.046013+00:00",
      "_reset_from_status": "done"
    }