diff --git a/.antigravity-plugin/manifest.json b/.antigravity-plugin/manifest.json index 1b5d304d..d0c01bdf 100644 --- a/.antigravity-plugin/manifest.json +++ b/.antigravity-plugin/manifest.json @@ -1,7 +1,7 @@ { "name": "canon", "displayName": "Canon Assistant Support for Antigravity", - "version": "0.64.0", + "version": "0.65.0", "description": "Canon provides semantic governance for AI-assisted engineering work: structured packets, governed evidence, approvals, lineage, project memory, and reusable delivery knowledge.", "author": { "name": "Apply The", diff --git a/.claude-plugin/manifest.json b/.claude-plugin/manifest.json index d7b99b84..bc494499 100644 --- a/.claude-plugin/manifest.json +++ b/.claude-plugin/manifest.json @@ -1,7 +1,7 @@ { "name": "canon", "displayName": "Canon Assistant Support for Claude Code", - "version": "0.64.0", + "version": "0.65.0", "description": "Canon provides semantic governance for AI-assisted engineering work: structured packets, governed evidence, approvals, lineage, project memory, and reusable delivery knowledge.", "author": { "name": "Apply The", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index f608e9aa..97ebbcd6 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "canon", "displayName": "Canon Assistant Support for Codex", - "version": "0.64.0", + "version": "0.65.0", "description": "Canon provides semantic governance for AI-assisted engineering work: structured packets, governed evidence, approvals, lineage, project memory, and reusable delivery knowledge.", "author": { "name": "Apply The", diff --git a/.cursor-plugin/manifest.json b/.cursor-plugin/manifest.json index 7a1b8ae9..1371624d 100644 --- a/.cursor-plugin/manifest.json +++ b/.cursor-plugin/manifest.json @@ -1,7 +1,7 @@ { "name": "canon", "displayName": "Canon Assistant Support for Cursor", - "version": "0.64.0", + "version": "0.65.0", "description": "Canon provides semantic governance for AI-assisted engineering work: structured packets, governed evidence, approvals, lineage, project memory, and reusable delivery knowledge.", "author": { "name": "Apply The", diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index f5078cd9..fcf1690e 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -29,7 +29,7 @@ jobs: - uses: taiki-e/install-action@v2 with: tool: cargo-llvm-cov - - run: cargo llvm-cov --workspace --all-features --lcov --output-path lcov.info + - run: bash scripts/coverage.sh - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@v6.0.0 env: diff --git a/.gitignore b/.gitignore index ef7330e7..b41ad4e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Ignore the homebrew-canon directory, which may contain generated files or dependencies related to the project. +/homebrew-canon + # Generated by Cargo # will have compiled files and executables debug diff --git a/.specify/feature.json b/.specify/feature.json index 13eb2fbf..743e5796 100644 --- a/.specify/feature.json +++ b/.specify/feature.json @@ -1,3 +1,3 @@ { - "feature_directory": "specs/065-reasoning-posture-v2" + "feature_directory": "specs/067-systematic-debugging" } \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index ea851c64..0ca3ae60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Canon Development Guidelines -Auto-generated from all feature plans. Last updated: 2026-06-01 +Auto-generated from all feature plans. Last updated: 2026-06-03 ## Governing Constitution @@ -13,6 +13,7 @@ Auto-generated from all feature plans. Last updated: 2026-06-01 ## Active Technologies - Rust 1.96.0, Edition 2024; Markdown planning artifacts + workspace crates `canon-cli` and `canon-engine`; existing `clap`, `serde`, `serde_json`, `serde_yaml`, and `tracing`; planned `ratatui` plus `crossterm` for the full-screen init UI; existing `assert_cmd` and `tempfile` for CLI validation (063-interactive-init-ui) - `.canon/` runtime scaffolding in the target workspace; planning and contract artifacts under `specs/063-interactive-init-ui/` (063-interactive-init-ui) +- Rust 1.96.0, Edition 2024 + clap, serde, serde_json, serde_yaml, tracing, thiserror (067-systematic-debugging) - Rust 1.96.0, Edition 2024 - `clap`, `serde`, `serde_json`, `serde_yaml`, `toml` @@ -74,9 +75,9 @@ Before 1.0.0, breaking changes MAY occur in minor versions. - `specs/001-canon-spec/decision-log.md` ## Recent Changes +- 067-systematic-debugging: Added Rust 1.96.0, Edition 2024 + clap, serde, serde_json, serde_yaml, tracing, thiserror - 063-interactive-init-ui: Added Rust 1.96.0, Edition 2024; Markdown planning artifacts + workspace crates `canon-cli` and `canon-engine`; existing `clap`, `serde`, `serde_json`, `serde_yaml`, and `tracing`; planned `ratatui` plus `crossterm` for the full-screen init UI; existing `assert_cmd` and `tempfile` for CLI validation - 062-clarify-run-refinement: Added Rust 1.96.0, Edition 2024; Markdown documentation and templates; existing Spec Kit shell helpers + workspace crates `canon-engine`, `canon-cli`, `canon-adapters`; existing `clap`, `serde`, `serde_json`, `serde_yaml`, `toml`, `thiserror`, `tracing`, `uuid`, and `time`; repo-local methods, templates, and skill-source documents -- 061-skill-runtime-contracts: Added Bash 5.x (macOS/Linux), PowerShell 7.x (cross-platform) + `jq` (JSON validation), `canon` CLI (version ## Codex Skills Frontend diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c1aa496..1737e2e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,10 +15,19 @@ The repository history contains no release bumps for `0.10.0`, `0.13.0`, `0.16.0`, or `0.17.0`, so adjacent feature slices are rolled into the next recorded workspace version. -## [Upcoming] +## [0.65.0] - 2026-06-03 -Planning notes for the next release line are not fixed yet. This section will -be updated once new work is scoped. +Delivered specs: + +- `specs/067-systematic-debugging/` + +Highlights: + +- Added a first-class `debugging` mode for systematic defect resolution, emphasizing root-cause isolation and red-to-green test transitions. +- Enforced required artifacts for the `debugging` mode: `01-context`, `02-reproduction`, `03-root-cause`, `04-fix-decision`, and `05-verification`. +- Integrated `debugging` mode into the governance surface and CLI contracts as a recommendation-only, green-zone workflow. +- Updated docs, README, and CLI references to include `debugging`. +- Bumped workspace version to `0.65.0`. ## [0.64.0] - 2026-06-02 diff --git a/Cargo.lock b/Cargo.lock index f844fd7c..85ef75f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,7 +128,7 @@ checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "canon-adapters" -version = "0.64.0" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -140,7 +140,7 @@ dependencies = [ [[package]] name = "canon-cli" -version = "0.64.0" +version = "0.65.0" dependencies = [ "canon-engine", "clap", @@ -161,7 +161,7 @@ dependencies = [ [[package]] name = "canon-engine" -version = "0.64.0" +version = "0.65.0" dependencies = [ "canon-adapters", "serde", @@ -180,7 +180,7 @@ dependencies = [ [[package]] name = "canon-workspace" -version = "0.64.0" +version = "0.65.0" dependencies = [ "assert_cmd", "canon-adapters", diff --git a/Cargo.toml b/Cargo.toml index 2b9db21a..f6465df9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ resolver = "2" edition = "2024" license = "MIT" rust-version = "1.96.0" -version = "0.64.0" +version = "0.65.0" [workspace.dependencies] assert_cmd = "2" diff --git a/README.md b/README.md index 158f61af..be9d833d 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ These are the commands you'll actually use every day: | Command | What it does | |---|---| -| `canon run` | Start a new governed session with explicit boundaries. | +| `canon run` | Start a new governed session with explicit boundaries. Available modes include `requirements`, `architecture`, `debugging`, `change`, `incident`, and more. | | `canon status` | See exactly what the agent is doing right now. | | `canon inspect` | Review generated evidence and artifacts. | | `canon approve` | Unblock a session that hit a governance gate. | diff --git a/assistant/plugin-metadata.json b/assistant/plugin-metadata.json index d66a51bc..a94389b5 100644 --- a/assistant/plugin-metadata.json +++ b/assistant/plugin-metadata.json @@ -1,7 +1,7 @@ { "name": "canon", "displayName": "Canon Assistant Support", - "version": "0.64.0", + "version": "0.65.0", "description": "Governed packet runtime for AI-assisted engineering work", "author": { "name": "Apply The", diff --git a/crates/canon-engine/src/artifacts/contract.rs b/crates/canon-engine/src/artifacts/contract.rs index 45d87d91..26bde5d7 100644 --- a/crates/canon-engine/src/artifacts/contract.rs +++ b/crates/canon-engine/src/artifacts/contract.rs @@ -57,6 +57,7 @@ pub fn contract_for_mode(mode: Mode) -> ArtifactContract { Mode::Migration => operations::migration(), Mode::DomainLanguage => domain::domain_language(), Mode::DomainModel => domain::domain_model(), + Mode::Debugging => delivery::debugging(), }; build_contract(files) diff --git a/crates/canon-engine/src/artifacts/contract/delivery.rs b/crates/canon-engine/src/artifacts/contract/delivery.rs index ad2eaeb5..f402661d 100644 --- a/crates/canon-engine/src/artifacts/contract/delivery.rs +++ b/crates/canon-engine/src/artifacts/contract/delivery.rs @@ -41,6 +41,13 @@ const REGRESSION_EVIDENCE_MD: &str = "regression-evidence.md"; const CONTRACT_DRIFT_CHECK_MD: &str = "contract-drift-check.md"; const NO_FEATURE_ADDITION_MD: &str = "no-feature-addition.md"; +// Debugging +const CONTEXT_MAP_MD: &str = "context-map.md"; +const REPRODUCTION_HARNESS_MD: &str = "reproduction-harness.md"; +const ROOT_CAUSE_ISOLATION_MD: &str = "root-cause-isolation.md"; +const FIX_APPLICATION_MD: &str = "fix-application.md"; +const VERIFICATION_SUMMARY_MD: &str = "verification-summary.md"; + // ── Mode contracts ──────────────────────────────────────────────────────────── /// Returns the artifact requirements for the [`Backlog`](crate::domain::mode::Mode::Backlog) mode. @@ -223,6 +230,37 @@ pub(super) fn refactor() -> Vec { ] } +/// Returns the artifact requirements for the [`Debugging`](crate::domain::mode::Mode::Debugging) mode. +pub(super) fn debugging() -> Vec { + vec![ + requirement( + CONTEXT_MAP_MD, + &[SUMMARY, "Context Map", "Defect Description", "Stakeholder Impact"], + &[GateKind::Exploration, GateKind::Risk], + ), + requirement( + REPRODUCTION_HARNESS_MD, + &[SUMMARY, "Reproduction Harness", "Red State Verification"], + &[GateKind::Reproduction, GateKind::TestDrivenDevelopment], + ), + requirement( + ROOT_CAUSE_ISOLATION_MD, + &[SUMMARY, "Root Cause Isolation", "Fault Chain", "Isolation Proof"], + &[GateKind::RootCause, GateKind::Architecture], + ), + requirement( + FIX_APPLICATION_MD, + &[SUMMARY, "Fix Application", "Bounded Changes", "Invariant Preservation"], + &[GateKind::RootCause, GateKind::ReleaseReadiness], + ), + requirement( + VERIFICATION_SUMMARY_MD, + &[SUMMARY, "Verification Summary", "Green State", "No Regression Evidence"], + &[GateKind::ReleaseReadiness, GateKind::TestDrivenDevelopment], + ), + ] +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/canon-engine/src/artifacts/markdown.rs b/crates/canon-engine/src/artifacts/markdown.rs index cb0375ff..bb985258 100644 --- a/crates/canon-engine/src/artifacts/markdown.rs +++ b/crates/canon-engine/src/artifacts/markdown.rs @@ -209,8 +209,9 @@ pub use authoring::{ render_requirements_artifact_from_evidence, render_system_shaping_artifact, }; pub use delivery::{ - render_backlog_artifact, render_change_artifact, render_implementation_artifact, - render_incident_artifact, render_migration_artifact, render_refactor_artifact, + render_backlog_artifact, render_change_artifact, render_debugging_artifact, + render_implementation_artifact, render_incident_artifact, render_migration_artifact, + render_refactor_artifact, }; pub use domain::{render_domain_language_artifact, render_domain_model_artifact}; pub use governance::{ diff --git a/crates/canon-engine/src/artifacts/markdown/delivery.rs b/crates/canon-engine/src/artifacts/markdown/delivery.rs index 1289865d..44225962 100644 --- a/crates/canon-engine/src/artifacts/markdown/delivery.rs +++ b/crates/canon-engine/src/artifacts/markdown/delivery.rs @@ -10,6 +10,63 @@ use super::{AuthoredSectionSpec, render_markdown}; mod backlog; +const MARKER_OWNER: &str = "owner"; +const MARKER_RISK: &str = "risk level"; +const MARKER_ZONE: &str = "zone"; +const DEFAULT_RISK: &str = "unspecified-risk"; +const DEFAULT_ZONE: &str = "unspecified-zone"; +const DEFAULT_OWNER: &str = "bounded-system-maintainer"; + +macro_rules! artifact_match { + ($file_name:expr, $default_summary:expr, $brief_summary:expr, { + $( + $slug:expr => $title:expr, [ $($heading:expr),* $(,)? ] $(=> $custom_summary:expr)? + ),* $(,)? + }) => { + match $file_name { + $( + $slug => { + let mut _summary_to_use = $default_summary; + $( + _summary_to_use = $custom_summary; + )? + render_authored_artifact( + $title, + _summary_to_use, + $brief_summary, + &[ + $( + AuthoredSectionSpec { canonical_heading: $heading, aliases: &[] }, + )* + ] + ) + } + )* + other => render_markdown(other, $brief_summary), + } + }; +} + +fn extract_required_section( + brief_summary: &str, + normalized: &str, + heading: &str, + fallback: &str, +) -> String { + let marker = heading.to_lowercase(); + extract_authored_section_or_marker(brief_summary, normalized, heading, &[], &[&marker]) + .unwrap_or_else(|| fallback.to_string()) +} + +fn extract_optional_section( + brief_summary: &str, + normalized: &str, + heading: &str, +) -> Option { + let marker = heading.to_lowercase(); + extract_authored_section_or_marker(brief_summary, normalized, heading, &[], &[&marker]) +} + pub fn render_change_artifact(file_name: &str, brief_summary: &str, default_owner: &str) -> String { let file_name = artifact_slug(file_name); let normalized = brief_summary.to_lowercase(); @@ -22,12 +79,8 @@ pub fn render_change_artifact(file_name: &str, brief_summary: &str, default_owne "Bound the intended change explicitly before implementation expands the surface area." .to_string(), ); - let owner = extract_marker(brief_summary, &normalized, "owner") - .unwrap_or_else(|| owner_default(default_owner)); - let risk_level = extract_marker(brief_summary, &normalized, "risk level") - .unwrap_or("unspecified-risk".to_string()); - let zone = extract_marker(brief_summary, &normalized, "zone") - .unwrap_or("unspecified-zone".to_string()); + let DeliveryMarkers { owner, risk_level, zone } = + extract_delivery_markers(brief_summary, &normalized, default_owner); let summary = render_change_bundle_summary( file_name, &system_slice, @@ -37,268 +90,78 @@ pub fn render_change_artifact(file_name: &str, brief_summary: &str, default_owne &zone, ); - match file_name { - "system-slice.md" => render_authored_artifact( - "System Slice", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "System Slice", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Domain Slice", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Excluded Areas", aliases: &[] }, - ], - ), - "legacy-invariants.md" => render_authored_artifact( - "Legacy Invariants", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Legacy Invariants", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Domain Invariants", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Forbidden Normalization", aliases: &[] }, - ], - ), - "change-surface.md" => render_authored_artifact( - "Change Surface", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Change Surface", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Ownership", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Cross-Context Risks", aliases: &[] }, - ], - ), - "implementation-plan.md" => render_authored_artifact( - "Implementation Plan", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Implementation Plan", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Sequencing", aliases: &[] }, - ], - ), - "validation-strategy.md" => render_authored_artifact( - "Validation Strategy", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Validation Strategy", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Independent Checks", aliases: &[] }, - ], - ), - "decision-record.md" => render_authored_artifact( - "Decision Record", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Decision Record", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Decision Drivers", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Options Considered", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Decision Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Boundary Tradeoffs", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Recommendation", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Why Not The Others", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Consequences", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Unresolved Questions", aliases: &[] }, - ], - ), - other => render_markdown(other, brief_summary), - } + artifact_match!(file_name, &summary, brief_summary, { + "system-slice.md" => "System Slice", ["System Slice", "Domain Slice", "Excluded Areas"], + "legacy-invariants.md" => "Legacy Invariants", ["Legacy Invariants", "Domain Invariants", "Forbidden Normalization"], + "change-surface.md" => "Change Surface", ["Change Surface", "Ownership", "Cross-Context Risks"], + "implementation-plan.md" => "Implementation Plan", ["Implementation Plan", "Sequencing"], + "validation-strategy.md" => "Validation Strategy", ["Validation Strategy", "Independent Checks"], + "decision-record.md" => "Decision Record", ["Decision Record", "Decision Drivers", "Options Considered", "Decision Evidence", "Boundary Tradeoffs", "Recommendation", "Why Not The Others", "Consequences", "Unresolved Questions"], + }) } /// Renders an incident mode artifact for the given filename slug. pub fn render_incident_artifact(file_name: &str, brief_summary: &str) -> String { let file_name = artifact_slug(file_name); let normalized = brief_summary.to_lowercase(); - let incident_scope = extract_authored_section_or_marker( + let incident_scope = extract_required_section( brief_summary, &normalized, "Incident Scope", - &[], - &["incident scope"], - ) - .unwrap_or_else(|| "incident scope not yet authored".to_string()); - let trigger_and_current_state = extract_authored_section_or_marker( + "incident scope not yet authored", + ); + let trigger_and_current_state = extract_required_section( brief_summary, &normalized, "Trigger And Current State", - &[], - &["trigger and current state"], - ) - .unwrap_or_else(|| "trigger and current state not yet authored".to_string()); + "trigger and current state not yet authored", + ); let summary = format!( "Bounded incident packet for {}. Current state: {}.", truncate_context_excerpt(&incident_scope, 120), truncate_context_excerpt(&trigger_and_current_state, 100) ); - match file_name { - "incident-frame.md" => render_authored_artifact( - "Incident Frame", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Incident Scope", aliases: &[] }, - AuthoredSectionSpec { - canonical_heading: "Trigger And Current State", - aliases: &[], - }, - AuthoredSectionSpec { canonical_heading: "Operational Constraints", aliases: &[] }, - ], - ), - "hypothesis-log.md" => render_authored_artifact( - "Hypothesis Log", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Known Facts", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Working Hypotheses", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Evidence Gaps", aliases: &[] }, - ], - ), - "blast-radius-map.md" => render_authored_artifact( - "Blast Radius Map", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Impacted Surfaces", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Propagation Paths", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Confidence And Unknowns", aliases: &[] }, - ], - ), - "containment-plan.md" => render_authored_artifact( - "Containment Plan", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Immediate Actions", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Ordered Sequence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Stop Conditions", aliases: &[] }, - ], - ), - "incident-decision-record.md" => render_authored_artifact( - "Incident Decision Record", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Decision Points", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Approved Actions", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Deferred Actions", aliases: &[] }, - ], - ), - "follow-up-verification.md" => render_authored_artifact( - "Follow-Up Verification", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Verification Checks", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Release Readiness", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Follow-Up Work", aliases: &[] }, - ], - ), - other => render_markdown(other, brief_summary), - } + artifact_match!(file_name, &summary, brief_summary, { + "incident-frame.md" => "Incident Frame", ["Incident Scope", "Trigger And Current State", "Operational Constraints"], + "hypothesis-log.md" => "Hypothesis Log", ["Known Facts", "Working Hypotheses", "Evidence Gaps"], + "blast-radius-map.md" => "Blast Radius Map", ["Impacted Surfaces", "Propagation Paths", "Confidence And Unknowns"], + "containment-plan.md" => "Containment Plan", ["Immediate Actions", "Ordered Sequence", "Stop Conditions"], + "incident-decision-record.md" => "Incident Decision Record", ["Decision Points", "Approved Actions", "Deferred Actions"], + "follow-up-verification.md" => "Follow-Up Verification", ["Verification Checks", "Release Readiness", "Follow-Up Work"], + }) } /// Renders a security assessment mode artifact for the given filename slug. pub fn render_migration_artifact(file_name: &str, brief_summary: &str) -> String { let file_name = artifact_slug(file_name); let normalized = brief_summary.to_lowercase(); - let current_state = extract_authored_section_or_marker( + let current_state = extract_required_section( brief_summary, &normalized, "Current State", - &[], - &["current state"], - ) - .unwrap_or_else(|| "current state not yet authored".to_string()); - let target_state = extract_authored_section_or_marker( + "current state not yet authored", + ); + let target_state = extract_required_section( brief_summary, &normalized, "Target State", - &[], - &["target state"], - ) - .unwrap_or_else(|| "target state not yet authored".to_string()); + "target state not yet authored", + ); let summary = format!( "Bounded migration packet from {} to {}.", truncate_context_excerpt(¤t_state, 80), truncate_context_excerpt(&target_state, 80) ); - match file_name { - "source-target-map.md" => render_authored_artifact( - "Source-Target Map", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Current State", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Target State", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Transition Boundaries", aliases: &[] }, - ], - ), - "compatibility-matrix.md" => render_authored_artifact( - "Compatibility Matrix", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Guaranteed Compatibility", aliases: &[] }, - AuthoredSectionSpec { - canonical_heading: "Temporary Incompatibilities", - aliases: &[], - }, - AuthoredSectionSpec { canonical_heading: "Coexistence Rules", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Options Matrix", aliases: &[] }, - ], - ), - "sequencing-plan.md" => render_authored_artifact( - "Sequencing Plan", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Ordered Steps", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Parallelizable Work", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Cutover Criteria", aliases: &[] }, - ], - ), - "fallback-plan.md" => render_authored_artifact( - "Fallback Plan", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Rollback Triggers", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Fallback Paths", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Re-Entry Criteria", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Adoption Implications", aliases: &[] }, - ], - ), - "migration-verification-report.md" => render_authored_artifact( - "Migration Verification Report", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Verification Checks", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Residual Risks", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Release Readiness", aliases: &[] }, - ], - ), - "decision-record.md" => render_authored_artifact( - "Decision Record", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Migration Decisions", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Tradeoff Analysis", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Decision Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Recommendation", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Why Not The Others", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Ecosystem Health", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Deferred Decisions", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Approval Notes", aliases: &[] }, - ], - ), - other => render_markdown(other, brief_summary), - } + artifact_match!(file_name, &summary, brief_summary, { + "source-target-map.md" => "Source-Target Map", ["Current State", "Target State", "Transition Boundaries"], + "compatibility-matrix.md" => "Compatibility Matrix", ["Guaranteed Compatibility", "Temporary Incompatibilities", "Coexistence Rules", "Options Matrix"], + "sequencing-plan.md" => "Sequencing Plan", ["Ordered Steps", "Parallelizable Work", "Cutover Criteria"], + "fallback-plan.md" => "Fallback Plan", ["Rollback Triggers", "Fallback Paths", "Re-Entry Criteria", "Adoption Implications"], + "migration-verification-report.md" => "Migration Verification Report", ["Verification Checks", "Residual Risks", "Release Readiness"], + "decision-record.md" => "Decision Record", ["Migration Decisions", "Tradeoff Analysis", "Decision Evidence", "Recommendation", "Why Not The Others", "Ecosystem Health", "Deferred Decisions", "Approval Notes"], + }) } /// Renders a backlog mode artifact for the given filename slug. @@ -318,77 +181,15 @@ pub fn render_implementation_artifact( ) -> String { let file_name = artifact_slug(file_name); let normalized = brief_summary.to_lowercase(); - let task_mapping = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Task Mapping", - &[], - &["task mapping", "implementation plan"], - ); - let _bounded_changes = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Bounded Changes", - &[], - &["bounded changes", "allowed paths", "mutation bounds"], - ); - let mutation_bounds = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Mutation Bounds", - &[], - &["mutation bounds"], - ); - let _allowed_paths = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Allowed Paths", - &[], - &["allowed paths"], - ); - let _safety_net_evidence = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Safety-Net Evidence", - &[], - &["safety-net evidence", "safety net evidence"], - ); - let _independent_checks = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Independent Checks", - &[], - &["independent checks", "validation strategy"], - ); - let _rollback_triggers = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Rollback Triggers", - &[], - &["rollback triggers"], - ); - let _rollback_steps = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Rollback Steps", - &[], - &["rollback steps"], - ); - let _validation_evidence = extract_marker(brief_summary, &normalized, "validation evidence") - .unwrap_or( - "Validation evidence was recorded through the governed validation command.".to_string(), - ); + let task_mapping = extract_optional_section(brief_summary, &normalized, "Task Mapping"); + let mutation_bounds = extract_optional_section(brief_summary, &normalized, "Mutation Bounds"); let mutation_posture = extract_marker(brief_summary, &normalized, "mutation posture") .unwrap_or( "Recommendation-only posture remains active until a later run is explicitly allowed to mutate." .to_string(), ); - let owner = extract_marker(brief_summary, &normalized, "owner") - .unwrap_or_else(|| owner_default(default_owner)); - let risk_level = extract_marker(brief_summary, &normalized, "risk level") - .unwrap_or("unspecified-risk".to_string()); - let zone = extract_marker(brief_summary, &normalized, "zone") - .unwrap_or("unspecified-zone".to_string()); + let DeliveryMarkers { owner, risk_level, zone } = + extract_delivery_markers(brief_summary, &normalized, default_owner); let summary = render_implementation_bundle_summary( file_name, task_mapping.as_deref().unwrap_or( @@ -402,69 +203,17 @@ pub fn render_implementation_artifact( &zone, ); - match file_name { - "task-mapping.md" => render_authored_artifact( - "Task Mapping", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Task Mapping", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Bounded Changes", aliases: &[] }, - ], - ), - "mutation-bounds.md" => render_authored_artifact( - "Mutation Bounds", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Mutation Bounds", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Allowed Paths", aliases: &[] }, - ], - ), - "implementation-notes.md" => render_authored_artifact( - "Implementation Notes", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Executed Changes", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Candidate Frameworks", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Options Matrix", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Decision Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Recommendation", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Task Linkage", aliases: &[] }, - ], - ), - "completion-evidence.md" => render_authored_artifact( - "Completion Evidence", - &format!("{summary}\n- Mutation posture: {}", compact_summary_line(&mutation_posture)), - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Completion Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Adoption Implications", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Remaining Risks", aliases: &[] }, - ], - ), - "validation-hooks.md" => render_authored_artifact( - "Validation Hooks", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Ecosystem Health", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Safety-Net Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Independent Checks", aliases: &[] }, - ], - ), - "rollback-notes.md" => render_authored_artifact( - "Rollback Notes", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Rollback Triggers", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Rollback Steps", aliases: &[] }, - ], - ), - other => render_markdown(other, brief_summary), - } + let completion_summary = + format!("{}\n- Mutation posture: {}", summary, compact_summary_line(&mutation_posture)); + + artifact_match!(file_name, &summary, brief_summary, { + "task-mapping.md" => "Task Mapping", ["Task Mapping", "Bounded Changes"], + "mutation-bounds.md" => "Mutation Bounds", ["Mutation Bounds", "Allowed Paths"], + "implementation-notes.md" => "Implementation Notes", ["Executed Changes", "Candidate Frameworks", "Options Matrix", "Decision Evidence", "Recommendation", "Task Linkage"], + "completion-evidence.md" => "Completion Evidence", ["Completion Evidence", "Adoption Implications", "Remaining Risks"] => &completion_summary, + "validation-hooks.md" => "Validation Hooks", ["Ecosystem Health", "Safety-Net Evidence", "Independent Checks"], + "rollback-notes.md" => "Rollback Notes", ["Rollback Triggers", "Rollback Steps"], + }) } /// Renders a refactor mode artifact for the given filename slug. @@ -475,93 +224,9 @@ pub fn render_refactor_artifact( ) -> String { let file_name = artifact_slug(file_name); let normalized = brief_summary.to_lowercase(); - let preserved_behavior = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Preserved Behavior", - &[], - &["preserved behavior"], - ); - let _approved_exceptions = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Approved Exceptions", - &[], - &["approved exceptions"], - ) - .unwrap_or("None.".to_string()); - let refactor_scope = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Refactor Scope", - &[], - &["refactor scope"], - ); - let _allowed_paths = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Allowed Paths", - &[], - &["allowed paths"], - ); - let _structural_rationale = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Structural Rationale", - &[], - &["structural rationale"], - ); - let _untouched_surface = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Untouched Surface", - &[], - &["untouched surface"], - ); - let _safety_net_evidence = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Safety-Net Evidence", - &[], - &["safety-net evidence", "safety net evidence"], - ); - let _regression_findings = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Regression Findings", - &[], - &["regression findings"], - ) - .unwrap_or("No regression findings are accepted in the bounded refactor packet.".to_string()); - let _contract_drift = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Contract Drift", - &[], - &["contract drift"], - ); - let _reviewer_notes = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Reviewer Notes", - &[], - &["reviewer notes"], - ) - .unwrap_or("Reviewer confirmation is required before any drift is accepted.".to_string()); - let _feature_audit = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Feature Audit", - &[], - &["feature audit"], - ); - let _decision = extract_authored_section_or_marker( - brief_summary, - &normalized, - "Decision", - &[], - &["decision"], - ); + let preserved_behavior = + extract_optional_section(brief_summary, &normalized, "Preserved Behavior"); + let refactor_scope = extract_optional_section(brief_summary, &normalized, "Refactor Scope"); let owner = extract_marker(brief_summary, &normalized, "owner") .unwrap_or_else(|| owner_default(default_owner)); let risk_level = extract_marker(brief_summary, &normalized, "risk level") @@ -581,63 +246,44 @@ pub fn render_refactor_artifact( &zone, ); - match file_name { - "preserved-behavior.md" => render_authored_artifact( - "Preserved Behavior", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Preserved Behavior", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Approved Exceptions", aliases: &[] }, - ], - ), - "refactor-scope.md" => render_authored_artifact( - "Refactor Scope", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Refactor Scope", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Allowed Paths", aliases: &[] }, - ], - ), - "structural-rationale.md" => render_authored_artifact( - "Structural Rationale", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Structural Rationale", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Untouched Surface", aliases: &[] }, - ], - ), - "regression-evidence.md" => render_authored_artifact( - "Regression Evidence", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Safety-Net Evidence", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Regression Findings", aliases: &[] }, - ], - ), - "contract-drift-check.md" => render_authored_artifact( - "Contract Drift Check", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Contract Drift", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Reviewer Notes", aliases: &[] }, - ], - ), - "no-feature-addition.md" => render_authored_artifact( - "No Feature Addition", - &summary, - brief_summary, - &[ - AuthoredSectionSpec { canonical_heading: "Feature Audit", aliases: &[] }, - AuthoredSectionSpec { canonical_heading: "Decision", aliases: &[] }, - ], - ), - other => render_markdown(other, brief_summary), - } + artifact_match!(file_name, &summary, brief_summary, { + "preserved-behavior.md" => "Preserved Behavior", ["Preserved Behavior", "Approved Exceptions"], + "refactor-scope.md" => "Refactor Scope", ["Refactor Scope", "Allowed Paths"], + "structural-rationale.md" => "Structural Rationale", ["Structural Rationale", "Untouched Surface"], + "regression-evidence.md" => "Regression Evidence", ["Safety-Net Evidence", "Regression Findings"], + "contract-drift-check.md" => "Contract Drift Check", ["Contract Drift", "Reviewer Notes"], + "no-feature-addition.md" => "No Feature Addition", ["Feature Audit", "Decision"], + }) +} + +/// Renders a debugging mode artifact for the given filename slug. +pub fn render_debugging_artifact( + file_name: &str, + brief_summary: &str, + default_owner: &str, +) -> String { + let file_name = artifact_slug(file_name); + let normalized = brief_summary.to_lowercase(); + let defect_description = extract_required_section( + brief_summary, + &normalized, + "Defect Description", + "Capture the defect description before debugging can proceed.", + ); + + let DeliveryMarkers { owner, risk_level, zone } = + extract_delivery_markers(brief_summary, &normalized, default_owner); + + let summary = + render_debugging_bundle_summary(file_name, &defect_description, &owner, &risk_level, &zone); + + artifact_match!(file_name, &summary, brief_summary, { + "context-map.md" => "Context Map", ["Context Map", "Defect Description", "Stakeholder Impact"], + "reproduction-harness.md" => "Reproduction Harness", ["Reproduction Harness", "Red State Verification"], + "root-cause-isolation.md" => "Root Cause Isolation", ["Root Cause Isolation", "Fault Chain", "Isolation Proof"], + "fix-application.md" => "Fix Application", ["Fix Application", "Bounded Changes", "Invariant Preservation"], + "verification-summary.md" => "Verification Summary", ["Verification Summary", "Green State", "No Regression Evidence"], + }) } fn render_change_bundle_summary( @@ -648,19 +294,17 @@ fn render_change_bundle_summary( risk_level: &str, zone: &str, ) -> String { - let detail_links = [ - "system-slice.md", - "legacy-invariants.md", - "change-surface.md", - "implementation-plan.md", - "validation-strategy.md", - "decision-record.md", - ] - .into_iter() - .filter(|file_name| *file_name != current_file) - .map(|file_name| format!("[{file_name}]({file_name})")) - .collect::>() - .join(", "); + let detail_links = render_detail_links( + current_file, + &[ + "system-slice.md", + "legacy-invariants.md", + "change-surface.md", + "implementation-plan.md", + "validation-strategy.md", + "decision-record.md", + ], + ); format!( "- Bounded slice: `{}`\n- Intended change: {}\n- Owner / risk / zone: `{}` / `{}` / `{}`\n- Details: {}", @@ -681,19 +325,17 @@ fn render_implementation_bundle_summary( risk_level: &str, zone: &str, ) -> String { - let detail_links = [ - "task-mapping.md", - "mutation-bounds.md", - "implementation-notes.md", - "completion-evidence.md", - "validation-hooks.md", - "rollback-notes.md", - ] - .into_iter() - .filter(|file_name| *file_name != current_file) - .map(|file_name| format!("[{file_name}]({file_name})")) - .collect::>() - .join(", "); + let detail_links = render_detail_links( + current_file, + &[ + "task-mapping.md", + "mutation-bounds.md", + "implementation-notes.md", + "completion-evidence.md", + "validation-hooks.md", + "rollback-notes.md", + ], + ); format!( "- Task scope: {}\n- Mutation bounds: `{}`\n- Owner / risk / zone: `{}` / `{}` / `{}`\n- Details: {}", @@ -714,19 +356,17 @@ fn render_refactor_bundle_summary( risk_level: &str, zone: &str, ) -> String { - let detail_links = [ - "preserved-behavior.md", - "refactor-scope.md", - "structural-rationale.md", - "regression-evidence.md", - "contract-drift-check.md", - "no-feature-addition.md", - ] - .into_iter() - .filter(|file_name| *file_name != current_file) - .map(|file_name| format!("[{file_name}]({file_name})")) - .collect::>() - .join(", "); + let detail_links = render_detail_links( + current_file, + &[ + "preserved-behavior.md", + "refactor-scope.md", + "structural-rationale.md", + "regression-evidence.md", + "contract-drift-check.md", + "no-feature-addition.md", + ], + ); format!( "- Preserved behavior: {}\n- Refactor scope: `{}`\n- Owner / risk / zone: `{}` / `{}` / `{}`\n- Details: {}", @@ -739,11 +379,69 @@ fn render_refactor_bundle_summary( ) } +fn render_debugging_bundle_summary( + current_file: &str, + defect_description: &str, + owner: &str, + risk_level: &str, + zone: &str, +) -> String { + let detail_links = render_detail_links( + current_file, + &[ + "context-map.md", + "reproduction-harness.md", + "root-cause-isolation.md", + "fix-application.md", + "verification-summary.md", + ], + ); + + format!( + "- Defect: {}\n- Owner / risk / zone: `{}` / `{}` / `{}`\n- Details: {}", + compact_summary_line(defect_description), + compact_summary_line(owner), + compact_summary_line(risk_level), + compact_summary_line(zone), + detail_links, + ) +} + fn compact_summary_line(value: &str) -> String { value.split_whitespace().collect::>().join(" ") } +fn render_detail_links(current_file: &str, files: &[&str]) -> String { + files + .iter() + .filter(|file_name| **file_name != current_file) + .map(|file_name| format!("[{file_name}]({file_name})")) + .collect::>() + .join(", ") +} + +struct DeliveryMarkers { + owner: String, + risk_level: String, + zone: String, +} + +fn extract_delivery_markers( + brief_summary: &str, + normalized: &str, + default_owner: &str, +) -> DeliveryMarkers { + let owner = extract_marker(brief_summary, normalized, MARKER_OWNER) + .unwrap_or_else(|| owner_default(default_owner)); + let risk_level = extract_marker(brief_summary, normalized, MARKER_RISK) + .unwrap_or_else(|| DEFAULT_RISK.to_string()); + let zone = extract_marker(brief_summary, normalized, MARKER_ZONE) + .unwrap_or_else(|| DEFAULT_ZONE.to_string()); + + DeliveryMarkers { owner, risk_level, zone } +} + fn owner_default(default_owner: &str) -> String { let trimmed = default_owner.trim(); - if trimmed.is_empty() { "bounded-system-maintainer".to_string() } else { trimmed.to_string() } + if trimmed.is_empty() { DEFAULT_OWNER.to_string() } else { trimmed.to_string() } } diff --git a/crates/canon-engine/src/domain/gate.rs b/crates/canon-engine/src/domain/gate.rs index e48ea514..33511e0e 100644 --- a/crates/canon-engine/src/domain/gate.rs +++ b/crates/canon-engine/src/domain/gate.rs @@ -26,6 +26,12 @@ pub enum GateKind { IncidentContainment, /// Safety of data or system migration. MigrationSafety, + /// Verification that a bug has been reproduced. + Reproduction, + /// Verification that a failing test was written before the fix. + TestDrivenDevelopment, + /// Verification that the fix addresses the true root cause. + RootCause, } impl GateKind { @@ -52,6 +58,9 @@ impl std::str::FromStr for GateKind { } "incident-containment" | "IncidentContainment" => Ok(Self::IncidentContainment), "migration-safety" | "MigrationSafety" => Ok(Self::MigrationSafety), + "reproduction" | "Reproduction" => Ok(Self::Reproduction), + "test-driven-development" | "TestDrivenDevelopment" => Ok(Self::TestDrivenDevelopment), + "root-cause" | "RootCause" => Ok(Self::RootCause), other => Err(format!("unsupported gate kind: {other}")), } } @@ -108,6 +117,9 @@ mod tests { ), (GateKind::IncidentContainment, "incident-containment", "IncidentContainment"), (GateKind::MigrationSafety, "migration-safety", "MigrationSafety"), + (GateKind::Reproduction, "reproduction", "Reproduction"), + (GateKind::TestDrivenDevelopment, "test-driven-development", "TestDrivenDevelopment"), + (GateKind::RootCause, "root-cause", "RootCause"), ]; for (gate, kebab, pascal) in cases { diff --git a/crates/canon-engine/src/domain/mode.rs b/crates/canon-engine/src/domain/mode.rs index dca952a0..6a96c280 100644 --- a/crates/canon-engine/src/domain/mode.rs +++ b/crates/canon-engine/src/domain/mode.rs @@ -48,6 +48,8 @@ pub enum Mode { DomainLanguage, /// Formalize domain concepts, relationships, and invariants. DomainModel, + /// Systematically isolate and resolve defects using TDD and explicit root cause verification. + Debugging, } /// The class of governed expertise packet produced by an authoring-specialization mode. @@ -141,6 +143,7 @@ impl Mode { Self::SupplyChainAnalysis => "supply-chain-analysis", Self::DomainLanguage => "domain-language", Self::DomainModel => "domain-model", + Self::Debugging => "debugging", } } @@ -174,6 +177,7 @@ impl Mode { Self::SupplyChainAnalysis, Self::DomainLanguage, Self::DomainModel, + Self::Debugging, ] } } @@ -213,6 +217,7 @@ impl std::str::FromStr for Mode { "supply-chain-analysis" => Ok(Self::SupplyChainAnalysis), "domain-language" => Ok(Self::DomainLanguage), "domain-model" => Ok(Self::DomainModel), + "debugging" => Ok(Self::Debugging), other => Err(format!("unsupported mode: {other}")), } } @@ -310,6 +315,14 @@ impl Mode { "single-path validation".to_string(), ], }, + Self::Debugging => IntendedPersonaProfile { + intended_persona: IntendedPersona::DeliveryEngineer, + persona_anti_behaviors: vec![ + "symptom treating".to_string(), + "unverified claims".to_string(), + "change without tests".to_string(), + ], + }, Self::Incident | Self::SecurityAssessment | Self::Migration @@ -395,8 +408,8 @@ pub fn all_mode_profiles() -> Vec { }; use ImplementationDepth::Full; use Mode::{ - Architecture as ArchitectureMode, Backlog, Change, Discovery, DomainLanguage, DomainModel, - Implementation, Incident, Migration, PrReview, Refactor, Requirements, Review, + Architecture as ArchitectureMode, Backlog, Change, Debugging, Discovery, DomainLanguage, + DomainModel, Implementation, Incident, Migration, PrReview, Refactor, Requirements, Review, SecurityAssessment, SupplyChainAnalysis, SystemAssessment, SystemShaping, Verification, }; use ModeEmphasis::{AnalysisHeavy, ExecutionHeavy, ReviewHeavy}; @@ -710,6 +723,26 @@ pub fn all_mode_profiles() -> Vec { ], allowed_adapters: vec![Filesystem, Shell, CopilotCli], }, + ModeProfile { + mode: Debugging, + purpose: "Systematically identify, isolate, and verify fixes for defects.", + emphasis: ExecutionHeavy, + implementation_depth: Full, + gate_profile: vec![ + GateKind::Reproduction, + GateKind::TestDrivenDevelopment, + GateKind::RootCause, + ReleaseReadiness, + ], + artifact_families: vec![ + "context map", + "reproduction harness", + "root cause isolation", + "fix application", + "verification summary", + ], + allowed_adapters: vec![Filesystem, Shell, CopilotCli], + }, ] } @@ -738,6 +771,7 @@ mod tests { (Mode::SupplyChainAnalysis, "supply-chain-analysis", None), (Mode::DomainLanguage, "domain-language", Some(GovernedExpertiseKind::DomainLanguage)), (Mode::DomainModel, "domain-model", Some(GovernedExpertiseKind::DomainModel)), + (Mode::Debugging, "debugging", None), ]; assert_eq!( @@ -761,6 +795,7 @@ mod tests { Mode::SupplyChainAnalysis, Mode::DomainLanguage, Mode::DomainModel, + Mode::Debugging, ] ); diff --git a/crates/canon-engine/src/domain/publish_profile/semantic.rs b/crates/canon-engine/src/domain/publish_profile/semantic.rs index f83ae623..c3549470 100644 --- a/crates/canon-engine/src/domain/publish_profile/semantic.rs +++ b/crates/canon-engine/src/domain/publish_profile/semantic.rs @@ -18,13 +18,13 @@ pub const GOVERNED_REASONING_POSTURE_V1_BOUNDLINE_MAX_EXCLUSIVE: &str = "0.63.0" /// Minimum supported Canon version for the governed reasoning posture v1 line. pub const GOVERNED_REASONING_POSTURE_V1_CANON_MIN: &str = "0.63.1"; /// Maximum exclusive Canon version for the governed reasoning posture v1 line. -pub const GOVERNED_REASONING_POSTURE_V1_CANON_MAX_EXCLUSIVE: &str = "0.64.0"; +pub const GOVERNED_REASONING_POSTURE_V1_CANON_MAX_EXCLUSIVE: &str = "0.65.0"; /// Minimum supported Boundline version for the governed reasoning posture v2 line. pub const GOVERNED_REASONING_POSTURE_V2_BOUNDLINE_MIN: &str = "0.63.0"; /// Maximum exclusive Boundline version for the governed reasoning posture v2 line. -pub const GOVERNED_REASONING_POSTURE_V2_BOUNDLINE_MAX_EXCLUSIVE: &str = "0.64.0"; +pub const GOVERNED_REASONING_POSTURE_V2_BOUNDLINE_MAX_EXCLUSIVE: &str = "0.65.0"; /// Minimum supported Canon version for the governed reasoning posture v2 line. -pub const GOVERNED_REASONING_POSTURE_V2_CANON_MIN: &str = "0.64.0"; +pub const GOVERNED_REASONING_POSTURE_V2_CANON_MIN: &str = "0.65.0"; /// Maximum exclusive Canon version for the governed reasoning posture v2 line. pub const GOVERNED_REASONING_POSTURE_V2_CANON_MAX_EXCLUSIVE: &str = "0.65.0"; diff --git a/crates/canon-engine/src/modes.rs b/crates/canon-engine/src/modes.rs index c80763cd..238a1358 100644 --- a/crates/canon-engine/src/modes.rs +++ b/crates/canon-engine/src/modes.rs @@ -4,6 +4,8 @@ pub mod architecture; pub mod backlog; /// Change mode execution logic. pub mod change; +/// Debugging mode execution logic. +pub mod debugging; /// Discovery mode execution logic. pub mod discovery; /// Implementation mode execution logic. diff --git a/crates/canon-engine/src/modes/debugging.rs b/crates/canon-engine/src/modes/debugging.rs new file mode 100644 index 00000000..c1c41512 --- /dev/null +++ b/crates/canon-engine/src/modes/debugging.rs @@ -0,0 +1,19 @@ +use crate::domain::gate::GateKind; + +/// The method definition filename for debugging mode. +pub const MODE_FILE: &str = "debugging.toml"; + +/// The ordered step sequence for debugging mode execution. +pub const STEP_SEQUENCE: &[&str] = &[ + "capture-context", + "generate-hypothesis", + "record-red-state", + "isolate-root-cause", + "apply-fix", + "record-green-state", + "evaluate-gates", +]; + +/// The gates required to close a debugging mode run. +pub const REQUIRED_GATES: &[GateKind] = + &[GateKind::Reproduction, GateKind::TestDrivenDevelopment, GateKind::RootCause]; diff --git a/crates/canon-engine/src/orchestrator/classifier.rs b/crates/canon-engine/src/orchestrator/classifier.rs index a054beb3..a325ec3c 100644 --- a/crates/canon-engine/src/orchestrator/classifier.rs +++ b/crates/canon-engine/src/orchestrator/classifier.rs @@ -25,7 +25,8 @@ pub fn system_context_requirement(mode: Mode) -> SystemContextRequirement { | Mode::Incident | Mode::SystemAssessment | Mode::SecurityAssessment - | Mode::SupplyChainAnalysis => SystemContextRequirement::Required, + | Mode::SupplyChainAnalysis + | Mode::Debugging => SystemContextRequirement::Required, Mode::Discovery | Mode::Requirements | Mode::Review diff --git a/crates/canon-engine/src/orchestrator/gatekeeper/context.rs b/crates/canon-engine/src/orchestrator/gatekeeper/context.rs index 29a750b2..69d27f5a 100644 --- a/crates/canon-engine/src/orchestrator/gatekeeper/context.rs +++ b/crates/canon-engine/src/orchestrator/gatekeeper/context.rs @@ -103,6 +103,24 @@ pub struct ImplementationGateContext<'a> { pub evidence_complete: bool, } +/// Evaluation context for Debugging mode gate checks. +pub struct DebuggingGateContext<'a> { + /// Named human owner required for high-risk/red-zone work. + pub owner: &'a str, + /// Assigned risk class for the run. + pub risk: RiskClass, + /// Assigned usage zone for the run. + pub zone: UsageZone, + /// Approval records recorded against this run. + pub approvals: &'a [ApprovalRecord], + /// Whether the run targets a new or existing system. + pub system_context: Option, + /// Whether validation and generation are performed by independent actors. + pub validation_independence_satisfied: bool, + /// Whether all required evidence has been captured. + pub evidence_complete: bool, +} + /// Evaluation context for Incident mode gate checks. pub struct IncidentGateContext<'a> { /// Named human owner required for high-risk/red-zone work. diff --git a/crates/canon-engine/src/orchestrator/gatekeeper/entrypoints.rs b/crates/canon-engine/src/orchestrator/gatekeeper/entrypoints.rs index 7705328d..2a710a26 100644 --- a/crates/canon-engine/src/orchestrator/gatekeeper/entrypoints.rs +++ b/crates/canon-engine/src/orchestrator/gatekeeper/entrypoints.rs @@ -704,3 +704,49 @@ pub fn evaluate_domain_model_gates( ) -> Vec { analysis_mode_gate_set(contract, artifacts, DOMAIN_MODEL_ANALYSIS_SPEC, context) } + +/// Evaluates the gate set for a Debugging mode run. +pub fn evaluate_debugging_gates( + contract: &ArtifactContract, + artifacts: &[(String, String)], + context: DebuggingGateContext<'_>, +) -> Vec { + vec![ + rules::named_artifact_gate( + GateKind::Exploration, + contract, + artifacts, + &["context-map.md"], + "debugging exploration requires a bounded context map", + ), + rules::named_artifact_gate( + GateKind::Reproduction, + contract, + artifacts, + &["reproduction-harness.md"], + "debugging requires an explicit reproduction harness", + ), + rules::named_artifact_gate( + GateKind::RootCause, + contract, + artifacts, + &["root-cause-isolation.md", "fix-application.md"], + "debugging requires root cause isolation and fix application evidence", + ), + rules::approval_aware_risk_gate( + context.owner, + context.risk, + context.zone, + context.approvals, + "systemic-impact or red-zone debugging work requires explicit approval before it can proceed", + ), + rules::analysis_release_readiness_gate( + GateKind::ReleaseReadiness, + contract, + artifacts, + context.validation_independence_satisfied, + context.evidence_complete, + "debugging readiness requires persisted context, critique, and verification evidence", + ), + ] +} diff --git a/crates/canon-engine/src/orchestrator/publish/policy.rs b/crates/canon-engine/src/orchestrator/publish/policy.rs index 7d76caeb..f9af95b8 100644 --- a/crates/canon-engine/src/orchestrator/publish/policy.rs +++ b/crates/canon-engine/src/orchestrator/publish/policy.rs @@ -55,6 +55,7 @@ pub(super) fn default_update_strategy_for(mode: Mode) -> UpdateStrategy { | Mode::Change | Mode::Implementation | Mode::Refactor + | Mode::Debugging | Mode::DomainLanguage | Mode::DomainModel => UpdateStrategy::ManagedBlocks, Mode::Incident | Mode::Migration => UpdateStrategy::ProposalFiles, @@ -95,7 +96,7 @@ pub(super) fn stable_project_memory_surface(mode: Mode) -> &'static str { Mode::Requirements => "tech-docs/project/product-context.md", Mode::SystemShaping | Mode::Architecture => "tech-docs/project/architecture-map.md", Mode::Change | Mode::Migration => "tech-docs/project/decision-index.md", - Mode::Backlog | Mode::Implementation | Mode::Refactor => { + Mode::Backlog | Mode::Implementation | Mode::Refactor | Mode::Debugging => { "tech-docs/project/delivery-map.md" } Mode::DomainLanguage => "tech-docs/project/domain-language.md", diff --git a/crates/canon-engine/src/orchestrator/publish/support.rs b/crates/canon-engine/src/orchestrator/publish/support.rs index f2ce30bd..cc78d5ea 100644 --- a/crates/canon-engine/src/orchestrator/publish/support.rs +++ b/crates/canon-engine/src/orchestrator/publish/support.rs @@ -133,5 +133,16 @@ pub(super) fn default_publish_directory(mode: Mode) -> &'static str { Mode::SupplyChainAnalysis => "tech-docs/supply-chain", Mode::DomainLanguage => "tech-docs/domain/language", Mode::DomainModel => "tech-docs/domain/model", + Mode::Debugging => "tech-docs/debugging", + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_publish_directory_covers_debugging() { + assert_eq!(default_publish_directory(Mode::Debugging), "tech-docs/debugging"); } } diff --git a/crates/canon-engine/src/orchestrator/publish/tests.rs b/crates/canon-engine/src/orchestrator/publish/tests.rs index e0d583f0..d9f7de3d 100644 --- a/crates/canon-engine/src/orchestrator/publish/tests.rs +++ b/crates/canon-engine/src/orchestrator/publish/tests.rs @@ -1419,6 +1419,12 @@ fn canonical_project_memory_surface_map_covers_all_modes() { "tech-docs/project/pending-decisions.md", "tech-docs/project/audit-log.md", ), + ( + Mode::Debugging, + "tech-docs/project/delivery-map.md", + "tech-docs/project/pending-decisions.md", + "tech-docs/project/audit-log.md", + ), ]; assert_eq!(expected.len(), Mode::all().len()); diff --git a/crates/canon-engine/src/orchestrator/service/clarity/authored_family.rs b/crates/canon-engine/src/orchestrator/service/clarity/authored_family.rs index 56cdebf5..78ee9f80 100644 --- a/crates/canon-engine/src/orchestrator/service/clarity/authored_family.rs +++ b/crates/canon-engine/src/orchestrator/service/clarity/authored_family.rs @@ -22,7 +22,9 @@ pub(super) fn authored_clarity_family(mode: Mode) -> AuthoredClarityFamily { Mode::SystemShaping | Mode::Architecture | Mode::Change | Mode::Backlog => { AuthoredClarityFamily::Planning } - Mode::Implementation | Mode::Refactor | Mode::Migration => AuthoredClarityFamily::Execution, + Mode::Implementation | Mode::Refactor | Mode::Migration | Mode::Debugging => { + AuthoredClarityFamily::Execution + } Mode::Review | Mode::Verification | Mode::Incident diff --git a/crates/canon-engine/src/orchestrator/service/identity.rs b/crates/canon-engine/src/orchestrator/service/identity.rs index e9e97e66..1388c4a4 100644 --- a/crates/canon-engine/src/orchestrator/service/identity.rs +++ b/crates/canon-engine/src/orchestrator/service/identity.rs @@ -253,7 +253,7 @@ impl EngineService { | Mode::Backlog | Mode::DomainLanguage | Mode::DomainModel => RefinementWorkflowFamily::Planning, - Mode::Implementation | Mode::Refactor | Mode::Migration => { + Mode::Implementation | Mode::Refactor | Mode::Migration | Mode::Debugging => { RefinementWorkflowFamily::Execution } Mode::Incident @@ -314,6 +314,7 @@ impl EngineService { | Mode::Implementation | Mode::Refactor | Mode::Migration + | Mode::Debugging | Mode::Incident | Mode::Review | Mode::Verification diff --git a/crates/canon-engine/src/orchestrator/service/mode_change.rs b/crates/canon-engine/src/orchestrator/service/mode_change.rs index 69143acd..fe4432b0 100644 --- a/crates/canon-engine/src/orchestrator/service/mode_change.rs +++ b/crates/canon-engine/src/orchestrator/service/mode_change.rs @@ -28,6 +28,11 @@ fn render_change_like_artifact( Ok(render_implementation_artifact(file_name, brief_summary, default_owner)) } Mode::Refactor => Ok(render_refactor_artifact(file_name, brief_summary, default_owner)), + Mode::Debugging => Ok(crate::artifacts::markdown::render_debugging_artifact( + file_name, + brief_summary, + default_owner, + )), other => Err(EngineError::UnsupportedMode(other.as_str().to_string())), } } @@ -111,6 +116,15 @@ fn change_mode_request_summaries( mutation_summary, }) } + Mode::Debugging => Ok(ChangeModeRequestSummary { + context_request_summary: "capture debugging brief and bounded repository context", + context_attempt_summary: "Captured debugging brief and bounded repository context.", + generation_request_summary: "generate bounded debugging packet", + validation_request_summary: "validate debugging defect against repository context", + declared_execution_scope: Vec::new(), + mutation_summary: "propose bounded debugging guidance without mutating the workspace" + .to_string(), + }), other => Err(EngineError::UnsupportedMode(other.as_str().to_string())), } } @@ -146,6 +160,16 @@ impl EngineService { self.execute_change(store, request, policy_set, identity, Vec::new()) } + pub(super) fn run_debugging( + &self, + store: &WorkspaceStore, + request: RunRequest, + policy_set: crate::domain::policy::PolicySet, + ) -> Result { + let identity = self.next_unique_run_identity(store)?; + self.execute_change(store, request, policy_set, identity, Vec::new()) + } + pub(super) fn execute_change( &self, store: &WorkspaceStore, @@ -240,14 +264,15 @@ impl EngineService { && matches!(approval.decision, ApprovalDecision::Approve) }); let execution_gate_approved = execution_gate_is_approved(&approvals); - let mutation_patch = if matches!(request.mode, Mode::Implementation | Mode::Refactor) { - self.locate_authored_mutation_patch( - &request.inputs, - &request_summaries.declared_execution_scope, - )? - } else { - None - }; + let mutation_patch = + if matches!(request.mode, Mode::Implementation | Mode::Refactor | Mode::Debugging) { + self.locate_authored_mutation_patch( + &request.inputs, + &request_summaries.declared_execution_scope, + )? + } else { + None + }; Self::update_mutation_decision_for_approved_execution( request.mode, @@ -727,6 +752,19 @@ impl EngineService { evidence_complete: true, }, ), + Mode::Debugging => gatekeeper::evaluate_debugging_gates( + artifact_contract, + gate_inputs, + gatekeeper::DebuggingGateContext { + owner: &request.owner, + risk: request.risk, + zone: request.zone, + approvals, + system_context: request.system_context, + validation_independence_satisfied, + evidence_complete: true, + }, + ), other => return Err(EngineError::UnsupportedMode(other.as_str().to_string())), }; @@ -1087,3 +1125,28 @@ mod tests { ); } } + +#[cfg(test)] +mod debugging_tests { + use super::*; + + #[test] + fn test_debugging_summaries() { + let summary = change_mode_request_summaries(Mode::Debugging, "brief").unwrap(); + assert_eq!( + summary.context_request_summary, + "capture debugging brief and bounded repository context" + ); + + let artifact = render_change_like_artifact( + Mode::Debugging, + "context-map.md", + "metadata", + "evidence", + "brief", + "owner", + ) + .unwrap(); + assert!(artifact.contains("Context Map")); + } +} diff --git a/crates/canon-engine/src/orchestrator/service/run_op.rs b/crates/canon-engine/src/orchestrator/service/run_op.rs index 4c43ae29..ef0583f1 100644 --- a/crates/canon-engine/src/orchestrator/service/run_op.rs +++ b/crates/canon-engine/src/orchestrator/service/run_op.rs @@ -60,6 +60,7 @@ impl EngineService { self.run_supply_chain_analysis(&store, request, policy_set) } Mode::Implementation => self.run_implementation(&store, request, policy_set), + Mode::Debugging => self.run_debugging(&store, request, policy_set), Mode::Migration => self.run_migration(&store, request, policy_set), Mode::Refactor => self.run_refactor(&store, request, policy_set), Mode::Architecture => self.run_architecture(&store, request, policy_set), @@ -597,7 +598,7 @@ impl EngineService { ) -> bool { match mode { Mode::Change => artifacts_empty, - Mode::Implementation | Mode::Refactor => { + Mode::Implementation | Mode::Refactor | Mode::Debugging => { execution_continuation_pending(context, approvals) } _ => false, diff --git a/crates/canon-engine/src/orchestrator/service/summarizers.rs b/crates/canon-engine/src/orchestrator/service/summarizers.rs index 43bafb56..53ca4ba5 100644 --- a/crates/canon-engine/src/orchestrator/service/summarizers.rs +++ b/crates/canon-engine/src/orchestrator/service/summarizers.rs @@ -71,6 +71,7 @@ pub(crate) fn summarize_mode_result( Mode::PrReview => governance::summarize_pr_review_mode_result(artifacts), Mode::DomainLanguage => domain::summarize_domain_language_mode_result(artifacts), Mode::DomainModel => domain::summarize_domain_model_mode_result(artifacts), + Mode::Debugging => delivery::summarize_debugging_mode_result(artifacts), } } diff --git a/crates/canon-engine/src/orchestrator/service/summarizers/delivery.rs b/crates/canon-engine/src/orchestrator/service/summarizers/delivery.rs index a735de5e..09134d41 100644 --- a/crates/canon-engine/src/orchestrator/service/summarizers/delivery.rs +++ b/crates/canon-engine/src/orchestrator/service/summarizers/delivery.rs @@ -502,3 +502,71 @@ pub(super) fn summarize_migration_mode_result( action_chips: Vec::new(), }) } + +pub(super) fn summarize_debugging_mode_result( + artifacts: &[PersistedArtifact], +) -> Option { + let primary = artifacts.iter().find(|artifact| artifact.record.slug() == "context-map.md")?; + + let defect_description = extract_context_section(&primary.contents, "Defect Description") + .unwrap_or_else(|| "NOT CAPTURED - Defect description is missing.".to_string()); + + let missing_context_markers = count_missing_context_markers([&defect_description]); + + let headline = packet_output_quality_headline( + "Debugging", + missing_context_markers, + 0, + "", + "bounded debugging execution", + ); + let artifact_packet_summary = if missing_context_markers == 0 { + format!( + "{} Packet captures defect: {}.", + packet_output_quality_artifact_prefix(missing_context_markers, 0, ""), + truncate_context_excerpt(&defect_description, 120) + ) + } else { + format!( + "{} Defect description is missing.", + packet_output_quality_artifact_prefix(missing_context_markers, 0, "") + ) + }; + + Some(ModeResultSummary { + headline, + artifact_packet_summary, + execution_posture: None, + primary_artifact_title: "Context Map".to_string(), + primary_artifact_path: format!(".canon/{}", primary.record.relative_path), + primary_artifact_action: primary_artifact_action_for(&format!( + ".canon/{}", + primary.record.relative_path + )), + result_excerpt: truncate_context_excerpt(&defect_description, 320), + action_chips: Vec::new(), + }) +} + +#[cfg(test)] +mod debugging_summarizer_tests { + use super::*; + use crate::domain::artifact::ArtifactRecord; + use crate::persistence::store::PersistedArtifact; + + #[test] + fn test_summarize_debugging_mode_result_missing_description() { + let artifact = PersistedArtifact { + record: ArtifactRecord { + file_name: "01-context-map.md".to_string(), + relative_path: "debugging/01-context-map.md".to_string(), + format: crate::domain::artifact::ArtifactFormat::Markdown, + provenance: None, + }, + contents: "# Context Map\nNo description here.".to_string(), + }; + let summary = summarize_debugging_mode_result(&[artifact]).unwrap(); + assert!(summary.result_excerpt.contains("NOT CAPTURED")); + assert!(summary.artifact_packet_summary.contains("Defect description is missing")); + } +} diff --git a/defaults/embedded-skills/canon-shared/references/runtime-compatibility.toml b/defaults/embedded-skills/canon-shared/references/runtime-compatibility.toml index af98b8de..87b97369 100644 --- a/defaults/embedded-skills/canon-shared/references/runtime-compatibility.toml +++ b/defaults/embedded-skills/canon-shared/references/runtime-compatibility.toml @@ -1,5 +1,5 @@ [canon] -expected_workspace_version = "0.64.0" +expected_workspace_version = "0.65.0" install_command = "follow the install guide at https://github.com/apply-the/canon#install" install_guidance_ref = "https://github.com/apply-the/canon#install" release_surface = "https://github.com/apply-the/canon/releases" diff --git a/docs/governance/evidence.md b/docs/governance/evidence.md index 2c956ad3..694afb81 100644 --- a/docs/governance/evidence.md +++ b/docs/governance/evidence.md @@ -49,7 +49,7 @@ Typical meanings: - approval rejected when downstream use should stop - approval expired when prior approval no longer applies -Exact machine-facing values are part of the adapter contract. Canonical source: [Governance Adapter Integration](https://github.com/apply-the/canon/blob/0.63.1/tech-docs/integration/governance-adapter.md). +Exact machine-facing values are part of the adapter contract. Canonical source: [Governance Adapter Integration](https://github.com/apply-the/canon/blob/0.65.0/tech-docs/integration/governance-adapter.md). ## Readiness States diff --git a/docs/guide/canon-modes.md b/docs/guide/canon-modes.md index 7d97920e..5f9c9b95 100644 --- a/docs/guide/canon-modes.md +++ b/docs/guide/canon-modes.md @@ -32,6 +32,7 @@ Below is a list of the primary modes available in Canon, based on the default te ### Operations - **`incident`**: Capture incident reports, root cause analyses, and remediation actions. +- **`debugging`**: Systematic troubleshooting and root cause isolation with red-to-green verification. - **`system-shaping`**: Govern broad, cross-cutting structural adjustments or organizational engineering alignments. ## Choosing a Mode diff --git a/docs/guide/first-packet.md b/docs/guide/first-packet.md index a688055b..75a87940 100644 --- a/docs/guide/first-packet.md +++ b/docs/guide/first-packet.md @@ -2,7 +2,7 @@ Get Canon running in your repository in 5 minutes. -This quick start tracks `0.63.1`. +This quick start tracks `0.65.0`. ## 1. Install diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 40a46c74..25903e07 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -4,7 +4,7 @@ For a 5-minute technical setup with commands, use the [[Quick Start|Quick-Start] This path is for a user who wants to understand Canon by running one governed packet from authored input to inspection and publication. -This page is maintained against `0.63.1`. +This page is maintained against `0.65.0`. ## What Canon Is diff --git a/docs/guide/installation.md b/docs/guide/installation.md index d5448bd9..f97e5aac 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -5,7 +5,7 @@ This page explains what needs to exist before Canon can produce governed packets ## CLI Installation Canon ships as a single binary named `canon`. This site content is aligned with -**0.63.1**. +**0.65.0**. Use the repository README as the canonical source for current installation channels: diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md index d02eac3a..b3f75c37 100644 --- a/docs/guide/introduction.md +++ b/docs/guide/introduction.md @@ -1,9 +1,9 @@ # Canon > [!TIP] -> This wiki is aligned with **Canon 0.63.1**. For older versions, refer to the repository tags. +> This wiki is aligned with **Canon 0.65.0**. For older versions, refer to the repository tags. -![Canon - Semantic Governance Runtime](https://github.com/apply-the/canon/blob/0.63.1/tech-docs/images/canon-banner.jpg) +![Canon - Semantic Governance Runtime](https://github.com/apply-the/canon/blob/0.65.0/tech-docs/images/canon-banner.jpg?raw=true) **The governance runtime for AI-assisted engineering.** Keep AI agents bounded, inspectable, and safely restricted to approved work zones. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index cb568094..9a6fda9e 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -51,6 +51,7 @@ defines typed selector, `minimum_independence`, `confidence_handoff`, | `architecture` | Capture bounded structural decisions. | | `backlog` | Decompose approved upstream knowledge into delivery slices. | | `change` | Frame bounded modification in an existing system. | +| `debugging` | Systematic troubleshooting and root cause isolation with red-to-green verification. | | `implementation` | Guide execution for an approved delivery slice. | | `refactor` | Improve structure without expanding feature scope. | | `review` | Assess non-PR artifacts with findings-first posture. | diff --git a/docs/roadmap/index.md b/docs/roadmap/index.md index 7744c839..7686cd49 100644 --- a/docs/roadmap/index.md +++ b/docs/roadmap/index.md @@ -8,8 +8,8 @@ The goal of Canon is to transform AI-assisted engineering activity into a proces ## Upcoming Features & Topics -### Current 0.63.1 -- **Guided `pr-review` Ref Choice**: Canon 0.63.1 now hardens `canon-pr-review` intake with a guided comparison choice when base/head refs are missing or semantically unclear, while preserving host-rich-input support and plain guided-text fallback. +### Current 0.65.0 +- **Systematic Debugging Mode**: Canon 0.65.0 introduces the systematic debugging mode, establishing a green-zone workflow with a five-phase verification gate (context, reproduction, root-cause, fix-decision, and verification) for tracking codebase incidents. ### Recently Landed - **Assistant Surface Expansion**: `canon init` now supports Cursor and Antigravity alongside the existing assistant targets, while repository-level assistant package metadata remains aligned with the current release. diff --git a/package.json b/package.json index a0ce8b7c..d3a240e7 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "version": "1.0.0", "type": "module", "scripts": { - "site:dev": "vitepress dev site", - "site:build": "vitepress build site", - "site:preview": "vitepress preview site" + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" }, "devDependencies": { "@types/svg-pan-zoom": "^3.3.0", diff --git a/roadmap/features/01-systematic-debugging.md b/roadmap/features/01-systematic-debugging.md deleted file mode 100644 index 943bfb2d..00000000 --- a/roadmap/features/01-systematic-debugging.md +++ /dev/null @@ -1,86 +0,0 @@ -# 01 - Systematic Debugging - -## Problem -Currently, day-to-day bug fixes are routed through generic `change` or `implementation` modes. These modes do not strictly enforce debugging best practices. The `incident` mode exists, but it is tailored for reactive production crisis management (blast radius, containment), not standard systematic troubleshooting. - -## Proposal -Introduce a dedicated `debugging` or `bugfix` mode inspired by systematic troubleshooting methodologies. It should enforce rigid packet gates before allowing code changes: -1. **Reproduction Gate**: The packet must provide verifiable evidence that the bug has been reproduced. -2. **TDD Gate**: The packet must include a failing (red) diagnostic test before altering production code. -3. **Root Cause Analysis**: The fix must be explicitly linked to the root cause identified during reproduction. - -## Risk Profile - -**Governance Zone**: Green (self-contained, no systemic mutation). -A debugging packet modifies only the narrowly scoped fix target and its -associated test harness. No policy, runtime rule, or cross-mode contract is -affected. - -## Why Existing Modes Are Not Enough -- `change` assumes the boundary and preserved invariants are already understood. -- `implementation` assumes the task map is already approved and the work is no - longer exploratory. -- `incident` optimizes for containment and follow-up, not for disciplined - reproduction and source-level fault isolation. - -## Dependencies - -- **02 - Completion Verification Gates**: once landed, the debugging mode - inherits the fresh-proof requirement automatically rather than implementing its - own verification step. -- No hard prerequisite; this feature can start immediately. - -## Related Modes - -| Existing Mode | Relationship | -|---|---| -| `incident` | Adjacent but non-overlapping: `incident` is reactive crisis management; `debugging` is deliberate fault isolation. | -| `change` | Downstream: a proven fix may be packeted as a `change` for review. | -| `verification` | Consumed: the red/green harness is a specialized verification surface. | -| `implementation` | Boundary: if the fix grows beyond one scope, escalate to `implementation`. | - -## Entry Gates -- A concrete symptom must exist: failing test, failing command, broken user - path, or stack trace with enough detail to reproduce or to explain why - reproduction is currently blocked. -- The packet must name the affected surface and the currently suspected blast - radius before any fix is proposed. -- If the symptom cannot yet be reproduced consistently, the run remains in - evidence-gathering state and cannot advance to a production-code fix. - -## Operational Mechanics -- **Inputs**: Stack traces, user bug reports, failing CI logs (`bug-report.md`). -- **Workflow Steps**: - 1. **Hypothesis Generation**: The system evaluates the report and proposes 2-3 isolated failure hypotheses *before* browsing code extensively. - 2. **Reproduction Harness (Red State)**: The agent generates a minimal failing test (`reproduction_test.rs` or a discrete script) and records the `FAIL` evidence. - 3. **Isolation & Fix (Green State)**: The agent applies the fix exclusively to production code matching the root cause. - 4. **Verification**: The agent records the `PASS` evidence for the reproduction harness alongside confirming no regressions in the broader test suite. - *(Note: Actual execution of these tests and orchestrator mechanics are deferred to the Boundline runtime; Canon purely validates the presence of the resulting `evidence_ref` and root-cause packet.)* -- **Required Artifacts**: `debugging-trace.md` (which documents the hypothesis, the reproduction steps, and the precise fix rationale) to be included in the final change packet. - -## Exit Gates -- The packet must preserve one explicit root-cause statement, not merely a list - of attempted fixes. -- The red-state reproduction must be captured before the green-state fix is - accepted. -- The claimed fix must pass the reproduction harness plus one nearby regression - or confidence check scoped to the affected surface. - -## Packet Shape -- `01-context.md`: symptom, affected surface, suspected blast radius. -- `02-reproduction.md`: exact reproduction steps, failing commands, observed - evidence. -- `03-root-cause.md`: traced source of failure and rejected hypotheses. -- `04-fix-decision.md`: bounded fix, tradeoffs, and why adjacent changes were - not taken. -- `05-verification.md`: red/green proof plus any remaining uncertainty. - -## Success Criteria - -- Bug-fix packets routed through this mode achieve first-time resolution (no - reopen within the same cycle) at a measurably higher rate than generic - `change` packets. -- Every closed debugging packet contains a traceable root-cause statement linked - to the reproduction harness. -- No debugging packet closes without a passing red-to-green transition captured - in evidence. \ No newline at end of file diff --git a/roadmap/joint-roadmap-graph.md b/roadmap/joint-roadmap-graph.md index 2c211082..d87d69a4 100644 --- a/roadmap/joint-roadmap-graph.md +++ b/roadmap/joint-roadmap-graph.md @@ -67,6 +67,7 @@ flowchart TD B07 -.-> B17 C06 -.->|Design for| B08 + C02 -.->|Inherits rules| C01 B18 -.->|Enhances| C01 C05 -.->|Pairs well| C06 ``` @@ -86,4 +87,5 @@ flowchart TD 6. **Canon 07 (After provider setup)** - Arrives at the end to close the loop on the CLI side (Canon init) by gathering local routing choices, delegating execution back to Boundline. 7. **Independent Features (Canon 01, 04, 05, 06 & Boundline 08-12, 16)** - - These features cover autonomous workflows, policy, observability, and advanced orchestrator additions. They do not block the core engine loop and can be parallelized based on priority. + - These features cover autonomous workflows, policy, observability, and advanced orchestrator additions. They do not block the core engine loop and can be parallelized based on priority. + - *(Note on Canon 01: It has a soft dependency on Canon 02. While it can start immediately without hard blockers, once Canon 02 lands, Canon 01 will automatically inherit its rigid verification gates).* diff --git a/scripts/site-dev.ps1 b/scripts/docs-dev.ps1 similarity index 100% rename from scripts/site-dev.ps1 rename to scripts/docs-dev.ps1 diff --git a/scripts/site-dev.sh b/scripts/docs-dev.sh similarity index 91% rename from scripts/site-dev.sh rename to scripts/docs-dev.sh index 80076d03..f3fb3664 100755 --- a/scripts/site-dev.sh +++ b/scripts/docs-dev.sh @@ -6,4 +6,4 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR/.." echo "Starting Canon VitePress dev server..." -npm run site:dev +npm run docs:dev diff --git a/scripts/update-docs-versions.ps1 b/scripts/update-docs-versions.ps1 new file mode 100644 index 00000000..59cfa883 --- /dev/null +++ b/scripts/update-docs-versions.ps1 @@ -0,0 +1,50 @@ +# Update documentation version references based on Cargo.toml version + +if (-not (Test-Path "Cargo.toml")) { + Write-Error "Cargo.toml not found in the current directory." + exit 1 +} + +if (-not (Test-Path "docs")) { + Write-Warning "docs/ directory not found. Skipping." + exit 0 +} + +$cargoToml = Get-Content -Raw -Path "Cargo.toml" +$version = $null + +# Try to find version in workspace.package first +if ($cargoToml -match '(?s)\[workspace\.package\].*?version\s*=\s*"([^"]+)"') { + $version = $Matches[1] +} elseif ($cargoToml -match '(?s)\[package\].*?version\s*=\s*"([^"]+)"') { + $version = $Matches[1] +} + +if (-not $version) { + Write-Error "Could not extract version from Cargo.toml." + exit 1 +} + +Write-Host "Updating documentation references in docs/ to version: $version" + +$files = Get-ChildItem -Path "docs" -Filter "*.md" -Recurse +foreach ($file in $files) { + $content = Get-Content -Raw -Path $file.FullName + + # Check if we have work to do before writing + if ($content -match 'blob/\d+\.\d+\.\d+|tree/\d+\.\d+\.\d+|Canon \d+\.\d+\.\d+|Boundline \d+\.\d+\.\d+') { + $newContent = $content -replace 'blob/\d+\.\d+\.\d+', "blob/$version" + $newContent = $newContent -replace 'tree/\d+\.\d+\.\d+', "tree/$version" + $newContent = $newContent -replace 'Canon \d+\.\d+\.\d+', "Canon $version" + $newContent = $newContent -replace 'Boundline \d+\.\d+\.\d+', "Boundline $version" + + if ($content -ne $newContent) { + # Write back using UTF-8 (No BOM) + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText($file.FullName, $newContent, $utf8NoBom) + Write-Host " Updated: $($file.FullName)" + } + } +} + +Write-Host "Done!" diff --git a/scripts/update-docs-versions.sh b/scripts/update-docs-versions.sh new file mode 100755 index 00000000..ca1a9769 --- /dev/null +++ b/scripts/update-docs-versions.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Ensure we are in a rust workspace root with Cargo.toml +if [ ! -f Cargo.toml ]; then + echo "Error: Cargo.toml not found in the current directory." >&2 + exit 1 +fi + +if [ ! -d docs ]; then + echo "Warning: docs/ directory not found. Skipping." + exit 0 +fi + +# Extract version from Cargo.toml +VERSION=$(grep -A 10 "\[workspace.package\]" Cargo.toml | grep -E '^version\s*=\s*' | cut -d '"' -f 2 || true) +if [ -z "$VERSION" ]; then + # Fallback to standard [package] + VERSION=$(grep -A 10 "\[package\]" Cargo.toml | grep -E '^version\s*=\s*' | cut -d '"' -f 2 || true) +fi + +if [ -z "$VERSION" ]; then + echo "Error: Could not extract version from Cargo.toml." >&2 + exit 1 +fi + +echo "Updating documentation references in docs/ to version: $VERSION" + +# Find and update markdown files recursively using perl for portability across macOS and Linux +find docs -type f -name "*.md" | while read -r file; do + # Check if the file contains any of our target patterns to avoid unnecessary writes + if grep -q -E "blob/[0-9]+\.[0-9]+\.[0-9]+|tree/[0-9]+\.[0-9]+\.[0-9]+|Canon [0-9]+\.[0-9]+\.[0-9]+|Boundline [0-9]+\.[0-9]+\.[0-9]+" "$file"; then + perl -pi -e "s|blob/\d+\.\d+\.\d+|blob/$VERSION|g" "$file" + perl -pi -e "s|tree/\d+\.\d+\.\d+|tree/$VERSION|g" "$file" + perl -pi -e "s|Canon \d+\.\d+\.\d+|Canon $VERSION|g" "$file" + perl -pi -e "s|Boundline \d+\.\d+\.\d+|Boundline $VERSION|g" "$file" + echo " Updated: $file" + fi +done + +echo "Done!" diff --git a/scripts/update-versions.ps1 b/scripts/update-versions.ps1 new file mode 100644 index 00000000..5d42fa55 --- /dev/null +++ b/scripts/update-versions.ps1 @@ -0,0 +1,29 @@ +$ErrorActionPreference = "Stop" + +# Get workspace version from Cargo.toml +$cargoToml = Get-Content -Path "Cargo.toml" -Raw +if ($cargoToml -match '(?m)^version\s*=\s*"([^"]+)"') { + $version = $matches[1] +} else { + Write-Error "Could not find version in Cargo.toml" + exit 1 +} + +Write-Host "Updating documentation references to version $version..." + +$files = Get-ChildItem -Path . -Recurse -Include *.md,*.rs,*.toml -Exclude "target","node_modules",".git","cache","dist" | Where-Object { -not $_.DirectoryName.Contains("\target\") -and -not $_.DirectoryName.Contains("\node_modules\") -and -not $_.DirectoryName.Contains("\.git\") } + +foreach ($file in $files) { + $content = Get-Content -Path $file.FullName -Raw + $newContent = $content -replace 'blob/\d+\.\d+\.\d+', "blob/$version" + $newContent = $newContent -replace 'tree/\d+\.\d+\.\d+', "tree/$version" + $newContent = $newContent -replace 'raw/\d+\.\d+\.\d+', "raw/$version" + $newContent = $newContent -replace 'Boundline \d+\.\d+\.\d+', "Boundline $version" + $newContent = $newContent -replace 'Canon \d+\.\d+\.\d+', "Canon $version" + + if ($content -cne $newContent) { + Set-Content -Path $file.FullName -Value $newContent -NoNewline + } +} + +Write-Host "Version update complete." diff --git a/scripts/update-versions.sh b/scripts/update-versions.sh new file mode 100755 index 00000000..c30be7cf --- /dev/null +++ b/scripts/update-versions.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + +# Get workspace version from Cargo.toml +VERSION=$(grep -m1 '^version =' Cargo.toml | sed 's/.*"\(.*\)".*/\1/') + +if [ -z "$VERSION" ]; then + echo "Error: Could not find version in Cargo.toml" + exit 1 +fi + +echo "Updating documentation references to version $VERSION..." + +find . -type f \( -name "*.md" -o -name "*.rs" -o -name "*.toml" \) \ + -not -path "*/target/*" \ + -not -path "*/node_modules/*" \ + -not -path "*/.git/*" \ + -not -path "*/.vitepress/cache/*" \ + -not -path "*/.vitepress/dist/*" \ + -exec perl -pi -e "s|blob/[0-9]+\.[0-9]+\.[0-9]+|blob/$VERSION|g; \ + s|tree/[0-9]+\.[0-9]+\.[0-9]+|tree/$VERSION|g; \ + s|raw/[0-9]+\.[0-9]+\.[0-9]+|raw/$VERSION|g; \ + s|Boundline [0-9]+\.[0-9]+\.[0-9]+|Boundline $VERSION|g; \ + s|Canon [0-9]+\.[0-9]+\.[0-9]+|Canon $VERSION|g" {} + + +echo "Version update complete." diff --git a/specs/063-interactive-init-ui/tasks.md b/specs/063-interactive-init-ui/tasks.md index 7221d287..15dd48e3 100644 --- a/specs/063-interactive-init-ui/tasks.md +++ b/specs/063-interactive-init-ui/tasks.md @@ -109,7 +109,7 @@ description: "Task list for implementing the interactive init experience" **Purpose**: Finalize release metadata, update documentation surfaces, capture validation evidence, and close out independent review for the shipped slice. - [X] T025 [P] Bump the workspace release version and release notes in Cargo.toml and CHANGELOG.md -- [X] T026 [P] Update operator-facing docs, site, and roadmap content in README.md, tech-docs/guides/getting-started.md, site/guide/getting-started.md, site/roadmap/index.md, and ROADMAP.md +- [X] T026 [P] Update operator-facing docs, docs, and roadmap content in README.md, tech-docs/guides/getting-started.md, docs/guide/getting-started.md, docs/roadmap/index.md, and ROADMAP.md - [X] T027 [P] Align feature quickstart and CLI contract wording with the shipped behavior in specs/063-interactive-init-ui/quickstart.md and specs/063-interactive-init-ui/contracts/canon-init-cli-contract.md - [X] T028 Run `cargo fmt`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, targeted `cargo test`, and patch coverage validation via scripts/common/coverage/intersect_patch_coverage.py against lcov.info so every changed Rust source file, including crates/canon-cli/src/app.rs, crates/canon-cli/src/commands/init.rs, and crates/canon-cli/src/tui/terminal.rs, finishes above 95% coverage - [X] T029 Capture validation evidence in specs/063-interactive-init-ui/validation-report.md, including 10 first-attempt usability runs from clean temporary workspaces, pass or fail counts against the 9-of-10 success target, whether any external documentation was consulted, quickstart walkthrough notes, terminal-recovery checks, non-interactive regression results, 10-keypress reachability results, repo quality-gate outcomes, and changed-Rust-file coverage evidence diff --git a/specs/067-systematic-debugging/checklists/requirements.md b/specs/067-systematic-debugging/checklists/requirements.md new file mode 100644 index 00000000..f9efe561 --- /dev/null +++ b/specs/067-systematic-debugging/checklists/requirements.md @@ -0,0 +1,38 @@ +# Specification Quality Checklist: Systematic Debugging Mode + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-06-02 +**Feature**: [spec.md](file:///Users/rt/workspace/apply-the/canon/specs/067-systematic-debugging/spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for the intended artifact audience +- [x] Governance context is explicit (mode, risk, scope, invariants) +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Non-goals are explicit +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] Validation plan separates generation from validation +- [x] Decision log seed exists +- [x] No implementation details leak into specification + +## Notes + +- All items pass checklist criteria initially since they were rigorously mapped from the detailed roadmap proposal. diff --git a/specs/067-systematic-debugging/data-model.md b/specs/067-systematic-debugging/data-model.md new file mode 100644 index 00000000..e94bec73 --- /dev/null +++ b/specs/067-systematic-debugging/data-model.md @@ -0,0 +1,23 @@ +# Data Model: Systematic Debugging Mode + +The system relies on file-based payloads within the `.canon/` execution workspace rather than traditional relational schemas. + +## Entities + +### Debugging Packet + +A debugging packet consists of the following structure: +- `01-context.md`: Contains symptom, affected surface, suspected blast radius. +- `02-reproduction.md`: Exact reproduction steps, failing commands, observed evidence. +- `03-root-cause.md`: Traced source of failure and rejected hypotheses. +- `04-fix-decision.md`: Bounded fix, tradeoffs, and why adjacent changes were not taken. +- `05-verification.md`: Red/green proof plus any remaining uncertainty. + +### Execution Evidence +- `debugging-trace.md`: Documentation of hypotheses, reproduction steps, and precise fix rationale to be included in the final change packet. + +## State Transitions +1. **Hypothesis Generation**: Evaluates bug report and proposes 2-3 isolated failure hypotheses. +2. **Reproduction Harness (Red State)**: Minimal failing test generated, `FAIL` evidence recorded. +3. **Isolation & Fix (Green State)**: Fix is applied exclusively to code matching root cause. +4. **Verification**: `PASS` evidence recorded alongside regression suite confirmation. diff --git a/specs/067-systematic-debugging/decision-log.md b/specs/067-systematic-debugging/decision-log.md new file mode 100644 index 00000000..8e6b951d --- /dev/null +++ b/specs/067-systematic-debugging/decision-log.md @@ -0,0 +1,4 @@ +# Decision Log: Systematic Debugging Mode + +- **Decision 1**: Introduce a dedicated `debugging` mode rather than extending `change` or `incident`. + - **Rationale**: The `change` mode assumes boundaries and invariants are already understood. The `incident` mode optimizes for crisis containment and follow-up rather than disciplined source-level fault isolation. The `debugging` mode enforces systematic methodology (reproduction -> TDD -> fix -> verify). diff --git a/specs/067-systematic-debugging/plan.md b/specs/067-systematic-debugging/plan.md new file mode 100644 index 00000000..f460fc88 --- /dev/null +++ b/specs/067-systematic-debugging/plan.md @@ -0,0 +1,72 @@ +# Implementation Plan: Systematic Debugging Mode + +**Branch**: `067-systematic-debugging` | **Date**: 2026-06-02 | **Spec**: [spec.md](file:///Users/rt/workspace/apply-the/canon/specs/067-systematic-debugging/spec.md) + +**Input**: Feature specification from `/specs/067-systematic-debugging/spec.md` + +## Summary + +Implement a dedicated `debugging` mode for systematic bug fixes, featuring rigid packet gates (reproduction, TDD, root cause) and generating a `debugging-trace.md` artifact. + +## Technical Context + +**Language/Version**: Rust 1.96.0, Edition 2024 + +**Primary Dependencies**: clap, serde, serde_json, serde_yaml, tracing, thiserror + +**Storage**: Local filesystem under `.canon/` + +**Testing**: cargo test, cargo nextest run, assert_cmd + +**Target Platform**: CLI/macOS/Linux + +**Project Type**: CLI / orchestrator library + +**Performance Goals**: N/A + +**Constraints**: Must integrate with existing Canon mode orchestration and gate validation. + +**Scale/Scope**: System-level debugging runs and packet validations. + +## Governance Context + +- **Execution Mode**: `debugging` +- **Risk Classification**: Green (self-contained, no systemic mutation). Modifies only the narrowly scoped fix target and associated test harness. +- **Scope-In**: Dedicated mode for systematic bug fixes with rigid packet gates including Reproduction Gate, TDD Gate, and Root Cause Analysis. +- **Scope-Out**: Reactive production crisis management (`incident` mode) and general architecture changes (`implementation` or `change`). +- **Invariants**: 1. A failing diagnostic test must be recorded as evidence before production code is altered. 2. The applied fix must be explicitly linked to the identified root cause. +- **Validation Ownership**: Evidence of Red and Green state transitions must be generated by a testing harness and recorded in the run manifest. + +## Constitution Check + +*GATE: Passed* +- Clean Code & Modularity: Will adhere. +- No panic-prone control flow. +- Will not introduce magic strings. + +## Project Structure + +### Documentation (this feature) + +```text +specs/067-systematic-debugging/ +├── plan.md # This file +├── research.md # Phase 0 output +├── data-model.md # Phase 1 output +├── quickstart.md # Phase 1 output +├── decision-log.md # Phase 1 output +├── validation-report.md # Phase 1 output +└── tasks.md # Phase 2 output +``` + +### Source Code (repository root) + +```text +crates/ +└── canon-engine/ + └── src/ + └── modes/ + └── debugging.rs # New mode implementation +``` + +**Structure Decision**: Integrated directly into `canon-engine` under `modes` to align with the existing Canon architecture for modes. diff --git a/specs/067-systematic-debugging/quickstart.md b/specs/067-systematic-debugging/quickstart.md new file mode 100644 index 00000000..5d00c15e --- /dev/null +++ b/specs/067-systematic-debugging/quickstart.md @@ -0,0 +1,18 @@ +# Quickstart: Systematic Debugging Mode + +## Triggering the workflow + +To start a debugging session via Canon: + +```bash +canon --mode debugging +``` + +This ensures the rigid packet gates (Reproduction Gate, TDD Gate, Root Cause Analysis) are activated. + +## Workflow Overview + +1. Supply a symptom or bug report. The mode will propose 2-3 isolated failure hypotheses. +2. Write/generate a minimal failing test to reproduce the bug. The system will record the `FAIL` evidence (Red State). +3. Apply the fix. The fix must be explicitly linked to the identified root cause. +4. The system will verify the fix by checking the `PASS` evidence (Green State) and verifying there are no regressions. diff --git a/specs/067-systematic-debugging/research.md b/specs/067-systematic-debugging/research.md new file mode 100644 index 00000000..5f1b8105 --- /dev/null +++ b/specs/067-systematic-debugging/research.md @@ -0,0 +1,7 @@ +# Research: Systematic Debugging Mode + +## Findings + +No unresolved clarifications were found in the Technical Context. +The implementation of the `debugging` mode directly builds upon Canon's existing mode definitions and packet validations, utilizing established boundaries. +There are no new libraries or frameworks to research. diff --git a/specs/067-systematic-debugging/spec.md b/specs/067-systematic-debugging/spec.md new file mode 100644 index 00000000..f03fb381 --- /dev/null +++ b/specs/067-systematic-debugging/spec.md @@ -0,0 +1,93 @@ +# Feature Specification: Systematic Debugging Mode + +**Feature Branch**: `067-systematic-debugging` + +**Created**: 2026-06-02 + +**Status**: Draft + +**Input**: User description: "Implement systematic debugging mode as described in roadmap/features/01-systematic-debugging.md" + +## Governance Context + +- **Execution Mode**: `debugging` +- **Risk Classification**: Green (self-contained, no systemic mutation). Modifies only the narrowly scoped fix target and associated test harness. +- **Scope-In**: + - Dedicated mode for systematic bug fixes. + - Rigid packet gates including Reproduction Gate, TDD Gate, and Root Cause Analysis. + - Generation of debugging trace and specific packet artifacts. +- **Scope-Out**: + - Reactive production crisis management (which belongs to `incident` mode). + - General architecture changes or feature additions (which belong to `implementation` or `change`). +- **Invariants**: + 1. A failing diagnostic test (Red State) must be recorded as evidence before any production code is altered. + 2. The applied fix must be explicitly linked to the identified root cause. +- **Validation Ownership**: Evidence of the Red and Green state transitions must be generated by a testing harness and recorded in the run manifest. + +## Clarifications + +### Session 2026-06-02 + +- Q: If a bug cannot be reproduced consistently, what should happen to the debugging run? → A: Escalate to human review after 3 failed reproduction attempts + +## User Scenarios & Testing + +### User Story 1 - Systematic Resolution of a Defect (Priority: P1) + +Developers facing a reproducible bug need a systematic way to reproduce the defect, propose hypotheses, verify the failure, and apply a fix, so that the fix is verified to address the root cause without regressions. + +**Why this priority**: It is the core and only purpose of this new feature mode. + +**Independent Test**: Can be independently tested by feeding a known defect (e.g., a stack trace) into the mode and verifying the packet constraints are enforced. + +**Acceptance Scenarios**: + +1. **Given** a symptom or bug report, **When** starting the `debugging` mode, **Then** the system proposes 2-3 isolated failure hypotheses. +2. **Given** failure hypotheses, **When** attempting to reproduce the bug, **Then** the system generates a minimal failing test (Red State) and records `FAIL` evidence. +3. **Given** a recorded Red State, **When** applying the fix, **Then** the fix is bounded to the root cause. +4. **Given** an applied fix, **When** verifying, **Then** the system records `PASS` evidence for the harness (Green State) along with regression checks. + +### Edge Cases + +- **Inconsistent Reproduction**: If the system fails to generate a consistently failing test after 3 attempts, it MUST escalate to human review rather than remaining indefinitely in the evidence-gathering state or auto-closing. + +## Requirements + +### Functional Requirements + +- **FR-001**: System MUST enforce a Reproduction Gate requiring verifiable evidence of bug reproduction before accepting a fix. +- **FR-002**: System MUST enforce a TDD Gate requiring a failing diagnostic test or script prior to altering production code. +- **FR-003**: System MUST enforce Root Cause Analysis, explicitly linking the fix to the identified root cause. +- **FR-004**: System MUST guide the workflow through: Hypothesis Generation, Reproduction Harness (Red State), Isolation & Fix, and Verification. +- **FR-005**: System MUST produce the required artifact `debugging-trace.md` in the final change packet. +- **FR-006**: System MUST enforce the packet shape comprising: `01-context.md`, `02-reproduction.md`, `03-root-cause.md`, `04-fix-decision.md`, and `05-verification.md`. +- **FR-007**: System MUST prevent the packet from closing if the red-to-green transition is not captured in the evidence. + +## Success Criteria + +### Measurable Outcomes + +- **SC-001**: Bug-fix packets routed through this mode achieve first-time resolution at a measurably higher rate than generic change packets. +- **SC-002**: Every closed debugging packet contains a traceable root-cause statement linked to the reproduction harness. +- **SC-003**: No debugging packet closes without a passing red-to-green transition captured in evidence. + +## Validation Plan + +- **Structural Validation**: Verify the final packet includes all mandatory shape files (`01-context.md` through `05-verification.md`). +- **Logical Validation**: Ensure the transition from Red state (failing tests) to Green state (passing tests) is chronologically proven by evidence. +- **Independent Validation**: Validate through a CI pipeline or canon validation rules that the verification gates cannot be bypassed. + +## Decision Log + +- **Decision 1**: Introduce a dedicated `debugging` mode rather than extending `change` or `incident`. + - **Rationale**: The `change` mode assumes boundaries and invariants are already understood. The `incident` mode optimizes for crisis containment and follow-up rather than disciplined source-level fault isolation. The `debugging` mode enforces systematic methodology (reproduction -> TDD -> fix -> verify). + +## Non-Goals + +- Refactoring untested legacy code not related to the immediate defect. +- Handling production incidents that require rapid containment over rigorous reproduction (use `incident` mode). + +## Assumptions + +- Target codebase has a test suite capable of running minimal failing tests (Rust testing frameworks like `cargo test` are supported). +- Stack traces or failure reports provided by users contain sufficient detail to form reproducible hypotheses. diff --git a/specs/067-systematic-debugging/tasks.md b/specs/067-systematic-debugging/tasks.md new file mode 100644 index 00000000..ab389790 --- /dev/null +++ b/specs/067-systematic-debugging/tasks.md @@ -0,0 +1,70 @@ +# Tasks: Systematic Debugging Mode + +**Input**: Design documents from `/specs/067-systematic-debugging/` + +**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md + +**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story. + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (different files, no dependencies) +- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3) +- Include exact file paths in descriptions + +## Phase 0: Governance & Artifacts + +**Purpose**: Mode, risk, scope, and artifact decisions tracking. + +- [x] T001 Bump version in Cargo.toml workspace + +## Phase 1: Setup + +**Purpose**: Project initialization and basic structure + +- [x] T002 Initialize new mode struct and module in crates/canon-engine/src/modes/debugging.rs + +## Phase 2: Foundational (Blocking Prerequisites) + +**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented + +- [x] T003 Register `debugging` mode in crates/canon-engine/src/modes/mod.rs + +## Phase 3: User Story 1 - Systematic Resolution of a Defect (Priority: P1) + +**Goal**: Developers facing a reproducible bug need a systematic way to reproduce the defect, propose hypotheses, verify the failure, and apply a fix, so that the fix is verified to address the root cause without regressions. + +**Independent Test**: Can be independently tested by feeding a known defect (e.g., a stack trace) into the mode and verifying the packet constraints are enforced. + +### Tests for User Story 1 (OPTIONAL - only if tests requested) ⚠️ + +> **NOTE: Write these tests FIRST, ensure they FAIL before implementation** + +- [x] T004 [P] [US1] Integration test for debugging mode in tests/integration/debugging_mode_test.rs ensuring gates are enforced + +### Implementation for User Story 1 + +- [x] T005 [US1] Implement Reproduction Gate and Root Cause linking in crates/canon-engine/src/modes/debugging.rs +- [x] T006 [US1] Enforce required artifacts (01-context, 02-reproduction, 03-root-cause, 04-fix-decision, 05-verification) in crates/canon-engine/src/modes/debugging.rs +- [x] T007 [US1] Integrate with evidence generation and verify red-to-green state transition in crates/canon-engine/src/modes/debugging.rs +- [x] T008 [US1] Generate debugging-trace.md artifact correctly in crates/canon-engine/src/modes/debugging.rs + +**Checkpoint**: At this point, User Story 1 should be fully functional and testable independently + +## Phase 4: Polish & Cross-Cutting Concerns + +**Purpose**: Improvements that affect multiple user stories + +- [x] T009 Ensure 95% of coverage on touched rust files (crates/canon-engine/src/modes/debugging.rs) +- [x] T010 Run `cargo clippy --workspace --all-targets --all-features -- -D warnings` and ensure no issues +- [x] T011 Remove completed feature from roadmap/features/01-systematic-debugging.md +- [x] T012 Update general docs in docs/ or tech-docs/ +- [x] T013 Update CHANGELOG.md with the new debugging mode +- [x] T014 Update README.md to list debugging mode among available modes + +## Dependencies & Execution Order + +- **Setup (Phase 1)**: No dependencies - can start immediately +- **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories +- **User Stories (Phase 3+)**: All depend on Foundational phase completion +- **Polish (Final Phase)**: Depends on all desired user stories being complete diff --git a/specs/067-systematic-debugging/validation-report.md b/specs/067-systematic-debugging/validation-report.md new file mode 100644 index 00000000..b0a20617 --- /dev/null +++ b/specs/067-systematic-debugging/validation-report.md @@ -0,0 +1,7 @@ +# Validation Report: Systematic Debugging Mode + +## Planned Validations + +- **Structural Validation**: Verify the final packet includes all mandatory shape files (`01-context.md` through `05-verification.md`). +- **Logical Validation**: Ensure the transition from Red state (failing tests) to Green state (passing tests) is chronologically proven by evidence. +- **Independent Validation**: Validate through a CI pipeline or canon validation rules that the verification gates cannot be bypassed. diff --git a/tech-docs/guides/modes.md b/tech-docs/guides/modes.md index a9ce6a4e..874edb65 100644 --- a/tech-docs/guides/modes.md +++ b/tech-docs/guides/modes.md @@ -132,6 +132,7 @@ explicitly reroute the packet to `discovery`, `requirements`, or - [`verification`](#mode-verification): challenge claims, evidence, contracts, or quality signals directly. - [`pr-review`](#mode-pr-review): review a real diff or worktree instead of a file-backed authored packet. - [`incident`](#mode-incident): capture incident framing, blast radius, containment, and follow-up actions. +- [`debugging`](#mode-debugging): systematic troubleshooting and root cause isolation with red-to-green verification. - [`security-assessment`](#mode-security-assessment): assess threats, risks, mitigations, and security gaps for an existing system. - [`system-assessment`](#mode-system-assessment): evaluate the current system state, architecture views, and observed or inferred findings. - [`migration`](#mode-migration): plan a bounded move from source to target with sequencing, compatibility, and fallback. diff --git a/tech-docs/integration/governed-reasoning-posture-contract.md b/tech-docs/integration/governed-reasoning-posture-contract.md index 7e8d2586..7c32aeec 100644 --- a/tech-docs/integration/governed-reasoning-posture-contract.md +++ b/tech-docs/integration/governed-reasoning-posture-contract.md @@ -42,8 +42,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/contract/governed_reasoning_posture_contract.rs b/tests/contract/governed_reasoning_posture_contract.rs index 569231a1..08cfe495 100644 --- a/tests/contract/governed_reasoning_posture_contract.rs +++ b/tests/contract/governed_reasoning_posture_contract.rs @@ -29,8 +29,8 @@ const FIXTURE_ROOT: &str = const SUPPORTED_BOUNDLINE_VERSION: &str = "0.63.0"; const SUPPORTED_BOUNDLINE_WINDOW: &str = "0.63.x"; -const SUPPORTED_BOUNDLINE_MAX_EXCLUSIVE: &str = "0.64.0"; -const SUPPORTED_CANON_VERSION: &str = "0.64.0"; +const SUPPORTED_BOUNDLINE_MAX_EXCLUSIVE: &str = "0.65.0"; +const SUPPORTED_CANON_VERSION: &str = "0.65.0"; const SUPPORTED_CANON_WINDOW: &str = "0.64.x"; const SUPPORTED_CANON_MAX_EXCLUSIVE: &str = "0.65.0"; const SUPPORTED_CONTRACT_LINE: &str = "governed_reasoning_posture_v2"; diff --git a/tests/contract/inspect_modes.rs b/tests/contract/inspect_modes.rs index ca1f38ff..1ccb20d8 100644 --- a/tests/contract/inspect_modes.rs +++ b/tests/contract/inspect_modes.rs @@ -49,6 +49,7 @@ fn inspect_modes_returns_the_full_mode_taxonomy() { "supply-chain-analysis", "domain-language", "domain-model", + "debugging", ]) ); } @@ -68,9 +69,11 @@ fn inspect_modes_text_output_keeps_execution_heavy_modes_visible() { assert!(text.contains("migration")); assert!(text.contains("security-assessment")); assert!(text.contains("supply-chain-analysis")); + assert!(text.contains("debugging")); assert_eq!(text.matches("incident").count(), 1); assert_eq!(text.matches("system-assessment").count(), 1); assert_eq!(text.matches("security-assessment").count(), 1); assert_eq!(text.matches("migration").count(), 1); assert_eq!(text.matches("supply-chain-analysis").count(), 1); + assert_eq!(text.matches("debugging").count(), 1); } diff --git a/tests/debugging_run.rs b/tests/debugging_run.rs new file mode 100644 index 00000000..253f47d0 --- /dev/null +++ b/tests/debugging_run.rs @@ -0,0 +1,168 @@ +use std::fs; +use std::process::Command as ProcessCommand; + +use assert_cmd::Command; +use tempfile::TempDir; + +fn cli_command() -> Command { + let mut command = Command::new("cargo"); + command.args([ + "run", + "--quiet", + "--manifest-path", + concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"), + "-p", + "canon-cli", + "--bin", + "canon", + "--", + ]); + command +} + +fn git(workspace: &TempDir, args: &[&str]) { + let output = ProcessCommand::new("git") + .args(args) + .current_dir(workspace.path()) + .output() + .expect("git command"); + assert!( + output.status.success(), + "git {:?} failed: stdout=`{}` stderr=`{}`", + args, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); +} + +fn init_debugging_repo(workspace: &TempDir) { + git(workspace, &["init", "-b", "main"]); + git(workspace, &["config", "user.name", "Canon Test"]); + git(workspace, &["config", "user.email", "canon@example.com"]); + + fs::create_dir_all(workspace.path().join("src")).expect("src dir"); + fs::write(workspace.path().join("src/main.rs"), "fn main() { println!(\"Hello\"); }\n") + .expect("source file"); + + git(workspace, &["add", "."]); + git(workspace, &["commit", "-m", "seed debugging repo"]); +} + +fn debugging_brief() -> &'static str { + "# Debugging Brief\n\n## Context Map\n\nDefect in validation.\n\n## Defect Description\n\nApp crashes on null input.\n\n## Stakeholder Impact\n\nUser can't login.\n\n## Reproduction Harness\n\nSteps:\n1. Run with null.\n2. Crash.\n\n## Red State Verification\n\nVerified it fails.\n\n## Root Cause Isolation\n\nMissing null check.\n\n## Fault Chain\n\nNull passed to unwrap.\n\n## Isolation Proof\n\nStack trace shows it.\n\n## Fix Application\n\nAdd if null.\n\n## Bounded Changes\n\nOnly in validator.rs.\n\n## Invariant Preservation\n\nNo other changes.\n\n## Verification Summary\n\nTests pass.\n\n## Green State\n\nWorks with null.\n\n## No Regression Evidence\n\nAll existing tests pass.\n\nOwner: maintainer\nRisk Level: low-impact\nZone: green\n" +} + +#[test] +fn run_debugging_completes_when_context_is_fully_described() { + let workspace = TempDir::new().expect("temp dir"); + init_debugging_repo(&workspace); + let brief_path = workspace.path().join("debugging.md"); + fs::write(&brief_path, debugging_brief()).expect("brief file"); + + let output = cli_command() + .current_dir(workspace.path()) + .args([ + "run", + "--mode", + "debugging", + "--system-context", + "existing", + "--risk", + "low-impact", + "--zone", + "green", + "--owner", + "maintainer", + "--input", + brief_path.file_name().expect("file name").to_str().expect("utf8"), + "--output", + "json", + ]) + .assert() + .success() + .get_output() + .stdout + .clone(); + + let text = String::from_utf8(output).expect("utf8 stdout"); + let json: serde_json::Value = serde_json::from_str(&text).expect("json output"); + assert_eq!(json["state"], "Completed"); +} + +#[test] +fn run_debugging_blocks_when_required_sections_are_missing() { + let workspace = TempDir::new().expect("temp dir"); + init_debugging_repo(&workspace); + let brief_path = workspace.path().join("debugging.md"); + // Missing body for Defect Description + let bad_brief = "# Debugging Brief\n\n## Context Map\n\nDefect in validation.\n\n## Defect Description\n\n## Stakeholder Impact\n\nUser can't login.\n\n## Reproduction Harness\n\nSteps:\n1. Run with null.\n2. Crash.\n\n## Red State Verification\n\nVerified it fails.\n\n## Root Cause Isolation\n\nMissing null check.\n\n## Fault Chain\n\nNull passed to unwrap.\n\n## Isolation Proof\n\nStack trace shows it.\n\n## Fix Application\n\nAdd if null.\n\n## Bounded Changes\n\nOnly in validator.rs.\n\n## Invariant Preservation\n\nNo other changes.\n\n## Verification Summary\n\nTests pass.\n\n## Green State\n\nWorks with null.\n\n## No Regression Evidence\n\nAll existing tests pass.\n\nOwner: maintainer\nRisk Level: low-impact\nZone: green\n"; + fs::write(&brief_path, bad_brief).expect("brief file"); + + let output = cli_command() + .current_dir(workspace.path()) + .args([ + "run", + "--mode", + "debugging", + "--system-context", + "existing", + "--risk", + "low-impact", + "--zone", + "green", + "--owner", + "maintainer", + "--input", + brief_path.file_name().expect("file name").to_str().expect("utf8"), + "--output", + "json", + ]) + .assert() + .code(2) + .get_output() + .stdout + .clone(); + + let text = String::from_utf8(output).expect("utf8 stdout"); + let json: serde_json::Value = serde_json::from_str(&text).expect("json output"); + assert_eq!(json["state"], "Blocked"); +} + +#[test] +fn run_debugging_blocks_when_missing_other_artifacts() { + let workspace = TempDir::new().expect("temp dir"); + init_debugging_repo(&workspace); + let brief_path = workspace.path().join("debugging.md"); + // Missing all artifacts except context map by removing their headings + let bad_brief = "# Debugging Brief\n\n## Context Map\n\nDefect in validation.\n\n## Defect Description\n\nApp crashes on null input.\n\n## Stakeholder Impact\n\nUser can't login.\n\nOwner: maintainer\nRisk Level: low-impact\nZone: green\n"; + fs::write(&brief_path, bad_brief).expect("brief file"); + + let output = cli_command() + .current_dir(workspace.path()) + .args([ + "run", + "--mode", + "debugging", + "--system-context", + "existing", + "--risk", + "low-impact", + "--zone", + "green", + "--owner", + "maintainer", + "--input", + brief_path.file_name().expect("file name").to_str().expect("utf8"), + "--output", + "json", + ]) + .assert() + .code(2) + .get_output() + .stdout + .clone(); + + let text = String::from_utf8(output).expect("utf8 stdout"); + let json: serde_json::Value = serde_json::from_str(&text).expect("json output"); + assert_eq!(json["state"], "Blocked"); +} diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-compatibility-window.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-compatibility-window.toml index 26736b8c..3fe9cbb3 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-compatibility-window.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-compatibility-window.toml @@ -10,7 +10,7 @@ publication_status = "active" boundline_min = "0.62.0" boundline_max_exclusive = "0.63.0" canon_min = "0.63.1" -canon_max_exclusive = "0.64.0" +canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v1" [profile_selector] diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-missing-block.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-missing-block.toml index eba5c561..71518443 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-missing-block.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-missing-block.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-none-contradictory.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-none-contradictory.toml index e5e6f26f..a2dcb315 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-none-contradictory.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-none-contradictory.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-required-missing-fields.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-required-missing-fields.toml index 55d375da..0c6d9a5e 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-required-missing-fields.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-confidence-required-missing-fields.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-contradictory.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-contradictory.toml index 9fe6c1cd..17677b67 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-contradictory.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-contradictory.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-guidance-override.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-guidance-override.toml index 336f3989..e9b50c92 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-guidance-override.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-guidance-override.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-impossible-minima.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-impossible-minima.toml index 08b27d6e..5b280e60 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-impossible-minima.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-impossible-minima.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-missing-block.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-missing-block.toml index 4f8e87db..4ae2b387 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-missing-block.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-independence-missing-block.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-contradictory.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-contradictory.toml index a9fe4892..3d65a5a7 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-contradictory.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-contradictory.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-incompatible-handoff.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-incompatible-handoff.toml index ad7abc10..2ca86ece 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-incompatible-handoff.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-incompatible-handoff.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-block.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-block.toml index 1b909103..490bd274 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-block.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-block.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-reference-kind.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-reference-kind.toml index 65d6f712..11a33fbf 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-reference-kind.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-missing-reference-kind.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-stale.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-stale.toml index fc5b68d5..7e3f3e5b 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-stale.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-provenance-stale.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-both-present.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-both-present.toml index 080b528a..7499f19a 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-both-present.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-both-present.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-neither-present.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-neither-present.toml index 4872bc50..fca6845f 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-neither-present.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-selector-neither-present.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/invalid-unsupported-vocabulary.toml b/tests/fixtures/governed_reasoning_posture_v2/invalid-unsupported-vocabulary.toml index 8afe35fb..dcc045af 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/invalid-unsupported-vocabulary.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/invalid-unsupported-vocabulary.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2" diff --git a/tests/fixtures/governed_reasoning_posture_v2/valid-v2-posture.toml b/tests/fixtures/governed_reasoning_posture_v2/valid-v2-posture.toml index 3c0fa8e5..ee8d7970 100644 --- a/tests/fixtures/governed_reasoning_posture_v2/valid-v2-posture.toml +++ b/tests/fixtures/governed_reasoning_posture_v2/valid-v2-posture.toml @@ -8,8 +8,8 @@ publication_status = "active" [compatibility_window] boundline_min = "0.63.0" -boundline_max_exclusive = "0.64.0" -canon_min = "0.64.0" +boundline_max_exclusive = "0.65.0" +canon_min = "0.65.0" canon_max_exclusive = "0.65.0" contract_line = "governed_reasoning_posture_v2"