#1893·OpenSpec

Apply skill's static "Pause if" defaults override a custom schema's apply.instruction — extend #1405's "schema instruction is authoritative" principle to the apply phase

Author: pierreboissinotCreated Sep 15, 2026Updated Sep 15, 2026

Context

We run a project-local schema fork (via openspec schema fork spec-driven) whose apply.instruction defines an autonomous deviation policy: tasks.md is an indicative plan; the behavior contract (delta specs + acceptance .feature files) is what binds; when the implementation deviates from a task while the contract stays respected, the agent records the deviation inline under the checkbox and continues — pausing only on explicit [STOP]-marked tasks, contract impact, contradictions, or real blockers.

Problem

The apply workflow template (src/core/templates/workflows/apply-change.ts, rendered into both .claude/commands/opsx/apply.md and openspec-apply-change/SKILL.md) carries a static, unconditional "Pause if:" list (step 6) and matching Guardrails bullets, e.g.:

A task needs work beyond what the spec and tasks describe, or you are tempted to drop, narrow, defer, or accept exceptions to specified behavior to make it fit → surface the added scope and ask; do not absorb it silently

During /opsx:apply, the agent therefore receives two contradictory policies with no tiebreak: the template's static defaults and the CLI-returned schema instruction. Behavior becomes nondeterministic across sessions — one run pauses and asks, the next adapts — which defeats the predictability a custom schema exists to provide.

Notably, the template already resolves precedence for config-level inputs: context and operationGuidance "do not replace the built-in instruction […] preserve the controlling value". The built-in (CLI-returned) instruction is the schema's apply.instruction — yet the relationship between that controlling value and the template's own static pause list is unspecified. This looks like exactly the kind of gap #1456 aims to close.

Proposal

Declare the schema-authored instruction authoritative over the template's pause/deviation defaults, conditionally:

  • The static "Pause if" triggers remain the defaults for any schema whose apply instruction states no policy of its own — no behavior change for the built-in schemas, in line with "changes should work well for everyone".
  • When the CLI-returned instruction defines its own pause/continue or deviation policy, the agent follows it instead of the defaults.
  • User interrupts, errors, and real blockers always pause, regardless of schema policy.
  • The #1529 surface-added-scope behavior is untouched as the default.

Precedent and related work

  • #777 / #1405 established this exact principle for artifact creation: "the instruction field is the authoritative guidance, even for familiar artifact names". The apply phase is the remaining blind spot.
  • #444 made apply instructions schema-aware in the first place (apply.requires/tracks/instruction).
  • #1530 introduced the current pause trigger and describes itself as "advisory guidance, not an enforcement gate"; it did not consider custom schemas whose instruction says otherwise.
  • #1456 (open) is the umbrella design question on where phase behavior is configured and how precedence works — this issue is a concrete instance on the skill-vs-schema axis.
  • #825 (open) documents that schema instructions are advisory and can be ignored; an explicit precedence statement in the template reduces that ambiguity for the apply phase.
  • Distinct from #1830, which adds a separate opt-in autonomous-apply workflow; this proposal changes no default behavior and touches only the existing template's precedence wording (no file overlap).

Implementation

I have a small PR ready (template wording + tests extending test/core/templates/apply-defer-guardrail.test.ts, parity hashes and skills/ regenerated, changeset included). Happy to open it if this direction is acceptable, or to fold the wording into whatever #1456 decides.

Disclosure: the patch was written with AI assistance (Claude) and tested locally (pnpm build, full template test suite, tsc --noEmit, lint).