Skip to content

File scope fences: batch scope escalations (prompt contract) and warn on missing generated counterparts (config-declared, discovery/doctor) #644

Description

@HenryLach

Origin

Penster batch 20260909T000015: a task's ## File Scope was authored short (it named penster.yaml, an OpenAPI spec, but not api_gen.go nor the ~14 files importing its types). Penster's worker overlay treats scope as a hard fence — touching an out-of-scope file → escalate_to_supervisor → hold → ruling. The gap was discovered one file class at a time: three holds that could have been one.

Two observations from the Penster agent, assessed against core:

What is core's and what is not

## File Scope in Taskplane core drives lane affinity (extensions/taskplane/waves.ts:350-417) and is echoed into the merge prompt (merge.ts:678-700). The worker template only says "note out-of-scope discoveries in STATUS.md" (templates/agents/task-worker.md:286) — core never asks a worker to hold on a scope fence. The fence is a Penster overlay convention, and a good one; but "generated counterpart of an OpenAPI/proto/schema file" is toolchain knowledge core must not hard-code. Both items below stay project-agnostic.

1. Prompt contract — batch the escalation (cheap, ship first)

The engine cannot know which files a worker "can already see"; the worker does (a failed build lists them all). Make the contract explicit:

  • templates/agents/task-worker.md (escalation section): when escalating on a scope or permission fence, enumerate every file / file class you already know is affected — paths, not diffs — in ONE escalation; do not hold once per discovery. Note the outbox message cap (4 KB, agent-bridge-extension.ts) so long lists are summarised by directory/glob.
  • extensions/taskplane/supervisor-primer.md (ruling recipe): if the escalation lists N files, rule on all N in one ruling; ask (type="query") for the full list before ruling if it is truncated.
  • Docs: describe the scope-fence pattern as an opt-in overlay convention (docs/reference/task-format.md File Scope section) so other projects adopt it deliberately.

2. Engine — coupled-scope warning at discovery / doctor (config-declared, generic)

Let the project declare its own generator couplings:

"taskRunner": {
  "fileScope": {
    "coupled": [
      { "when": "api/openapi.yaml", "require": ["internal/api_gen.go", "internal/handlers/**"] },
      { "when": "proto/**/*.proto",  "require": ["gen/**"] }
    ]
  }
}

Discovery (and taskplane doctor) warns when a task's File Scope matches a when but omits any require: "TP-2101: api/openapi.yaml is in scope; internal/api_gen.go and internal/handlers/** are not — scope fences will hold on them." Uses the existing scopesOverlap (waves.ts:315-335) for matching. Deterministic, no toolchain knowledge in core; the project maintains the list.

Deferred variant (only if the declared form proves insufficient): a project-supplied expandCommand that doctor --scope <task> runs to derive the real dependency closure and diff it against the declared scope. More power, more surface.

Related

Acceptance

  • Worker template + primer updated; a fixture escalation listing 3 files produces one hold, one ruling.
  • taskRunner.fileScope.coupled parsed (config-schema/loader, YAML alias file_scope.coupled), validated, documented in the config reference.
  • Discovery warning + doctor warning with a unit test (scope matches when, misses a require → warning; all present → silent).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions