Skip to content

feat: inject config.yaml context and rules into apply/verify phases#887

Open
k1r3zz wants to merge 4 commits intoFission-AI:mainfrom
k1r3zz:main
Open

feat: inject config.yaml context and rules into apply/verify phases#887
k1r3zz wants to merge 4 commits intoFission-AI:mainfrom
k1r3zz:main

Conversation

@k1r3zz
Copy link
Copy Markdown

@k1r3zz k1r3zz commented Mar 28, 2026

Summary

Previously, config.yaml rules only worked for artifact generation phases (proposal, specs, design, tasks). The apply and verify phases had no access to project-level rules, making it impossible to customize execution strategies (e.g., subagent dispatching, parallel verification scans).

Changes

Core: Apply phase now reads config

  • generateApplyInstructions() reads config.yaml context + rules.apply
  • printApplyInstructionsText() outputs Project Context and Rules sections
  • ApplyInstructions interface gains optional context and rules fields

Core: New verify instructions system

  • New generateVerifyInstructions() reads config.yaml context + rules.verify
  • New verifyInstructionsCommand() + printVerifyInstructionsText()
  • New VerifyInstructions interface in shared.ts
  • Fix: verify templates previously called openspec instructions apply — now correctly call openspec instructions verify

CLI

  • openspec instructions verify is now a valid command

Config validation

  • apply and verify are accepted as valid rule keys in validateConfigRules() (previously triggered unknown-artifact warnings)

Templates

  • Apply skill/command templates: if rules are present, AI must follow them as mandatory execution constraints instead of default behavior
  • Verify skill/command templates: same rules-first logic — custom rules override default steps 4-8

Usage

Teams can now define custom execution strategies in config.yaml:

rules:
  apply:
    - "Use subagent for each task"
    - "Run tests after each task completion"
  verify:
    - "Run 4 parallel scan subagents"
    - "Classify issues by severity"

Files Changed (7)

  • src/commands/workflow/shared.ts — types
  • src/commands/workflow/instructions.ts — core logic
  • src/commands/workflow/index.ts — exports
  • src/cli/index.ts — CLI entry
  • src/core/project-config.ts — validation
  • src/core/templates/workflows/apply-change.ts — apply templates
  • src/core/templates/workflows/verify-change.ts — verify templates

Summary by CodeRabbit

  • New Features

    • Added a verify-instructions command that lists incomplete tasks, reports progress, and can output JSON.
    • Apply and verify outputs may include optional project context and mandatory rules.
  • CLI

    • CLI routes a dedicated verify phase command for verify operations.
  • Validation

    • Schemas now reject artifact IDs that conflict with reserved phase IDs (apply, verify).
  • Workflows

    • Updated workflow templates and guidance to enforce configured rules when present.
  • Tests

    • Updated template parity tests to match new template content.

Previously, config.yaml rules only worked for artifact generation phases
(proposal, specs, design, tasks). The apply and verify phases had no access
to project-level rules, making it impossible to customize execution strategies
(e.g., subagent dispatching, parallel verification scans).

Changes:
- Apply: generateApplyInstructions() now reads config.yaml context + rules.apply
- Verify: new generateVerifyInstructions() reads config.yaml context + rules.verify
- CLI: `openspec instructions verify` is now a valid command
- Validation: apply/verify are accepted as valid rule keys in config.yaml
- Templates: generated command/skill prompts instruct AI to follow rules as
  mandatory execution constraints

This enables teams to define custom execution strategies in config.yaml:
  rules:
    apply:
      - "Use subagent for each task"
    verify:
      - "Run 4 parallel scan subagents"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@k1r3zz k1r3zz requested a review from TabishB as a code owner March 28, 2026 15:00
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e8a84f01-23e7-4c50-bc88-0cabd893fa05

📥 Commits

Reviewing files that changed from the base of the PR and between d192f08 and afb54d9.

📒 Files selected for processing (1)
  • src/core/project-config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/project-config.ts

📝 Walkthrough

Walkthrough

Adds a new verify instructions workflow and CLI routing, extends apply/verify instruction payloads with optional context and rules from project config, updates templates to surface those fields and enforce rule-driven control flow, adds reserved phase IDs to config/schema validation, and updates related tests.

Changes

Cohort / File(s) Summary
CLI routing & workflow exports
src/cli/index.ts, src/commands/workflow/index.ts
Wire instructions verify to verifyInstructionsCommand; import and re-export VerifyInstructionsOptions and verifyInstructionsCommand.
Instructions & printing
src/commands/workflow/instructions.ts
Add generateVerifyInstructions, verifyInstructionsCommand, and printVerifyInstructionsText; extend apply-generation/printing to include optional context and rules.
Shared types & helpers
src/commands/workflow/shared.ts
Add VerifyInstructions interface; add optional context and rules to ApplyInstructions; add readPhaseConfig() helper and re-export RESERVED_PHASE_IDS.
Project config & validation
src/core/project-config.ts
Add exported RESERVED_PHASE_IDS (includes apply, verify); normalize raw.rules entries before validation and exempt reserved phase IDs from unknown-artifact warnings.
Schema validation
src/core/artifact-graph/schema.ts
Introduce validateNoReservedIds pre-validation to reject artifacts whose IDs match reserved phase IDs.
Workflow templates
src/core/templates/workflows/...
Update apply/verify templates to include context and rules, switch verify templates to call instructions verify, and add rule-driven control-flow guidance.
Tests
test/core/templates/skill-templates-parity.test.ts
Update expected SHA-256 hashes for modified templates/skill outputs.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI
    participant WF as "workflow/index"
    participant Instr as "workflow/instructions"
    participant Config as "project-config"
    participant Schema as "artifact/schema"
    participant Out as "stdout/json"

    CLI->>WF: parse args (instructions verify ...)
    WF->>Instr: invoke verifyInstructionsCommand(options)
    Instr->>Config: readPhaseConfig(projectRoot, 'verify')
    Instr->>Schema: load change & schema, build contextFiles, parse progress/tasks
    Instr->>Out: output JSON OR printVerifyInstructionsText
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through code with ears alight,
I taught a new "verify" to check the night,
Context tucked and rules in view,
Tasks march true, each step made new,
A little rabbit cheers the flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main objective of the changeset: injecting context and rules from config.yaml into the apply and verify workflow phases.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/commands/workflow/instructions.ts (1)

397-410: Consider a small helper for phase-config injection.

The apply and verify paths now duplicate the same readProjectConfig() / context / rules[phase] extraction. Centralizing that logic will make future config changes land in one place instead of two.

Also applies to: 566-579

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/workflow/instructions.ts` around lines 397 - 410, Extract the
duplicated project-config reading logic into a small helper function (e.g.,
getPhaseConfig or readPhaseConfig) that calls readProjectConfig(projectRoot) and
returns an object with trimmed context and phase-specific rules (e.g., {
context?: string, rules?: string[] }) based on a phase parameter like 'apply' or
'verify'; replace the duplicated blocks that populate configContext and
configRules with calls to this helper in both the apply and verify paths so all
config extraction (context trimming and rules['phase'] handling) is centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cli/index.ts`:
- Around line 448-456: The CLI currently intercepts artifactId values 'apply'
and 'verify' in the action handler (the branch that calls
applyInstructionsCommand, verifyInstructionsCommand, or instructionsCommand),
which makes project-defined artifacts with those names unreachable; fix by
adding a schema-level reservation for those names in ArtifactSchema (in
src/core/artifact-graph/types.ts) so the validator rejects artifact IDs 'apply'
and 'verify' (or alternatively refactor the CLI to expose apply/verify as
explicit subcommands/flags instead of positional matches), and update any
relevant tests to assert the reserved-name validation and that
instructionsCommand only runs for non-reserved artifact IDs.

In `@src/commands/workflow/instructions.ts`:
- Around line 542-547: The loop that builds contextFiles stores
path.join(changeDir, artifact.generates) verbatim even though artifact.generates
may be a glob; update the logic in the block that iterates schema.artifacts (the
contextFiles map, the artifactOutputExists check, and the artifact.generates
usage) to expand the glob into concrete file paths before assigning: use the
same glob resolution used by artifactOutputExists to collect all matching files
under changeDir and either (a) set contextFiles[artifact.id] to an array of
resolved absolute/relative file paths, or (b) if you must keep a string shape,
join the resolved paths into a delimiter-separated string—ensure consumers are
updated accordingly so multi-file artifacts like "specs/*.md" return the actual
matched files rather than the wildcard.

---

Nitpick comments:
In `@src/commands/workflow/instructions.ts`:
- Around line 397-410: Extract the duplicated project-config reading logic into
a small helper function (e.g., getPhaseConfig or readPhaseConfig) that calls
readProjectConfig(projectRoot) and returns an object with trimmed context and
phase-specific rules (e.g., { context?: string, rules?: string[] }) based on a
phase parameter like 'apply' or 'verify'; replace the duplicated blocks that
populate configContext and configRules with calls to this helper in both the
apply and verify paths so all config extraction (context trimming and
rules['phase'] handling) is centralized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 813da049-3643-4bb9-a7dc-9141e64380a3

📥 Commits

Reviewing files that changed from the base of the PR and between afdca0d and 6610014.

📒 Files selected for processing (7)
  • src/cli/index.ts
  • src/commands/workflow/index.ts
  • src/commands/workflow/instructions.ts
  • src/commands/workflow/shared.ts
  • src/core/project-config.ts
  • src/core/templates/workflows/apply-change.ts
  • src/core/templates/workflows/verify-change.ts

Comment on lines +542 to +547
// Build context files from all existing artifacts in schema
const contextFiles: Record<string, string> = {};
for (const artifact of schema.artifacts) {
if (artifactOutputExists(changeDir, artifact.generates)) {
contextFiles[artifact.id] = path.join(changeDir, artifact.generates);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resolve globbed artifact outputs before returning contextFiles.

artifactOutputExists() already treats generates as a glob, but this branch stores path.join(changeDir, artifact.generates) verbatim. For multi-file artifacts like specs/*.md, verify output will hand consumers a wildcard string instead of the concrete files to read, so required context can be skipped. Please expand matches here, or change the field shape to hold arrays of resolved paths.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/workflow/instructions.ts` around lines 542 - 547, The loop that
builds contextFiles stores path.join(changeDir, artifact.generates) verbatim
even though artifact.generates may be a glob; update the logic in the block that
iterates schema.artifacts (the contextFiles map, the artifactOutputExists check,
and the artifact.generates usage) to expand the glob into concrete file paths
before assigning: use the same glob resolution used by artifactOutputExists to
collect all matching files under changeDir and either (a) set
contextFiles[artifact.id] to an array of resolved absolute/relative file paths,
or (b) if you must keep a string shape, join the resolved paths into a
delimiter-separated string—ensure consumers are updated accordingly so
multi-file artifacts like "specs/*.md" return the actual matched files rather
than the wildcard.

- Extract readPhaseConfig() helper to centralize config reading logic
  for apply/verify phases (eliminates duplication)
- Add RESERVED_PHASE_IDS constant shared between schema validation
  and config validation
- Add validateNoReservedIds() in schema parser to reject artifact IDs
  'apply'/'verify' (prevents CLI routing conflicts)
- Update parity test hashes to match template changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/core/artifact-graph/schema.ts`:
- Around line 55-63: The reserved-ID check in validateNoReservedIds currently
does a case-sensitive lookup against RESERVED_PHASE_IDS using artifact.id;
change the check to normalize the artifact id to lowercase (e.g., use
artifact.id.toLowerCase() when calling RESERVED_PHASE_IDS.has) so IDs like
"Apply" or "VERIFY" are caught, and update the thrown SchemaValidationError
message to reference the original artifact.id while still checking the lowercase
form; key symbols: validateNoReservedIds, RESERVED_PHASE_IDS, Artifact.id,
SchemaValidationError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 293f4a4f-0325-4c14-98ce-980bac156b90

📥 Commits

Reviewing files that changed from the base of the PR and between 6610014 and 317838c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • src/commands/workflow/instructions.ts
  • src/commands/workflow/shared.ts
  • src/core/artifact-graph/schema.ts
  • src/core/project-config.ts
  • test/core/templates/skill-templates-parity.test.ts
✅ Files skipped from review due to trivial changes (1)
  • test/core/templates/skill-templates-parity.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/project-config.ts
  • src/commands/workflow/shared.ts

Comment on lines +55 to +63
function validateNoReservedIds(artifacts: Artifact[]): void {
for (const artifact of artifacts) {
if (RESERVED_PHASE_IDS.has(artifact.id)) {
throw new SchemaValidationError(
`Artifact ID '${artifact.id}' is reserved for phase commands. ` +
`Reserved IDs: ${Array.from(RESERVED_PHASE_IDS).join(', ')}`
);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the definition of RESERVED_PHASE_IDS
echo "=== RESERVED_PHASE_IDS definition ==="
rg -n -A5 'RESERVED_PHASE_IDS' src/core/project-config.ts

# Check how CLI routes the instructions command to see if it's case-sensitive
echo -e "\n=== CLI routing for instructions command ==="
rg -n -B2 -A5 'instructions' src/cli/index.ts | head -50

Repository: Fission-AI/OpenSpec

Length of output: 1595


🏁 Script executed:

#!/bin/bash
# Check if artifact IDs are normalized to lowercase anywhere
echo "=== Artifact ID normalization ==="
rg -n -i 'toLowerCase|normalize' src/core/ | grep -i artifact | head -20

# Check the full schema validation file to understand artifact ID handling
echo -e "\n=== Artifact ID schema validation ==="
rg -n 'artifact.*id|id.*artifact' src/core/artifact-graph/schema.ts -i | head -20

# Check if there's any case-insensitive comparison or normalization
echo -e "\n=== Case-insensitive comparisons ==="
rg -n 'toLowerCase\|toUpperCase' src/ | head -20

# Look at the verifyInstructionsCommand and instructionsCommand handlers
echo -e "\n=== Verify instructions command handler ==="
rg -n -A10 'verifyInstructionsCommand' src/cli/ | head -40

Repository: Fission-AI/OpenSpec

Length of output: 3150


🏁 Script executed:

#!/bin/bash
# Check the Artifact interface definition to see if case is preserved
echo "=== Artifact interface definition ==="
rg -n -B2 -A5 'interface Artifact\|type Artifact' src/ | head -40

# Check how instructionsCommand handler processes the artifactId
echo -e "\n=== Instructions command handler implementation ==="
rg -n -A15 'export.*instructionsCommand|async.*instructionsCommand' src/cli/ | head -50

# Check how the artifact ID is retrieved/validated in the commands
echo -e "\n=== Artifact retrieval in commands ==="
rg -n 'getArtifact|findArtifact' src/cli/ -A3 | head -40

# Check if there's any example schema with mixed-case artifact IDs
echo -e "\n=== Sample artifact definitions ==="
find . -name "*.yaml" -o -name "*.yml" | xargs grep -l "artifacts:" 2>/dev/null | head -5 | xargs head -100

Repository: Fission-AI/OpenSpec

Length of output: 5139


🏁 Script executed:

#!/bin/bash
# Search more broadly for Artifact type
echo "=== Artifact type/interface definition ==="
rg -n 'Artifact' src/core/artifact-graph/schema.ts | head -20

# Check the imports and types in schema.ts
echo -e "\n=== Schema.ts imports and types ==="
head -30 src/core/artifact-graph/schema.ts

# Search for the actual type definition in artifact-graph directory
echo -e "\n=== Types in artifact-graph ==="
fd '\.ts' src/core/artifact-graph/ | xargs grep -l 'interface Artifact\|type Artifact'

# Find the commands implementation file
echo -e "\n=== Find commands file ==="
find src -name '*command*' -type f

# Check the instructions command more deeply
echo -e "\n=== Instructions command in commands file ==="
rg -n 'instructionsCommand' src/ -l

Repository: Fission-AI/OpenSpec

Length of output: 1928


🏁 Script executed:

#!/bin/bash
# Read the Artifact type definition
echo "=== Artifact type definition in types.ts ==="
cat src/core/artifact-graph/types.ts

# Read the instructions command handler
echo -e "\n=== Instructions command handler ==="
cat src/commands/workflow/instructions.ts

# Check if there's any validation of artifact IDs in the resolver
echo -e "\n=== Artifact resolution/retrieval ==="
rg -n 'artifact\.id|artifactId' src/commands/workflow/ -A2 -B2 | head -80

Repository: Fission-AI/OpenSpec

Length of output: 28155


🏁 Script executed:

#!/bin/bash
# Find and examine the getArtifact implementation
echo "=== getArtifact implementation ==="
rg -n 'getArtifact' src/core/artifact-graph/ -A5 | head -50

# Check if artifact IDs are used in case-sensitive lookups
echo -e "\n=== Artifact ID lookups in graph ==="
rg -n 'class.*Graph|getArtifact|find.*artifact' src/core/artifact-graph/ -B2 -A8 | head -100

Repository: Fission-AI/OpenSpec

Length of output: 4805


Normalize artifact IDs to lowercase when validating reserved IDs.

The check uses exact string matching (RESERVED_PHASE_IDS.has(artifact.id)), but the schema allows artifact IDs with any casing. An artifact ID like Apply or VERIFY would bypass this validation since only lowercase apply and verify are checked. Normalize to lowercase during validation:

if (RESERVED_PHASE_IDS.has(artifact.id.toLowerCase())) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/artifact-graph/schema.ts` around lines 55 - 63, The reserved-ID
check in validateNoReservedIds currently does a case-sensitive lookup against
RESERVED_PHASE_IDS using artifact.id; change the check to normalize the artifact
id to lowercase (e.g., use artifact.id.toLowerCase() when calling
RESERVED_PHASE_IDS.has) so IDs like "Apply" or "VERIFY" are caught, and update
the thrown SchemaValidationError message to reference the original artifact.id
while still checking the lowercase form; key symbols: validateNoReservedIds,
RESERVED_PHASE_IDS, Artifact.id, SchemaValidationError.

validateNoReservedIds now uses toLowerCase() to catch 'Apply', 'VERIFY'
and other case variants, not just exact lowercase matches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@k1r3zz
Copy link
Copy Markdown
Author

k1r3zz commented Mar 28, 2026

Hi maintainers 👋 Just wanted to flag this PR for review when you get a chance.

Summary: This adds support for custom rules.apply and rules.verify in config.yaml, so teams can define their own execution strategies for the apply and verify phases (e.g., subagent dispatching, parallel verification).

All CodeRabbit feedback has been addressed across 3 commits, and all 1341 tests are passing

Also fixes a bug where the verify template was incorrectly calling openspec instructions apply instead of openspec instructions verify.

Happy to make any further changes if needed. Thanks!

YAML parsers interpret unquoted strings containing colons as key-value
mappings. For example, a rule like:
  - subagent_type: flutter-agent
becomes {subagent_type: "flutter-agent"} instead of a plain string.

This caused Zod's z.array(z.string()) validation to reject the entire
artifact's rules silently, making apply/verify phases ignore user-
defined rules from config.yaml.

Fix: before Zod validation, normalize array elements by converting
any objects back to "key: value" strings. Users no longer need to
quote rules containing colons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant