openspec/changes/extend-config-injection-to-apply-archive/design.md
OpenSpec project config currently provides a top-level context value and an artifact-keyed rules map. Artifact instruction generation reads both values at runtime, but the apply and archive workflow surfaces do not expose equivalent current inputs.
Apply already has a dynamic instruction command: openspec instructions apply --change <name>. Archive skills are generated from static templates and currently have no dedicated runtime-input command. Adding operation-specific advice directly to generated templates would make it stale whenever project config changes.
This change adds a small runtime contract for apply and archive without changing archive execution ownership. The existing single-change archive skill, bulk archive skill, spec sync behavior, and direct openspec archive command keep their current flows.
Goals:
operations.<operation>.guidance.specs artifact rules into archive-driven and standalone spec sync whenever concrete delta specs are merged into main specs.Non-Goals:
openspec archive, its flags, filesystem behavior, or compatibility contract.rules.specs artifacts.Project config gains this optional shape:
context: |
TypeScript project using pnpm.
rules:
specs:
- Preserve requirement IDs when meaning is unchanged.
operations:
apply:
guidance:
- Keep test summaries concise.
archive:
guidance:
- Summarize the archive outcome before finishing.
The in-memory model uses explicit operation IDs:
const OPERATION_IDS = ['apply', 'archive'] as const;
type OperationId = (typeof OPERATION_IDS)[number];
interface OperationConfig {
guidance?: string[];
}
Parsing remains resilient and field-by-field. An invalid operation entry is omitted with a warning without discarding valid context, rules, references, store settings, or other operation entries. Unknown operation IDs and unknown fields receive actionable warnings. Empty guidance strings are removed while non-empty strings retain their original order, line breaks, and Markdown.
Artifact rules remain unchanged and are not read as operation guidance.
Apply and archive instruction generation use a shared helper conceptually shaped as:
loadOperationInputs(projectConfig, operationId): {
context?: string;
operationGuidance?: string[];
}
The existing root-config loader calls readProjectConfig() once for each instruction command and passes that parsed ProjectConfig to the helper. The same config snapshot supplies references, context, and operation guidance, so malformed-field warnings are not duplicated and one command cannot mix values from two reads. There is no generated-skill or module-state cache, so the next command observes later config changes.
Absent context and empty guidance are omitted rather than returned as empty values.
generateApplyInstructions() adds the shared operation inputs to its existing result:
{
context?: string;
operationGuidance?: string[];
}
The existing apply state, task progress, missing-artifact checks, context files, references, and schema instruction remain unchanged. JSON serialization includes the new fields automatically. Text output renders project context as a required instruction-input section and operation guidance as a distinct advisory section after the built-in apply instruction content.
The apply skill template keeps both fields structurally separate from CLI-returned state, progress, tasks, missing artifacts, context files, and built-in instruction. When context is present, the agent must read it and apply relevant project facts, conventions, and constraints as a required prompt-level input. When operation guidance is present, the agent must read and consider it as optional additive advice and follow entries that are applicable and compatible with the built-in workflow.
This change does not modify CLI-controlled fields or their state transitions. The template tells the agent not to treat context or guidance as task completion, a replacement for the state-driven workflow, or permission to bypass a blocked state. It must report context conflicts with the built-in instruction, explicit user choices, or CLI-controlled values. If guidance is inapplicable or conflicts with those controlling inputs, the agent preserves the built-in flow and explains why the advice was not followed. It must not copy either field's contents into implementation files or planning artifacts.
openspec instructions archive --change <name> --json is handled as a workflow instruction branch alongside apply. It:
changeName, optional context, optional operationGuidance, and the normal resolved-root envelope;openspec archive.Human-readable output shows project context as a required instruction-input section and operation guidance as a separate advisory section. If neither value is configured, the command still succeeds with the change and root metadata so skill behavior is uniform.
Keeping this as an instruction surface makes the runtime contract available immediately while leaving archive execution redesign independent.
After resolving the target change and selected root, the single-change archive skill calls:
openspec instructions archive --change "<name>" --json
It must read returned context and apply relevant project facts, conventions, and constraints as a required prompt-level input. It reads and considers returned archive guidance as optional additive advice and follows applicable entries that are compatible with the built-in archive workflow. Explicit user choices, target paths, CLI checks, and command flags are not replaced or inferred from either field. Context conflicts are reported; conflicting or inapplicable guidance is not followed and the reason is explained.
A successful response may omit both optional fields, which means no archive operation inputs are configured. If the command exits non-zero or does not return valid archive-instruction JSON, the single-change skill reports the error and stops before inspecting or writing specs or moving the change. A failed lookup is never treated as an empty successful response.
The bulk archive skill makes the same call once for the selected root, using one selected change to establish context, and applies the returned inputs across that batch. If this lookup exits non-zero or returns invalid archive-instruction JSON, the skill reports the error and stops the batch before inspecting or writing specs or moving any change. It does not change the existing bulk conflict analysis or archive orchestration.
Semantic spec sync keeps its existing artifact contract. The concrete delta spec paths are exactly artifactPaths.specs.existingOutputPaths from the selected change's status output. If artifactPaths.specs is absent or its concrete output list is empty, that change has no delta specs for this workflow: archive continues without a spec-sync prompt, standalone sync reports that there is nothing to sync, and neither workflow infers delta specs from other artifacts.
When concrete specs outputs exist and a write-producing sync will run:
openspec instructions specs --change "<name>" --json once immediately before the semantic merge.A valid artifact-instruction response that omits rules means that no specs rules are configured and the existing semantic merge continues. A non-zero exit or a response that is not valid artifact-instruction JSON is a lookup failure, not an empty rule set. Single-change archive and standalone sync report that error and stop before modifying any main spec; archive also stops before moving the change.
The single-change archive skill fetches this specs-instruction snapshot after sync has been selected and immediately before invoking inline semantic sync. The bulk archive skill resolves every required specs-instruction snapshot after its sync decisions but before the first main-spec write; if any lookup fails, it reports the affected change and stops the whole batch before writing any main spec or moving any change. Archive passes each successful specs-rule snapshot into the inline sync workflow, which reuses it without fetching the same instructions again. When the sync skill is invoked directly, with no archive-supplied snapshot, it fetches current specs instructions itself.
For a mixed-schema batch, this decision is made independently for each change. A change whose resolved schema exposes concrete artifactPaths.specs.existingOutputPaths participates in spec sync and receives that change's current specs rules. A change whose schema has no specs artifact, such as a research/design/plan workflow, has no spec sync and continues through the existing archive path.
Artifact rules are not returned from the archive operation-input surface, relabeled as archive guidance, or applied to unrelated archive steps.
The archive, bulk archive, and sync templates retain the existing rule that runtime context, operation guidance, and rule text must not be copied verbatim into specs, change artifacts, summaries, or other files unless the user separately asks for that content. Artifact rules constrain the produced artifact without becoming artifact content.
Current context is a required prompt-level input, not optional-to-ignore metadata. When present, the generated skill must tell the agent to read it and apply relevant project facts, conventions, and constraints.
Operation guidance is optional additive advice. When present, the generated skill must tell the agent to read and consider it and to follow entries that are applicable and compatible with the built-in workflow. If guidance is inapplicable or conflicts with an explicit user choice, resolved path, CLI-controlled state, or command contract, the skill preserves the controlling value and explains why the advice was not followed.
Both semantics remain behavioral contracts for the agent, not enforcement mechanisms. OpenSpec guarantees that it validates the config shape, keeps fields separate from CLI-controlled values, delivers current inputs through the documented instruction surfaces, and leaves existing CLI checks unchanged. Existing checks continue to run wherever the current CLI already owns them. Any invariant that must be non-bypassable belongs in a real CLI check and remains outside this change; stronger archive guarantees require a separate archive execution design.
operationGuidance, and test that they do not affect unrelated archive steps.specs artifact -> Treat it as having no semantic spec-sync input; do not infer delta specs from unrelated artifacts.specs rules when concrete delta specs exist and reuse the same snapshot during inline sync.Rollback is a code revert. The config field is additive, and no archive filesystem format or durable project state changes in this change.
None.