Skip to content

Commit e2c333e

Browse files
authored
fix(instructions): separate context and rules from template in JSON output (Fission-AI#547)
* fix(instructions): separate context and rules from template in JSON output Previously, project config context and rules were prepended to the template field in artifact instructions. This caused AI assistants to literally copy these constraint blocks into generated artifact files, rather than treating them as instructions. Changes: - Add `context` and `rules` as separate fields in ArtifactInstructions interface - Update generateInstructions() to populate these as distinct fields - Update CLI output to display context/rules with clear "do not include" comments - Rename <context> (dependencies) to <dependencies> to avoid confusion - Update tests for new structure The JSON output from `openspec instructions` now clearly separates: - `context`: Project background (constraints for AI) - `rules`: Artifact-specific rules (constraints for AI) - `template`: The actual structure for the output file * fix(skills): update skill templates with separate context/rules documentation Update generated skill templates (continue-change, ff-change) to document that context and rules are separate JSON fields that should NOT be copied into artifact output files. Users running `openspec update` will get the updated skill instructions.
1 parent e137dd3 commit e2c333e

4 files changed

Lines changed: 120 additions & 92 deletions

File tree

src/commands/artifact-workflow.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ function printInstructionsText(instructions: ArtifactInstructions, isBlocked: bo
313313
outputPath,
314314
description,
315315
instruction,
316+
context,
317+
rules,
316318
template,
317319
dependencies,
318320
unlocks,
@@ -339,9 +341,29 @@ function printInstructionsText(instructions: ArtifactInstructions, isBlocked: bo
339341
console.log('</task>');
340342
console.log();
341343

342-
// Context (dependencies)
344+
// Project context (AI constraint - do not include in output)
345+
if (context) {
346+
console.log('<project_context>');
347+
console.log('<!-- This is background information for you. Do NOT include this in your output. -->');
348+
console.log(context);
349+
console.log('</project_context>');
350+
console.log();
351+
}
352+
353+
// Rules (AI constraint - do not include in output)
354+
if (rules && rules.length > 0) {
355+
console.log('<rules>');
356+
console.log('<!-- These are constraints for you to follow. Do NOT include this in your output. -->');
357+
for (const rule of rules) {
358+
console.log(`- ${rule}`);
359+
}
360+
console.log('</rules>');
361+
console.log();
362+
}
363+
364+
// Dependencies (files to read for context)
343365
if (dependencies.length > 0) {
344-
console.log('<context>');
366+
console.log('<dependencies>');
345367
console.log('Read these files for context before creating this artifact:');
346368
console.log();
347369
for (const dep of dependencies) {
@@ -352,7 +374,7 @@ function printInstructionsText(instructions: ArtifactInstructions, isBlocked: bo
352374
console.log(` <description>${dep.description}</description>`);
353375
console.log('</dependency>');
354376
}
355-
console.log('</context>');
377+
console.log('</dependencies>');
356378
console.log();
357379
}
358380

@@ -372,6 +394,7 @@ function printInstructionsText(instructions: ArtifactInstructions, isBlocked: bo
372394

373395
// Template
374396
console.log('<template>');
397+
console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
375398
console.log(template.trim());
376399
console.log('</template>');
377400
console.log();

src/core/artifact-graph/instruction-loader.ts

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ export interface ArtifactInstructions {
5959
description: string;
6060
/** Guidance on how to create this artifact (from schema instruction field) */
6161
instruction: string | undefined;
62-
/** Template content (structure to follow) */
62+
/** Project context from config (constraints/background for AI, not to be included in output) */
63+
context: string | undefined;
64+
/** Artifact-specific rules from config (constraints for AI, not to be included in output) */
65+
rules: string[] | undefined;
66+
/** Template content (structure to follow - this IS the output format) */
6367
template: string;
6468
/** Dependencies with completion status and paths */
6569
dependencies: DependencyInfo[];
@@ -218,14 +222,11 @@ export function generateInstructions(
218222
const dependencies = getDependencyInfo(artifact, context.graph, context.completed);
219223
const unlocks = getUnlockedArtifacts(context.graph, artifactId);
220224

221-
// Build enriched template with project config injections
222-
let enrichedTemplate = '';
223-
let projectConfig = null;
224-
225225
// Use projectRoot from context if not explicitly provided
226226
const effectiveProjectRoot = projectRoot ?? context.projectRoot;
227227

228-
// Try to read project config
228+
// Try to read project config for context and rules
229+
let projectConfig = null;
229230
if (effectiveProjectRoot) {
230231
try {
231232
projectConfig = readProjectConfig(effectiveProjectRoot);
@@ -252,23 +253,10 @@ export function generateInstructions(
252253
}
253254
}
254255

255-
// 1. Add context (all artifacts)
256-
if (projectConfig?.context) {
257-
enrichedTemplate += `<context>\n${projectConfig.context}\n</context>\n\n`;
258-
}
259-
260-
// 2. Add rules (only for matching artifact)
256+
// Extract context and rules as separate fields (not prepended to template)
257+
const configContext = projectConfig?.context?.trim() || undefined;
261258
const rulesForArtifact = projectConfig?.rules?.[artifactId];
262-
if (rulesForArtifact && rulesForArtifact.length > 0) {
263-
enrichedTemplate += `<rules>\n`;
264-
for (const rule of rulesForArtifact) {
265-
enrichedTemplate += `- ${rule}\n`;
266-
}
267-
enrichedTemplate += `</rules>\n\n`;
268-
}
269-
270-
// 3. Add original template (without wrapper - CLI handles XML structure)
271-
enrichedTemplate += templateContent;
259+
const configRules = rulesForArtifact && rulesForArtifact.length > 0 ? rulesForArtifact : undefined;
272260

273261
return {
274262
changeName: context.changeName,
@@ -278,7 +266,9 @@ export function generateInstructions(
278266
outputPath: artifact.generates,
279267
description: artifact.description,
280268
instruction: artifact.instruction,
281-
template: enrichedTemplate,
269+
context: configContext,
270+
rules: configRules,
271+
template: templateContent,
282272
dependencies,
283273
unlocks,
284274
};

src/core/templates/skill-templates.ts

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,17 @@ export function getContinueChangeSkillTemplate(): SkillTemplate {
434434
\`\`\`bash
435435
openspec instructions <artifact-id> --change "<name>" --json
436436
\`\`\`
437-
- Parse the JSON to get template, dependencies, and what it unlocks
438-
- **Create the artifact file** using the template as a starting point:
437+
- Parse the JSON. The key fields are:
438+
- \`context\`: Project background (constraints for you - do NOT include in output)
439+
- \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
440+
- \`template\`: The structure to use for your output file
441+
- \`instruction\`: Schema-specific guidance
442+
- \`outputPath\`: Where to write the artifact
443+
- \`dependencies\`: Completed artifacts to read for context
444+
- **Create the artifact file**:
439445
- Read any completed dependency files for context
440-
- Fill in the template based on context and user's goals
446+
- Use \`template\` as the structure - fill in its sections
447+
- Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
441448
- Write to the output path specified in instructions
442449
- Show what was created and what's now unlocked
443450
- STOP after creating ONE artifact
@@ -489,7 +496,10 @@ For other schemas, follow the \`instruction\` field from the CLI output.
489496
- Never skip artifacts or create out of order
490497
- If context is unclear, ask the user before creating
491498
- Verify the artifact file exists after writing before marking progress
492-
- Use the schema's artifact sequence, don't assume specific artifact names`
499+
- Use the schema's artifact sequence, don't assume specific artifact names
500+
- **IMPORTANT**: \`context\` and \`rules\` are constraints for YOU, not content for the file
501+
- Do NOT copy \`<context>\`, \`<rules>\`, \`<project_context>\` blocks into the artifact
502+
- These guide what you write, but should never appear in the output`
493503
};
494504
}
495505

@@ -699,12 +709,15 @@ export function getFfChangeSkillTemplate(): SkillTemplate {
699709
openspec instructions <artifact-id> --change "<name>" --json
700710
\`\`\`
701711
- The instructions JSON includes:
702-
- \`template\`: The template content to use
712+
- \`context\`: Project background (constraints for you - do NOT include in output)
713+
- \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
714+
- \`template\`: The structure to use for your output file
703715
- \`instruction\`: Schema-specific guidance for this artifact type
704716
- \`outputPath\`: Where to write the artifact
705717
- \`dependencies\`: Completed artifacts to read for context
706718
- Read any completed dependency files for context
707-
- Create the artifact file following the schema's \`instruction\`
719+
- Create the artifact file using \`template\` as the structure
720+
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
708721
- Show brief progress: "✓ Created <artifact-id>"
709722
710723
b. **Continue until all \`applyRequires\` artifacts are complete**
@@ -734,7 +747,10 @@ After completing all artifacts, summarize:
734747
- Follow the \`instruction\` field from \`openspec instructions\` for each artifact type
735748
- The schema defines what each artifact should contain - follow it
736749
- Read dependency artifacts for context before creating new ones
737-
- Use the \`template\` as a starting point, filling in based on context
750+
- Use \`template\` as the structure for your output file - fill in its sections
751+
- **IMPORTANT**: \`context\` and \`rules\` are constraints for YOU, not content for the file
752+
- Do NOT copy \`<context>\`, \`<rules>\`, \`<project_context>\` blocks into the artifact
753+
- These guide what you write, but should never appear in the output
738754
739755
**Guardrails**
740756
- Create ALL artifacts needed for implementation (as defined by schema's \`apply.requires\`)
@@ -1206,10 +1222,17 @@ export function getOpsxContinueCommandTemplate(): CommandTemplate {
12061222
\`\`\`bash
12071223
openspec instructions <artifact-id> --change "<name>" --json
12081224
\`\`\`
1209-
- Parse the JSON to get template, dependencies, and what it unlocks
1210-
- **Create the artifact file** using the template as a starting point:
1225+
- Parse the JSON. The key fields are:
1226+
- \`context\`: Project background (constraints for you - do NOT include in output)
1227+
- \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
1228+
- \`template\`: The structure to use for your output file
1229+
- \`instruction\`: Schema-specific guidance
1230+
- \`outputPath\`: Where to write the artifact
1231+
- \`dependencies\`: Completed artifacts to read for context
1232+
- **Create the artifact file**:
12111233
- Read any completed dependency files for context
1212-
- Fill in the template based on context and user's goals
1234+
- Use \`template\` as the structure - fill in its sections
1235+
- Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
12131236
- Write to the output path specified in instructions
12141237
- Show what was created and what's now unlocked
12151238
- STOP after creating ONE artifact
@@ -1261,7 +1284,10 @@ For other schemas, follow the \`instruction\` field from the CLI output.
12611284
- Never skip artifacts or create out of order
12621285
- If context is unclear, ask the user before creating
12631286
- Verify the artifact file exists after writing before marking progress
1264-
- Use the schema's artifact sequence, don't assume specific artifact names`
1287+
- Use the schema's artifact sequence, don't assume specific artifact names
1288+
- **IMPORTANT**: \`context\` and \`rules\` are constraints for YOU, not content for the file
1289+
- Do NOT copy \`<context>\`, \`<rules>\`, \`<project_context>\` blocks into the artifact
1290+
- These guide what you write, but should never appear in the output`
12651291
};
12661292
}
12671293

@@ -1474,12 +1500,15 @@ export function getOpsxFfCommandTemplate(): CommandTemplate {
14741500
openspec instructions <artifact-id> --change "<name>" --json
14751501
\`\`\`
14761502
- The instructions JSON includes:
1477-
- \`template\`: The template content to use
1503+
- \`context\`: Project background (constraints for you - do NOT include in output)
1504+
- \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
1505+
- \`template\`: The structure to use for your output file
14781506
- \`instruction\`: Schema-specific guidance for this artifact type
14791507
- \`outputPath\`: Where to write the artifact
14801508
- \`dependencies\`: Completed artifacts to read for context
14811509
- Read any completed dependency files for context
1482-
- Create the artifact file following the schema's \`instruction\`
1510+
- Create the artifact file using \`template\` as the structure
1511+
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
14831512
- Show brief progress: "✓ Created <artifact-id>"
14841513
14851514
b. **Continue until all \`applyRequires\` artifacts are complete**

0 commit comments

Comments
 (0)