Skip to content

feat: cairn context command, cairn-dev skill, blueprint decision gate (#64, #68) - #92

Merged
George-RD merged 1 commit into
devfrom
05-11-feat_cairn_context_command_cairn-dev_skill_blueprint_decision_gate_64_68_
May 11, 2026
Merged

feat: cairn context command, cairn-dev skill, blueprint decision gate (#64, #68)#92
George-RD merged 1 commit into
devfrom
05-11-feat_cairn_context_command_cairn-dev_skill_blueprint_decision_gate_64_68_

Conversation

@George-RD

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings May 11, 2026 18:48
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added context command providing a comprehensive architecture overview including system metadata, node/edge counts, and findings summary.
    • Implemented automatic blueprint shape change detection that flags architectural modifications without corresponding decision documentation.
  • Documentation

    • Expanded Cairn development resources with comprehensive guides covering artefact schemas, blueprint syntax specifications, and finding code references.

Walkthrough

Adds blueprint shape change detection and gating with decision coverage. Persists node fingerprints across runs, detects structural modifications, and gates changes without decision artefact coverage via CA002 finding. Introduces CLI context command and query API handler to expose system metadata. Includes comprehensive documentation and skill guide.

Changes

Blueprint Shape Change Detection & Context

Layer / File(s) Summary
Error Code Registry
openspec/registries/error-codes.md
Registers CA002 error code: blueprint shape changed for node but no decision artefact covers it.
State Models & Persistence
src/scanner/state.rs
Introduces NodeFingerprint and BlueprintSnapshot types with schema versioning; write_blueprint_snapshot and read_blueprint_snapshot serialize/deserialize node fingerprints to/from .cairn/state/blueprint-snapshot.json.
Scanner Blueprint Detection
src/scanner/mod.rs
Adds blueprint_snapshot field to ScanResult; load_project computes current node fingerprints, loads prior snapshot, compares them, and gates CA002 findings when blueprint structure changes but no decision covers the affected nodes; scan persists the snapshot; helpers build snapshots from AST and emit findings.
Query API Integration
src/query_api/registry.rs, src/query_api/handlers.rs, src/query_api/mod.rs
Expands TOOL_REGISTRY to include "context"/"cairn_context"; count_findings helper aggregates error/warning/info severity counts; context_json handler returns system metadata (name, description, node/edge counts, artefact counts, finding summaries); execute_data accepts config and dispatches to context_json.
CLI Context Command
src/cli/mod.rs, src/cli/render.rs
CLI registers and dispatches new "context" command; scan_warning_count counts warning findings; render_context renders human-readable context summary with node/edge counts and finding totals.
Tests
tests/kernel.rs
Blueprint shape change gate: test fixtures create minimal blueprints; snapshot helpers write prior state; six test scenarios verify gate fires on new/removed/kind-changed modules, passes with decision coverage, ignores first scan, and ignores path-only changes.
Documentation & Skill
.claude/skills/cairn-dev/SKILL.md, .claude/skills/cairn-dev/references/*, CLAUDE.md
Comprehensive skill document with activation triggers, orientation commands, development loop, blueprint/artefact guides, finding codes, and hook semantics; reference documents for blueprint syntax, artefact YAML schemas, and finding codes; CLAUDE.md updated to activate cairn-dev skill and mandate /reforge//debate pre-submit review.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • cairn-framework/cairn#83: Modifies CLAUDE.md to add guidance for using the cairn context entry point; overlaps with this PR's doc-level changes.

Poem

🐰 A blueprint's shape now tracked with care,
Each snapshot held in state's drawer fair.
Decisions guide the changes made,
Gated safe, no change unpaid.
Context flows from CLI to cloud,
The Cairn way, documented loud! 🎯


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No description was provided by the author, making it impossible to assess whether it relates to the changeset. Add a pull request description that briefly explains the purpose and scope of these changes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main features added: a 'cairn context' command, 'cairn-dev' skill documentation, and a blueprint decision gate, with clear issue references.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 05-11-feat_cairn_context_command_cairn-dev_skill_blueprint_decision_gate_64_68_

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

Copy link
Copy Markdown
Collaborator Author

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/cli/mod.rs (1)

329-329: ⚡ Quick win

Add context to the core command human/json test matrix

context is now part of shared JSON routing and command dispatch, but it is not included in test_cli_core_commands_support_human_and_json_output. Adding it will catch regressions in both render paths.

Suggested test addition
         let cases = [
             ("get", vec!["get", "app.api"]),
+            ("context", vec!["context"]),
             (
                 "neighbourhood",
                 vec!["neighbourhood", "app.api", "--include-todos"],
             ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/mod.rs` at line 329, The test
test_cli_core_commands_support_human_and_json_output is missing the "context"
command in its core command matrix; update the test's list/array of commands
(the sequence currently containing items like "help", "version", etc.) to
include "context" so the test iterates over and validates both human and JSON
output for that command as well; locate the command vector/iterator in
src/cli/mod.rs within the test function and add the string "context" to that
collection.
src/query_api/handlers.rs (1)

229-243: 💤 Low value

Consider consolidating the System node lookup.

The code iterates over scan_result.graph.nodes.values() twice (lines 229-234 and 238-243) to find the System node. A single lookup would be slightly more efficient.

♻️ Proposed refactor
+    let system_node = scan_result
+        .graph
+        .nodes
+        .values()
+        .find(|n| n.kind == crate::blueprint::ast::NodeKind::System);
+
-    let system_name = scan_result
-        .graph
-        .nodes
-        .values()
-        .find(|n| n.kind == crate::blueprint::ast::NodeKind::System)
-        .map_or("unknown", |n| n.name.as_str());
+    let system_name = system_node.map_or("unknown", |n| n.name.as_str());

     let edge_count: usize = scan_result.graph.outbound.values().map(Vec::len).sum();

-    let system_description = scan_result
-        .graph
-        .nodes
-        .values()
-        .find(|n| n.kind == crate::blueprint::ast::NodeKind::System)
-        .map_or("", |n| n.description.as_str());
+    let system_description = system_node.map_or("", |n| n.description.as_str());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/query_api/handlers.rs` around lines 229 - 243, The code currently calls
scan_result.graph.nodes.values().find(...) twice to get the System node; instead
perform a single lookup by assigning the result to a local Option (e.g., let
system_node = scan_result.graph.nodes.values().find(|n| n.kind ==
crate::blueprint::ast::NodeKind::System)); then derive system_name and
system_description from that Option (using map_or("unknown", |n|
n.name.as_str()) and map_or("", |n| n.description.as_str()) respectively). Keep
edge_count as-is and ensure you reference the same system_node variable wherever
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/cairn-dev/references/blueprint-syntax.md:
- Around line 9-139: The Markdown file contains untyped fenced code blocks
(triple backticks) that trigger MD040; update each fence to include an
appropriate language identifier (e.g., ```text for plain examples, ```blueprint
for the blueprint syntax blocks, or ```yaml for YAML-like sections). Locate the
untyped fences in the sections such as the top-level example (the initial
grammar block starting with "# Comments start with #"), the Node
declarations/Module examples (the blocks showing System/Container/Module/Actor),
and the Complete example, and change each opening ``` to include the proper
identifier while leaving the block contents unchanged.

In @.claude/skills/cairn-dev/references/finding-codes.md:
- Around line 72-85: Update the "## Registry format" section to match the actual
registry file by replacing the table example with the current bullet/inline
format (e.g., a fenced code block showing "CXNNN -- one-line description --
phase/issue") and by updating the category list to include the real prefixes
used in openspec/registries/error-codes.md (CP, CK, CA, CC, CH, CE, CT, CM, CS,
CB, CD, CO, etc.) instead of the old CS/CI/... set; also add a language tag to
the fenced code block (e.g., ```md) so markdownlint is satisfied and ensure the
section references the registry filename "openspec/registries/error-codes.md"
for clarity.

In @.claude/skills/cairn-dev/SKILL.md:
- Around line 231-235: The fenced code block in the error correction template
inside .claude/skills/cairn-dev/SKILL.md is missing a language specifier; update
the fenced block around the line that currently contains "The cairn-dev skill
told me to [X], but cairn actually [Y]." to include a language identifier (e.g.,
add "text" after the opening ```), so the block reads ```text ... ```, ensuring
the code fence is annotated for proper rendering.
- Around line 82-101: The fenced code block showing the blueprint starting with
the line "System <TypeLabel> \"<description>\" id \"<dotted-id>\" [`@tag`...] {"
should include a language specifier after the opening triple backticks; update
the opening fence to use a common identifier such as ```text or a custom one
like ```blueprint so tools can render the DSL consistently (e.g., change ``` to
```text or ```blueprint at the top of the block that begins with "System
<TypeLabel> ...").

In `@src/cli/render.rs`:
- Around line 436-450: The "Modules:" heading is misleading because the loop
over scan_result.graph.nodes.values() renders all node kinds; either rename the
header string "Modules:\n" to "Nodes:\n" or filter the loop to only render
module nodes by checking node.kind == NodeKind::Module before writing; update
the header or add the filter in the loop that writes each node (the
write/writeln call using node.id, node.name, node.state, node.paths) so the
label matches the rendered data.

---

Nitpick comments:
In `@src/cli/mod.rs`:
- Line 329: The test test_cli_core_commands_support_human_and_json_output is
missing the "context" command in its core command matrix; update the test's
list/array of commands (the sequence currently containing items like "help",
"version", etc.) to include "context" so the test iterates over and validates
both human and JSON output for that command as well; locate the command
vector/iterator in src/cli/mod.rs within the test function and add the string
"context" to that collection.

In `@src/query_api/handlers.rs`:
- Around line 229-243: The code currently calls
scan_result.graph.nodes.values().find(...) twice to get the System node; instead
perform a single lookup by assigning the result to a local Option (e.g., let
system_node = scan_result.graph.nodes.values().find(|n| n.kind ==
crate::blueprint::ast::NodeKind::System)); then derive system_name and
system_description from that Option (using map_or("unknown", |n|
n.name.as_str()) and map_or("", |n| n.description.as_str()) respectively). Keep
edge_count as-is and ensure you reference the same system_node variable wherever
needed.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a6fefb53-8a9c-4bff-97b7-0314e6727bb1

📥 Commits

Reviewing files that changed from the base of the PR and between 10a7cbe and d95b01f.

⛔ Files ignored due to path filters (1)
  • tests/snapshots/wire_format_snapshots__api_meta.snap is excluded by !**/*.snap
📒 Files selected for processing (14)
  • .claude/skills/cairn-dev/SKILL.md
  • .claude/skills/cairn-dev/references/artefact-schemas.md
  • .claude/skills/cairn-dev/references/blueprint-syntax.md
  • .claude/skills/cairn-dev/references/finding-codes.md
  • CLAUDE.md
  • openspec/registries/error-codes.md
  • src/cli/mod.rs
  • src/cli/render.rs
  • src/query_api/handlers.rs
  • src/query_api/mod.rs
  • src/query_api/registry.rs
  • src/scanner/mod.rs
  • src/scanner/state.rs
  • tests/kernel.rs

Comment on lines +9 to +139
```
# Comments start with #

System <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {
<child nodes>
}

# Edges
from.id -> to.id "relationship label"
```

## Node declarations

Four node kinds, each nestable inside System or Container:

### System (top-level only)

```
System <TypeLabel> "<description>" id "<root-id>" [@tag...] {
# Contains Containers, Modules, or Actors
}
```

The system node is the root. Its `id` is the prefix for all child IDs.

### Container (grouping)

```
Container <TypeLabel> "<description>" id "<parent-id>.<name>" [@tag...] {
# Contains Modules, other Containers, or Actors
}
```

Containers have no code targets. They group related modules.

### Module (leaf with code)

```
Module <TypeLabel> "<description>" id "<parent-id>.<name>" [@tag...] {
path "<relative-path>" # required, can repeat for multiple paths
contract "<path>" # optional
decisions "<directory>" # optional
todos "<directory>" # optional
research "<directory>" # optional
sources "<directory>" # optional
reviews "<directory>" # optional
}
```

Modules are the primary leaf nodes. Each `path` line maps to files or directories that the reconciler scans.

### Actor (external entity)

```
Actor <TypeLabel> "<description>" id "<parent-id>.<name>" [@tag...] {
# Usually no fields; represents external systems or users
}
```

## Edge declarations

Edges are declared outside any block, at file scope:

```
from.node.id -> to.node.id "description of the relationship"
```

Both node IDs must exist in the node declarations above. Edges form a directed graph used for dependency analysis, cycle detection, and topological ordering.

## ID conventions

- IDs use dotted notation: `system.container.module`
- The system ID is the root prefix
- Child IDs extend the parent: if the system is `myapp`, a kernel container is `myapp.kernel`, and a parser module inside it is `myapp.kernel.parser`
- IDs are case-sensitive

## Tags

Tags are prefixed with `@` and are informational annotations:

```
Module Parser "Parses input files" id "myapp.parser" @core @v2 {
path "./src/parser"
}
```

Tags don't affect behavior. They're useful for filtering and documentation.

## Path declarations

- Paths are relative to the repository root
- A path can point to a file (`./src/main.rs`) or a directory (`./src/parser`)
- Directory paths claim all files recursively under that directory
- Multiple `path` lines are allowed per module
- Files not claimed by any module's path are reported as `CAIRN_ORPHANED_FILE`
- Paths referencing nonexistent files are reported as `CAIRN_GHOST_FILE`

## Complete example

```
System MyApp "A web application with API and frontend" id "myapp" @webapp {

Container Backend "Server-side services" id "myapp.backend" @server {

Module API "REST API endpoints and routing" id "myapp.backend.api" @http {
path "./src/api"
contract "meta/contracts/api.md"
decisions "meta/decisions/api"
todos "meta/todos/api"
}

Module Database "Data access layer and migrations" id "myapp.backend.db" {
path "./src/db"
path "./migrations"
}
}

Module Config "Shared configuration and environment" id "myapp.config" {
path "./src/config.rs"
}

Actor ExternalPayment "Third-party payment processor" id "myapp.payment" @external {
}
}

# Dependencies
myapp.backend.api -> myapp.backend.db "Queries and persists data"
myapp.backend.api -> myapp.config "Reads configuration"
myapp.backend.api -> myapp.payment "Processes payments"
myapp.backend.db -> myapp.config "Reads connection settings"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks

The new code fences are untyped, which triggers MD040. Please annotate each fence (text, blueprint, or yaml as appropriate) for lint-clean docs.

Example fix pattern
-```
+```text
 # Comments start with #
 ...
-```
+```

-```
+```text
 System <TypeLabel> "<description>" id "<root-id>" [`@tag`...] {
 ...
-```
+```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 26-26: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 36-36: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 46-46: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 62-62: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 89-89: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 108-108: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/cairn-dev/references/blueprint-syntax.md around lines 9 -
139, The Markdown file contains untyped fenced code blocks (triple backticks)
that trigger MD040; update each fence to include an appropriate language
identifier (e.g., ```text for plain examples, ```blueprint for the blueprint
syntax blocks, or ```yaml for YAML-like sections). Locate the untyped fences in
the sections such as the top-level example (the initial grammar block starting
with "# Comments start with #"), the Node declarations/Module examples (the
blocks showing System/Container/Module/Actor), and the Complete example, and
change each opening ``` to include the proper identifier while leaving the block
contents unchanged.

Comment on lines +72 to +85
## Registry format

Error codes are registered in `openspec/registries/error-codes.md` with the format:

```
| CXNNN | CAIRN_FULL_CODE_NAME | severity | description | issue # |
```

Categories:
- `CS` - Scanner/structural findings
- `CI` - Interface findings
- `CA` - Artefact findings
- `CH` - Hook findings
- `CC` - CLI findings

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Registry guidance here is out of sync with the actual registry file

This section says the registry uses a table format and CS/CI/... prefixes, but openspec/registries/error-codes.md currently uses bullet entries and categories like CP/CK/CA/.... Also, the fenced block at Line 76 should declare a language to satisfy markdownlint.

Proposed doc fix
-Error codes are registered in `openspec/registries/error-codes.md` with the format:
+Error codes are registered in `openspec/registries/error-codes.md` using bullet entries:

-```
-| CXNNN | CAIRN_FULL_CODE_NAME | severity | description | issue # |
+```md
+- CXNNN -- one-line description -- phase/issue

Categories:
-- CS - Scanner/structural findings
-- CI - Interface findings
-- CA - Artefact findings
-- CH - Hook findings
-- CC - CLI findings
+- CP - Parser
+- CK - Kernel/Map
+- CA - Artefacts
+- CC - Changes
+- CH - Hooks
+- CE - Edges
+- CT - Targets
+- CM - MCP
+- CS - Summariser
+- CB - Brownfield
+- CD - Distribution
+- CO - CLI output / I/O

</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
## Registry format

Error codes are registered in `openspec/registries/error-codes.md` using bullet entries:

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 76-76: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/cairn-dev/references/finding-codes.md around lines 72 - 85,
Update the "## Registry format" section to match the actual registry file by
replacing the table example with the current bullet/inline format (e.g., a
fenced code block showing "CXNNN -- one-line description -- phase/issue") and by
updating the category list to include the real prefixes used in
openspec/registries/error-codes.md (CP, CK, CA, CC, CH, CE, CT, CM, CS, CB, CD,
CO, etc.) instead of the old CS/CI/... set; also add a language tag to the
fenced code block (e.g., ```md) so markdownlint is satisfied and ensure the
section references the registry filename "openspec/registries/error-codes.md"
for clarity.

Comment on lines +82 to +101
```
System <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {

Container <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {

Module <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {
path "<relative-path>" # multiple path lines allowed
contract "<path>"
decisions "<dir>"
todos "<dir>"
research "<dir>"
sources "<dir>"
reviews "<dir>"
}
}
}

# Edges (outside blocks)
from.id -> to.id "relationship label"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language specifier to fenced code block.

The blueprint syntax example lacks a language identifier. While this is a custom DSL, using a common identifier like text or a custom one like blueprint helps tools render it consistently.

📝 Proposed fix
-```
+```text
 System <TypeLabel> "<description>" id "<dotted-id>" [`@tag`...] {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
System <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {
Container <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {
Module <TypeLabel> "<description>" id "<dotted-id>" [@tag...] {
path "<relative-path>" # multiple path lines allowed
contract "<path>"
decisions "<dir>"
todos "<dir>"
research "<dir>"
sources "<dir>"
reviews "<dir>"
}
}
}
# Edges (outside blocks)
from.id -> to.id "relationship label"
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 82-82: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/cairn-dev/SKILL.md around lines 82 - 101, The fenced code
block showing the blueprint starting with the line "System <TypeLabel>
\"<description>\" id \"<dotted-id>\" [`@tag`...] {" should include a language
specifier after the opening triple backticks; update the opening fence to use a
common identifier such as ```text or a custom one like ```blueprint so tools can
render the DSL consistently (e.g., change ``` to ```text or ```blueprint at the
top of the block that begins with "System <TypeLabel> ...").

Comment on lines +231 to +235
```
The cairn-dev skill told me to [X], but cairn actually [Y].
Please update the skill at .claude/skills/cairn-dev/SKILL.md
to correct [specific section].
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language specifier to fenced code block.

The error correction template lacks a language identifier.

📝 Proposed fix
-```
+```text
 The cairn-dev skill told me to [X], but cairn actually [Y].
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
The cairn-dev skill told me to [X], but cairn actually [Y].
Please update the skill at .claude/skills/cairn-dev/SKILL.md
to correct [specific section].
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 231-231: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/cairn-dev/SKILL.md around lines 231 - 235, The fenced code
block in the error correction template inside .claude/skills/cairn-dev/SKILL.md
is missing a language specifier; update the fenced block around the line that
currently contains "The cairn-dev skill told me to [X], but cairn actually [Y]."
to include a language identifier (e.g., add "text" after the opening ```), so
the block reads ```text ... ```, ensuring the code fence is annotated for proper
rendering.

Comment thread src/cli/render.rs
Comment on lines +436 to +450
"{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nModules:\n",
system_name,
scan_result.graph.nodes.len(),
edge_count,
system_desc,
errors,
warnings,
);

for node in scan_result.graph.nodes.values() {
let paths = node.paths.join(", ");
writeln!(
out,
" {} ({}) [{:?}] {}",
node.id, node.name, node.state, paths

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Modules: heading does not match the data being rendered

The section currently lists all node kinds, not just modules. Either rename the header to Nodes: or filter the loop to module nodes only.

Minimal wording fix
-        "{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nModules:\n",
+        "{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nNodes:\n",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nModules:\n",
system_name,
scan_result.graph.nodes.len(),
edge_count,
system_desc,
errors,
warnings,
);
for node in scan_result.graph.nodes.values() {
let paths = node.paths.join(", ");
writeln!(
out,
" {} ({}) [{:?}] {}",
node.id, node.name, node.state, paths
"{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nNodes:\n",
system_name,
scan_result.graph.nodes.len(),
edge_count,
system_desc,
errors,
warnings,
);
for node in scan_result.graph.nodes.values() {
let paths = node.paths.join(", ");
writeln!(
out,
" {} ({}) [{:?}] {}",
node.id, node.name, node.state, paths
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/render.rs` around lines 436 - 450, The "Modules:" heading is
misleading because the loop over scan_result.graph.nodes.values() renders all
node kinds; either rename the header string "Modules:\n" to "Nodes:\n" or filter
the loop to only render module nodes by checking node.kind == NodeKind::Module
before writing; update the header or add the filter in the loop that writes each
node (the write/writeln call using node.id, node.name, node.state, node.paths)
so the label matches the rendered data.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new cairn context command (CLI + query API tool) and introduces a “blueprint shape change” decision gate by persisting a blueprint snapshot to .cairn/state and emitting a blocking finding when structural changes aren’t covered by an active decision.

Changes:

  • Add blueprint snapshot read/write state and enforce CAIRN_BLUEPRINT_CHANGE_NO_DECISION on node add/remove/reparent/kind-change (gated by presence of prior snapshot + at least one decision).
  • Add context to the query API tool registry and CLI dispatch, including JSON + human renderers.
  • Update tests/snapshots and documentation/registries (error code allocation + new cairn-dev skill docs).

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/snapshots/wire_format_snapshots__api_meta.snap Snapshot updates for API meta/tool registry (includes new context tool).
tests/kernel.rs Adds test coverage for the new blueprint shape-change decision gate.
src/scanner/state.rs Introduces blueprint snapshot schema + read/write helpers.
src/scanner/mod.rs Computes blueprint snapshot, checks for uncovered shape changes, writes snapshot on scan.
src/query_api/registry.rs Registers the new context tool.
src/query_api/mod.rs Threads loaded config into tool execution and dispatches context.
src/query_api/handlers.rs Implements context_json response payload.
src/cli/render.rs Adds human-readable cairn context output.
src/cli/mod.rs Wires context into CLI dispatch and shared JSON routing.
openspec/registries/error-codes.md Allocates CA002 for CAIRN_BLUEPRINT_CHANGE_NO_DECISION.
CLAUDE.md Adds workflow guidance and references the new cairn-dev skill.
.claude/skills/cairn-dev/SKILL.md New skill documentation for cairn development workflows.
.claude/skills/cairn-dev/references/finding-codes.md New reference doc including CA002 semantics.
.claude/skills/cairn-dev/references/blueprint-syntax.md New blueprint syntax reference doc.
.claude/skills/cairn-dev/references/artefact-schemas.md New artefact schema reference doc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/scanner/state.rs
Comment on lines +116 to +119
if !path.exists() {
return Ok(BlueprintSnapshot::default());
}
let content = fs::read_to_string(path)?;
Comment thread src/cli/render.rs
Comment on lines +435 to +446
let mut out = format!(
"{} ({} nodes, {} edges)\n{}\n\nFindings: {} errors, {} warnings\n\nModules:\n",
system_name,
scan_result.graph.nodes.len(),
edge_count,
system_desc,
errors,
warnings,
);

for node in scan_result.graph.nodes.values() {
let paths = node.paths.join(", ");
@George-RD
George-RD merged commit 24ff392 into dev May 11, 2026
16 checks passed
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.

2 participants