From 0b3e956786147ad0a6b56996155ef373a0c1522c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:01:30 -0600 Subject: [PATCH 1/3] =?UTF-8?q?feat(cli+framework):=20fw-4.44.0=20/=20cli-?= =?UTF-8?q?3.48.0=20=E2=80=94=20guard-closure=20in=20remediation=20AILOGs?= =?UTF-8?q?=20(#419)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A remediation AILOG could close a Critical finding with prose alone — the fix landed, but no mechanical check stood between the defect and its recurrence, and nothing asked for one. PR 3 of CHARTER-02 makes the absence of a recurrence guard a first-class, validatable fact: - core 0.10.0: GuardClosureItem + Frontmatter.trigger/guard_closure (additive; experiment-loom/baton pins follow) - GUARD-001 (warn-first): remediation AILOGs (signature: trigger:) warn when guard_closure is missing/empty, when an item sets both or neither of guard/unguardable, or when unguardable is generic (stock phrases or <30 chars) - charter amend scaffolds the commented guard_closure: block so operators fill it at authoring time - STRAYMARK.md §8/§13/§15.B + straymark-audit-review skill (Guard line per remediation item; .qoder/.qwen copied, .codex/.agent regenerated) - fw-4.44.0 stamps (21 files) + dist-manifest; cli 3.48.0 Charter table gains the release-mechanics rows (experiment pins, dist-manifest, stamp sweep) per the drift-check closure protocol; recorded as R4 in AILOG-2026-08-14-002. --- ...14-002-guard-closure-remediation-ailogs.md | 153 +++++++++++++ ...al-verifiers-for-the-markdown-layer-419.md | 6 +- CHANGELOG.md | 12 + Cargo.lock | 4 +- README.md | 2 +- cli/Cargo.toml | 4 +- cli/src/commands/charter/amend.rs | 6 + cli/src/validation.rs | 210 ++++++++++++++++++ cli/tests/charter_amend_test.rs | 53 +++++ core/Cargo.toml | 2 +- core/src/document.rs | 27 +++ .../skills/straymark-audit-review/SKILL.md | 3 + .../skills/straymark-audit-review/SKILL.md | 3 + .../skills/straymark-audit-review/SKILL.md | 3 + .../skills/straymark-audit-review/SKILL.md | 3 + .../skills/straymark-audit-review/SKILL.md | 3 + dist/.straymark/00-governance/AGENT-RULES.md | 2 +- .../00-governance/C4-DIAGRAM-GUIDE.md | 2 +- .../00-governance/DOCUMENTATION-POLICY.md | 2 +- .../FOLLOW-UPS-BACKLOG-PATTERN.md | 2 +- .../00-governance/QUICK-REFERENCE.md | 2 +- .../00-governance/i18n/es/AGENT-RULES.md | 2 +- .../00-governance/i18n/es/C4-DIAGRAM-GUIDE.md | 2 +- .../i18n/es/DOCUMENTATION-POLICY.md | 2 +- .../i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md | 2 +- .../00-governance/i18n/es/QUICK-REFERENCE.md | 2 +- .../00-governance/i18n/zh-CN/AGENT-RULES.md | 2 +- .../i18n/zh-CN/C4-DIAGRAM-GUIDE.md | 2 +- .../i18n/zh-CN/DOCUMENTATION-POLICY.md | 2 +- .../i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md | 2 +- .../i18n/zh-CN/QUICK-REFERENCE.md | 2 +- dist/STRAYMARK.md | 14 ++ dist/dist-manifest.yml | 2 +- docs/adopters/CLI-REFERENCE.md | 2 +- docs/i18n/es/README.md | 2 +- docs/i18n/es/adopters/CLI-REFERENCE.md | 2 +- docs/i18n/zh-CN/README.md | 2 +- docs/i18n/zh-CN/adopters/CLI-REFERENCE.md | 2 +- experiment-baton/Cargo.toml | 2 +- experiment-loom/Cargo.toml | 2 +- 40 files changed, 524 insertions(+), 30 deletions(-) create mode 100644 .straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md diff --git a/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md b/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md new file mode 100644 index 00000000..dd30fa05 --- /dev/null +++ b/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md @@ -0,0 +1,153 @@ +--- +id: AILOG-2026-08-14-002 +title: guard_closure in remediation AILOGs — GUARD-001 + amend template (#419, PR 3 of CHARTER-02) +status: accepted +created: 2026-08-14 +agent: qoder-cli-v1.0 +confidence: high +review_required: false +risk_level: medium +eu_ai_act_risk: not_applicable +nist_genai_risks: [] +iso_42001_clause: [] +files_modified: + - core/src/document.rs + - core/Cargo.toml + - experiment-loom/Cargo.toml + - experiment-baton/Cargo.toml + - cli/src/validation.rs + - cli/src/commands/charter/amend.rs + - cli/Cargo.toml + - Cargo.lock + - cli/tests/charter_amend_test.rs + - STRAYMARK.md + - dist/STRAYMARK.md + - dist/dist-manifest.yml + - dist/.claude/skills/straymark-audit-review/SKILL.md + - dist/.qoder/skills/straymark-audit-review/SKILL.md + - dist/.qwen/skills/straymark-audit-review/SKILL.md + - dist/.codex/skills/straymark-audit-review/SKILL.md + - dist/.agent/skills/straymark-audit-review/SKILL.md + - CHANGELOG.md + - README.md + - docs/i18n/es/README.md + - docs/i18n/zh-CN/README.md + - docs/adopters/CLI-REFERENCE.md + - docs/i18n/es/adopters/CLI-REFERENCE.md + - docs/i18n/zh-CN/adopters/CLI-REFERENCE.md + - dist/.straymark/00-governance/AGENT-RULES.md + - dist/.straymark/00-governance/C4-DIAGRAM-GUIDE.md + - dist/.straymark/00-governance/DOCUMENTATION-POLICY.md + - dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md + - dist/.straymark/00-governance/QUICK-REFERENCE.md + - dist/.straymark/00-governance/i18n/es/AGENT-RULES.md + - dist/.straymark/00-governance/i18n/es/C4-DIAGRAM-GUIDE.md + - dist/.straymark/00-governance/i18n/es/DOCUMENTATION-POLICY.md + - dist/.straymark/00-governance/i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md + - dist/.straymark/00-governance/i18n/es/QUICK-REFERENCE.md + - dist/.straymark/00-governance/i18n/zh-CN/AGENT-RULES.md + - dist/.straymark/00-governance/i18n/zh-CN/C4-DIAGRAM-GUIDE.md + - dist/.straymark/00-governance/i18n/zh-CN/DOCUMENTATION-POLICY.md + - dist/.straymark/00-governance/i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md + - dist/.straymark/00-governance/i18n/zh-CN/QUICK-REFERENCE.md +observability_scope: none +tags: [guard-closure, remediation, charter-amend, reference-resolution, adopter-feedback, core, cli, charter-02] +related: + - AILOG-2026-08-14-001 +--- + +# AILOG: guard_closure in remediation AILOGs (#419, PR 3) + +## Summary + +Issue #419 case 3: a remediation AILOG closed a Critical finding with prose +alone — the fix landed, but no mechanical check stood between the defect and +its recurrence, and nothing asked for one. PRs 1–2 gave id citations and +registry code claims a resolver; this PR makes the *absence of a recurrence +guard* a first-class, validatable fact. CHARTER-02 closes here. + +Defect class covered (design constraint 3): **unguarded remediations** — +findings "fixed" with no barrier against recurrence. + +## Decision + +Remediation AILOGs carry a `guard_closure:` list — one item per finding being +closed, each declaring **exactly one** of: + +- `guard:` — the mechanical check (rule id, CLI flag, CI gate) that prevents + recurrence; or +- `unguardable:` — a specific rationale when no mechanical guard exists. + +An AILOG counts as a remediation AILOG by the presence of `trigger:` +(`external_audit | production_incident | deferred_implementation`) — the +signature `charter amend` already writes. Detection stays structural: no +heuristic guesses at prose intent. + +`GUARD-001` (Warning, warn-first — design constraint 1) fires when: + +- a remediation AILOG lacks `guard_closure:` or leaves it empty; +- an item sets both `guard` and `unguardable`, or neither; or +- `unguardable` is generic — a stock phrase ("human review", "n/a", "not + applicable", …) or a rationale under 30 characters. The generic-rationale + check is the precision-sensitive half: it is heuristic by nature, which is + exactly why the rule is a warning and not an error. + +Pre-existing remediation AILOGs legitimately lack the field, so the flip to +Error waits for a measured adopter baseline (same discipline as REF-003). + +`charter amend` scaffolds the field: the amendment template renders a +commented `guard_closure:` block with one placeholder item, so the operator +fills it at authoring time instead of discovering GUARD-001 at validation +time. The `straymark-audit-review` skill's remediation template now requires +a **Guard** line per item, so external audit cycles feed the field upstream. + +## Actions Performed + +- `core/src/document.rs`: `GuardClosureItem { finding, guard, unguardable }`; + `Frontmatter` gains `trigger` + `guard_closure` (additive → core 0.10.0; + experiment-loom / experiment-baton pins follow). +- `cli/src/validation.rs`: `check_guard_closure` wired after + `check_type_specific` — the three warning conditions above. 5 unit tests. +- `cli/src/commands/charter/amend.rs`: template renders the commented + `guard_closure:` block after `findings_closed:`. +- `STRAYMARK.md` + `dist/STRAYMARK.md`: §8 frontmatter snippet, §13 + type-table note, §15.B guard-closure paragraph. +- `straymark-audit-review` SKILL.md: Guard line required per remediation + item — canonical `.claude` edited; `.qoder` / `.qwen` copied; + `.codex` / `.agent` regenerated via `gen_minimal_skills` (`--check` + clean). +- Framework 4.44.0 / CLI 3.48.0: CHANGELOG entry, `dist-manifest.yml` + version, footer stamp sweep (21 files). +- 1 integration test (amend template renders `guard_closure:` and the + rendered frontmatter parses). + +## Risks + +- R2 (Charter): GUARD-001 false positives on remediation AILOGs whose guard + genuinely is "a human looks at it". Mitigation: warn-first, and the + `unguardable:` escape hatch only demands specificity (≥30 chars, no stock + phrases), not a different decision. No new instance surfaced during + implementation. +- R4 (new, not in Charter): release-mechanics files not in the original + declaration — the experiment-crate core pins, `dist/dist-manifest.yml`, + and the 21-file footer stamp sweep. Same category as PR 1's Cargo.lock + row: mechanical followers of a version bump, recorded here and added to + the Charter table in this same PR per the drift-check protocol. + +## Validation + +- `cargo test --workspace` green (429 unit tests incl. the 5 new GUARD-001 + cases; 5/5 `charter_amend` integration tests). +- Dogfood: `straymark validate . --include-charters` — 0 errors; warning + count steady (no GUARD-001 fire: this repository's remediation AILOGs + predate the field, and none is in the changed set — warn-first means even + a fire would not block). +- Dogfood: `charter amend` against a fixture charter renders the + `guard_closure:` block; the rendered frontmatter parses. +- `gen_minimal_skills --check` clean after regeneration. + +## Follow-ups + +- GUARD-001 severity flip to Error: only after the warn-first baseline is + measured across adopters (tracked alongside REF-003 in the Charter's + out-of-scope note; FU-007 covers the REF-003 side). diff --git a/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md b/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md index 40f41cba..d3017cd6 100644 --- a/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md +++ b/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md @@ -98,8 +98,12 @@ PR 3 — guard-closure in remediation AILOGs (fw + cli): | `cli/src/commands/analyze_declared_vs_wired.rs` | Reuse the extracted shared walker instead of its private `collect_symbols` | | `core/src/document.rs` | `Frontmatter` gains optional `guard_closure` (additive) | | `cli/src/commands/charter/amend.rs` | Remediation-AILOG template renders `guard_closure` | -| `cli/Cargo.toml` | `regex`+`glob` → required deps; version bumps (3.46.0, 3.47.0, patch for PR 3) | +| `cli/Cargo.toml` | `regex`+`glob` → required deps; version bumps (3.46.0, 3.47.0, 3.48.0) | | `core/Cargo.toml` | Minor bump for the additive Frontmatter field | +| `experiment-loom/Cargo.toml` | `straymark-core` pin → 0.10.0 (added during PR 3 per closure protocol) | +| `experiment-baton/Cargo.toml` | `straymark-core` pin → 0.10.0 (added during PR 3 per closure protocol) | +| `dist/dist-manifest.yml` | `version:` → 4.44.0 (added during PR 3 per closure protocol) | +| `README.md`, `docs/i18n/*/README.md`, `dist/.straymark/00-governance/**` (21 files) | Footer stamp sweep fw-4.43.0 → fw-4.44.0 — follows the framework release like Cargo.lock follows cli bumps (added during PR 3 per closure protocol) | | `cli/tests/validate_test.rs` | `--commit-msg` pass/fail, REF-003 warning, REF-001 error | | `cli/tests/followups_test.rs` | `--claims` batch cases over a fixture tree | | `cli/tests/charter_amend_test.rs` | Template renders `guard_closure` | diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f510a15..7d90a98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project uses [independent versioning](README.md#versioning) for Framewo --- +## Framework 4.44.0 / CLI 3.48.0 — 2026-08-14 + +Third and closing PR of CHARTER-02 (#419): a remediation AILOG used to be able to close a Critical finding with prose alone — no mechanical check preventing recurrence, and nothing asked for one. This release makes the absence of a guard a first-class, validatable fact instead of an invisible omission. + +### Added (Framework + CLI) + +- **`guard_closure:` frontmatter field on remediation AILOGs** (#419, fw-4.44.0 / core 0.10.0): one item per finding being closed, each declaring **exactly one** of `guard:` (the mechanical check — rule id, CLI flag, or CI gate — that prevents recurrence) or `unguardable:` (a specific rationale when no mechanical guard exists). A remediation AILOG is recognized by the presence of `trigger:` (`external_audit | production_incident | deferred_implementation`). Documented in STRAYMARK.md §8 (frontmatter snippet), §13 (type-table note), and §15.B (guard-closure paragraph); the `straymark-audit-review` skill's remediation template now requires a **Guard** line per item so the audit cycle feeds the field. + +- **`GUARD-001` validation rule** (#419, warn-first): fires on remediation AILOGs when `guard_closure:` is missing or empty, when an item sets both or neither of `guard`/`unguardable`, or when `unguardable` is generic — stock phrases ("human review", "n/a", "not applicable", …) or a rationale under 30 characters. Warn-first by design constraint: pre-existing remediation AILOGs legitimately lack the field, so the flip to Error waits for a measured adopter baseline. Defect class: unguarded remediations — findings "fixed" with no barrier against recurrence (issue case 3). + +- **`charter amend` scaffolds the field**: the amendment AILOG template now renders a commented `guard_closure:` block (one placeholder item, syntax reminder inline) so the operator fills it instead of discovering GUARD-001 at validation time. + ## CLI 3.47.0 — 2026-08-14 Second PR of CHARTER-02 (#419): the follow-ups registry also states claims about the tree — and those decay just as silently as id citations do. diff --git a/Cargo.lock b/Cargo.lock index 5a779b3b..43469955 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2574,7 +2574,7 @@ dependencies = [ [[package]] name = "straymark-cli" -version = "3.47.0" +version = "3.48.0" dependencies = [ "anyhow", "arborist-metrics", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "straymark-core" -version = "0.9.1" +version = "0.10.0" dependencies = [ "anyhow", "serde", diff --git a/README.md b/README.md index 44bddec5..4a740597 100644 --- a/README.md +++ b/README.md @@ -278,7 +278,7 @@ StrayMark uses independent version tags for each component: | Component | Tag prefix | Example | Includes | | --- | --- | --- | --- | -| Framework | `fw-` | `fw-4.43.0` | Templates (12 types), governance, directives, Charter template + schema | +| Framework | `fw-` | `fw-4.44.0` | Templates (12 types), governance, directives, Charter template + schema | | CLI | `cli-` | `cli-3.45.0` | The `straymark` binary | | Loom (EXPERIMENTAL) | `loom-` | `loom-0.4.2` | The `straymark-loom` visualization server, downloaded on demand by `straymark loom serve` | diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c9edf5e6..0ec5802f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "straymark-cli" -version = "3.47.0" +version = "3.48.0" edition = "2021" description = "CLI for StrayMark — the cognitive discipline your AI-assisted projects need" license = "MIT" @@ -25,7 +25,7 @@ path = "src/bin/gen_minimal_skills.rs" required-features = ["dev-tools"] [dependencies] -straymark-core = { version = "0.9.1", path = "../core" } +straymark-core = { version = "0.10.0", path = "../core" } clap = { version = "4", features = ["derive"] } reqwest = { version = "0.12", features = ["blocking", "rustls-tls", "json"] } serde = { version = "1", features = ["derive"] } diff --git a/cli/src/commands/charter/amend.rs b/cli/src/commands/charter/amend.rs index 6bbd55ca..40ca2259 100644 --- a/cli/src/commands/charter/amend.rs +++ b/cli/src/commands/charter/amend.rs @@ -319,6 +319,12 @@ review_required: true\n\ {amends_line}\ trigger: {trigger}\n\ findings_closed: {findings_closed}\n\ +guard_closure:\n\ + # Per finding being closed: exactly one of `guard:` (the mechanical check that now\n\ + # prevents recurrence — a command, rule, or test) or `unguardable:` (a specific\n\ + # rationale; stock phrases like 'human review' trip GUARD-001). One item per finding.\n\ + - finding: F1 # replace with the real finding id from the audit review\n\ + guard: \"\"\n\ ---\n\ \n\ # {title}\n\ diff --git a/cli/src/validation.rs b/cli/src/validation.rs index 68165680..675682c9 100644 --- a/cli/src/validation.rs +++ b/cli/src/validation.rs @@ -1152,6 +1152,7 @@ fn validate_document( check_valid_status(&mut result, doc); check_cross_rules(&mut result, doc); check_type_specific(&mut result, doc); + check_guard_closure(&mut result, doc); check_date_consistency(&mut result, doc); check_related_exist(&mut result, doc, index); check_sensitive_info(&mut result, doc); @@ -1531,6 +1532,93 @@ fn check_type_specific(result: &mut ValidationResult, doc: &StrayMarkDocument) { } } +/// GUARD-001 (#419, warn-first): a remediation AILOG — signature: `trigger:` +/// present, written by `charter amend` — must close the lesson-as-prose loop. +/// Every finding it closes names either the mechanical `guard` that now +/// prevents recurrence, or an `unguardable` rationale specific enough to act +/// on. A lesson that lives only in prose recurs (issue case 3). +fn check_guard_closure(result: &mut ValidationResult, doc: &StrayMarkDocument) { + if doc.doc_type != DocType::Ailog || doc.frontmatter.trigger.is_none() { + return; + } + let fm = &doc.frontmatter; + + let items = match &fm.guard_closure { + Some(items) if !items.is_empty() => items, + _ => { + result.add(ValidationIssue { + file: doc.path.clone(), + rule: "GUARD-001".to_string(), + message: "remediation AILOG (`trigger:` present) has no `guard_closure` — each closed finding must name its mechanical guard or an unguardable rationale".to_string(), + severity: Severity::Warning, + fix_hint: Some("Add a `guard_closure:` list with one item per finding: `- finding: F1` + exactly one of `guard:` / `unguardable:` (#419)".to_string()), + }); + return; + } + }; + + for (i, item) in items.iter().enumerate() { + let label = item + .finding + .as_deref() + .map(|f| format!(" (finding {f})")) + .unwrap_or_default(); + let guard = item.guard.as_deref().map(str::trim).filter(|s| !s.is_empty()); + let unguardable = item + .unguardable + .as_deref() + .map(str::trim) + .filter(|s| !s.is_empty()); + + if guard.is_some() == unguardable.is_some() { + result.add(ValidationIssue { + file: doc.path.clone(), + rule: "GUARD-001".to_string(), + message: format!("guard_closure item {}{} must set exactly one of `guard:` / `unguardable:`", i + 1, label), + severity: Severity::Warning, + fix_hint: Some("Keep `guard:` when a mechanical check prevents recurrence; otherwise keep `unguardable:` with a specific rationale".to_string()), + }); + continue; + } + + if let Some(rationale) = unguardable { + if is_generic_unguardable(rationale) { + result.add(ValidationIssue { + file: doc.path.clone(), + rule: "GUARD-001".to_string(), + message: format!("guard_closure item {}{} has a generic `unguardable:` rationale — it must say WHY no mechanical guard is possible, specifically", i + 1, label), + severity: Severity::Warning, + fix_hint: Some("Name what a guard would have to observe and why it cannot — e.g. which human judgment, which external system (#419)".to_string()), + }); + } + } + } +} + +/// Generic unguardable rationales: short enough to carry no information, or a +/// stock phrase. "Non-generic" is heuristic by design (warn-first) — the bar +/// is that a reader can tell what makes mechanization impossible. +fn is_generic_unguardable(rationale: &str) -> bool { + const STOCK: &[&str] = &[ + "n/a", + "none", + "not applicable", + "no guard", + "no guard possible", + "cannot be guarded", + "can't be guarded", + "not guardable", + "too hard", + "human review", + "manual review", + ]; + let lower = rationale.trim().to_lowercase(); + if STOCK.contains(&lower.as_str()) { + return true; + } + rationale.trim().len() < 30 +} + /// Returns true when `related` includes any entry whose ID starts with `prefix`. fn related_has_prefix(doc: &StrayMarkDocument, prefix: &str) -> bool { doc.frontmatter @@ -2515,4 +2603,126 @@ mod tests { check_id_references(std::slice::from_ref(&path), &index, &mut result); assert!(result.warnings.is_empty()); } + #[test] + fn test_guard_001_remediation_without_guard_closure_warns() { + let fm = Frontmatter { + id: Some("AILOG-2025-01-01-002".into()), + trigger: Some("external_audit".into()), + ..Default::default() + }; + let doc = make_doc("AILOG-2025-01-01-002-fix.md", DocType::Ailog, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert_eq!(result.warnings.len(), 1); + assert_eq!(result.warnings[0].rule, "GUARD-001"); + assert!(result.warnings[0].message.contains("no `guard_closure`")); + } + + #[test] + fn test_guard_001_clean_items_pass() { + use straymark_core::document::GuardClosureItem; + let fm = Frontmatter { + id: Some("AILOG-2025-01-01-002".into()), + trigger: Some("external_audit".into()), + guard_closure: Some(vec![ + GuardClosureItem { + finding: Some("F1".into()), + guard: Some("validate --commit-msg blocks phantom citations".into()), + unguardable: None, + }, + GuardClosureItem { + finding: Some("F2".into()), + guard: None, + unguardable: Some( + "Depends on auditor attention in an external CLI session, which no local check can observe" + .into(), + ), + }, + ]), + ..Default::default() + }; + let doc = make_doc("AILOG-2025-01-01-002-fix.md", DocType::Ailog, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert!(result.warnings.is_empty()); + } + + #[test] + fn test_guard_001_both_or_neither_set_warns() { + use straymark_core::document::GuardClosureItem; + let fm = Frontmatter { + id: Some("AILOG-2025-01-01-002".into()), + trigger: Some("production_incident".into()), + guard_closure: Some(vec![ + GuardClosureItem { + finding: None, + guard: Some("x".into()), + unguardable: Some("also set, which is ambiguous".into()), + }, + GuardClosureItem { + finding: Some("F2".into()), + guard: None, + unguardable: None, + }, + ]), + ..Default::default() + }; + let doc = make_doc("AILOG-2025-01-01-002-fix.md", DocType::Ailog, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert_eq!(result.warnings.len(), 2); + assert!(result.warnings.iter().all(|w| w.rule == "GUARD-001")); + assert!(result.warnings[0].message.contains("exactly one")); + } + + #[test] + fn test_guard_001_generic_unguardable_warns() { + use straymark_core::document::GuardClosureItem; + assert!(is_generic_unguardable("human review")); + assert!(is_generic_unguardable("n/a")); + assert!(is_generic_unguardable("too short")); + assert!(!is_generic_unguardable( + "Depends on auditor attention in an external CLI session, which no local check can observe" + )); + + let fm = Frontmatter { + id: Some("AILOG-2025-01-01-002".into()), + trigger: Some("deferred_implementation".into()), + guard_closure: Some(vec![GuardClosureItem { + finding: Some("F3".into()), + guard: None, + unguardable: Some("human review".into()), + }]), + ..Default::default() + }; + let doc = make_doc("AILOG-2025-01-01-002-fix.md", DocType::Ailog, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert_eq!(result.warnings.len(), 1); + assert!(result.warnings[0].message.contains("generic")); + } + + #[test] + fn test_guard_001_skips_non_remediation_docs() { + // No trigger: not a remediation AILOG — guard_closure is not required. + let fm = Frontmatter { + id: Some("AILOG-2025-01-01-002".into()), + ..Default::default() + }; + let doc = make_doc("AILOG-2025-01-01-002-fix.md", DocType::Ailog, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert!(result.warnings.is_empty()); + + // Trigger on a non-AILOG: same. + let fm = Frontmatter { + id: Some("ADR-2025-01-01-001".into()), + trigger: Some("external_audit".into()), + ..Default::default() + }; + let doc = make_doc("ADR-2025-01-01-001-x.md", DocType::Adr, fm, ""); + let mut result = ValidationResult::default(); + check_guard_closure(&mut result, &doc); + assert!(result.warnings.is_empty()); + } } diff --git a/cli/tests/charter_amend_test.rs b/cli/tests/charter_amend_test.rs index 75185321..d186a061 100644 --- a/cli/tests/charter_amend_test.rs +++ b/cli/tests/charter_amend_test.rs @@ -199,3 +199,56 @@ fn amend_merge_into_writes_post_close_amendment_to_telemetry() { .and_then(|v| v.as_str()); assert_eq!(trigger, Some("external_audit")); } + +#[test] +fn amend_new_ailog_renders_guard_closure_placeholder() { + let dir = TempDir::new().unwrap(); + write_closed_charter(dir.path()); + write_prior_ailog(dir.path()); + + cargo_bin_cmd!("straymark") + .args([ + "charter", + "amend", + "CHARTER-18", + "--trigger", + "external_audit", + "--findings-closed", + "2", + "--ailog-title", + "guard closure scaffold", + "--path", + ]) + .arg(dir.path().to_str().unwrap()) + .assert() + .success(); + + let logs = dir.path().join(".straymark/07-ai-audit/agent-logs"); + let new_ailog = std::fs::read_dir(&logs) + .unwrap() + .filter_map(|e| e.ok()) + .map(|e| e.path()) + .find(|p| { + p.file_name() + .and_then(|n| n.to_str()) + .is_some_and(|n| n.starts_with("AILOG-") && n.contains("guard-closure-scaffold")) + }) + .expect("new AILOG exists"); + let content = std::fs::read_to_string(new_ailog).unwrap(); + assert!( + content.contains("guard_closure:"), + "remediation AILOG template renders the guard_closure field (#419):\n{content}" + ); + assert!( + content.contains("- finding: F1"), + "placeholder item per finding:\n{content}" + ); + + // The placeholder frontmatter must parse as valid YAML carrying the field. + let fm_text = content + .strip_prefix("---\n") + .and_then(|rest| rest.split("\n---\n").next()) + .expect("frontmatter delimiters"); + let parsed: serde_yaml::Value = serde_yaml::from_str(fm_text).expect("frontmatter parses"); + assert!(parsed.get("guard_closure").is_some()); +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 56f48523..391cb6f7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "straymark-core" -version = "0.9.1" +version = "0.10.0" edition = "2021" description = "Shared document model and knowledge graph for StrayMark — parses governance documents and builds their typed traceability graph" license = "MIT" diff --git a/core/src/document.rs b/core/src/document.rs index 8e6a6b78..68acac42 100644 --- a/core/src/document.rs +++ b/core/src/document.rs @@ -178,6 +178,22 @@ impl fmt::Display for DocType { } } +/// One guard-closure item in a remediation AILOG (#419). Exactly one of +/// `guard` / `unguardable` must be present and non-empty — GUARD-001 warns +/// on both-set and on neither-set. +#[derive(Debug, Clone, Deserialize, Default)] +#[allow(dead_code)] +pub struct GuardClosureItem { + /// Audit finding id this item closes (e.g. `F3`), where applicable. + pub finding: Option, + /// The mechanical guard that now prevents recurrence (command, rule, or + /// check an adopter can run). + pub guard: Option, + /// Why no mechanical guard is possible — must be specific enough to act + /// on; generic rationales ("n/a", "human review") trip GUARD-001. + pub unguardable: Option, +} + /// Frontmatter fields extracted from a StrayMark document. /// All fields are optional so the validator can report which are missing. #[derive(Debug, Clone, Deserialize, Default)] @@ -240,6 +256,17 @@ pub struct Frontmatter { // REQ-specific (OpenAPI/AsyncAPI) pub api_spec_path: Option, + // ----- Remediation AILOGs (charter amend, fw-4.44.0 / #419) ----- + /// What surfaced after the Charter closed: `external_audit` | + /// `production_incident` | `deferred_implementation`. Its presence marks + /// the AILOG as a remediation document — the signature GUARD-001 keys on. + pub trigger: Option, + /// Per-finding closure of the lesson-as-prose loop: every finding the + /// remediation closes names either the mechanical `guard` that now + /// prevents recurrence, or an `unguardable` rationale explaining why no + /// mechanical guard is possible. Exactly one per item. + pub guard_closure: Option>, + // ----- China regulatory profile (regional_scope: china) ----- // TC260 v2.0 (AI Safety Governance Framework) diff --git a/dist/.agent/skills/straymark-audit-review/SKILL.md b/dist/.agent/skills/straymark-audit-review/SKILL.md index 8cffc600..e01defd5 100644 --- a/dist/.agent/skills/straymark-audit-review/SKILL.md +++ b/dist/.agent/skills/straymark-audit-review/SKILL.md @@ -150,10 +150,13 @@ of this Charter. The auditors' findings are evaluated against THIS scope.] ## 4. Remediation plan — VALID and PARTIALLY VALID findings +Every remediation item carries a **Guard** line (fw-4.44.0+): the mechanical check that will prevent the finding from recurring — a command, a validate rule, a test — or `unguardable: ` naming what a guard would have to observe and why it cannot. Stock rationales ("human review", "n/a") are not acceptable. These lines feed `guard_closure:` in the remediation AILOG (`charter amend`, §15.B); GUARD-001 warns when an item is missing, ambiguous, or generic. + ### P0 — Security - **Files:** `path:line` - **Problem:** [description with code evidence] - **Remediation:** [specific approach] +- **Guard:** [mechanical check preventing recurrence, or `unguardable: `] - **Complexity:** [Low / Medium / High] - **Detected by:** [auditor slug(s), or "Missed by all auditors" if you found it] diff --git a/dist/.claude/skills/straymark-audit-review/SKILL.md b/dist/.claude/skills/straymark-audit-review/SKILL.md index 5414ccdc..5ec1d07c 100644 --- a/dist/.claude/skills/straymark-audit-review/SKILL.md +++ b/dist/.claude/skills/straymark-audit-review/SKILL.md @@ -152,10 +152,13 @@ of this Charter. The auditors' findings are evaluated against THIS scope.] ## 4. Remediation plan — VALID and PARTIALLY VALID findings +Every remediation item carries a **Guard** line (fw-4.44.0+): the mechanical check that will prevent the finding from recurring — a command, a validate rule, a test — or `unguardable: ` naming what a guard would have to observe and why it cannot. Stock rationales ("human review", "n/a") are not acceptable. These lines feed `guard_closure:` in the remediation AILOG (`charter amend`, §15.B); GUARD-001 warns when an item is missing, ambiguous, or generic. + ### P0 — Security - **Files:** `path:line` - **Problem:** [description with code evidence] - **Remediation:** [specific approach] +- **Guard:** [mechanical check preventing recurrence, or `unguardable: `] - **Complexity:** [Low / Medium / High] - **Detected by:** [auditor slug(s), or "Missed by all auditors" if you found it] diff --git a/dist/.codex/skills/straymark-audit-review/SKILL.md b/dist/.codex/skills/straymark-audit-review/SKILL.md index 8cffc600..e01defd5 100644 --- a/dist/.codex/skills/straymark-audit-review/SKILL.md +++ b/dist/.codex/skills/straymark-audit-review/SKILL.md @@ -150,10 +150,13 @@ of this Charter. The auditors' findings are evaluated against THIS scope.] ## 4. Remediation plan — VALID and PARTIALLY VALID findings +Every remediation item carries a **Guard** line (fw-4.44.0+): the mechanical check that will prevent the finding from recurring — a command, a validate rule, a test — or `unguardable: ` naming what a guard would have to observe and why it cannot. Stock rationales ("human review", "n/a") are not acceptable. These lines feed `guard_closure:` in the remediation AILOG (`charter amend`, §15.B); GUARD-001 warns when an item is missing, ambiguous, or generic. + ### P0 — Security - **Files:** `path:line` - **Problem:** [description with code evidence] - **Remediation:** [specific approach] +- **Guard:** [mechanical check preventing recurrence, or `unguardable: `] - **Complexity:** [Low / Medium / High] - **Detected by:** [auditor slug(s), or "Missed by all auditors" if you found it] diff --git a/dist/.qoder/skills/straymark-audit-review/SKILL.md b/dist/.qoder/skills/straymark-audit-review/SKILL.md index 5414ccdc..5ec1d07c 100644 --- a/dist/.qoder/skills/straymark-audit-review/SKILL.md +++ b/dist/.qoder/skills/straymark-audit-review/SKILL.md @@ -152,10 +152,13 @@ of this Charter. The auditors' findings are evaluated against THIS scope.] ## 4. Remediation plan — VALID and PARTIALLY VALID findings +Every remediation item carries a **Guard** line (fw-4.44.0+): the mechanical check that will prevent the finding from recurring — a command, a validate rule, a test — or `unguardable: ` naming what a guard would have to observe and why it cannot. Stock rationales ("human review", "n/a") are not acceptable. These lines feed `guard_closure:` in the remediation AILOG (`charter amend`, §15.B); GUARD-001 warns when an item is missing, ambiguous, or generic. + ### P0 — Security - **Files:** `path:line` - **Problem:** [description with code evidence] - **Remediation:** [specific approach] +- **Guard:** [mechanical check preventing recurrence, or `unguardable: `] - **Complexity:** [Low / Medium / High] - **Detected by:** [auditor slug(s), or "Missed by all auditors" if you found it] diff --git a/dist/.qwen/skills/straymark-audit-review/SKILL.md b/dist/.qwen/skills/straymark-audit-review/SKILL.md index 5414ccdc..5ec1d07c 100644 --- a/dist/.qwen/skills/straymark-audit-review/SKILL.md +++ b/dist/.qwen/skills/straymark-audit-review/SKILL.md @@ -152,10 +152,13 @@ of this Charter. The auditors' findings are evaluated against THIS scope.] ## 4. Remediation plan — VALID and PARTIALLY VALID findings +Every remediation item carries a **Guard** line (fw-4.44.0+): the mechanical check that will prevent the finding from recurring — a command, a validate rule, a test — or `unguardable: ` naming what a guard would have to observe and why it cannot. Stock rationales ("human review", "n/a") are not acceptable. These lines feed `guard_closure:` in the remediation AILOG (`charter amend`, §15.B); GUARD-001 warns when an item is missing, ambiguous, or generic. + ### P0 — Security - **Files:** `path:line` - **Problem:** [description with code evidence] - **Remediation:** [specific approach] +- **Guard:** [mechanical check preventing recurrence, or `unguardable: `] - **Complexity:** [Low / Medium / High] - **Detected by:** [auditor slug(s), or "Missed by all auditors" if you found it] diff --git a/dist/.straymark/00-governance/AGENT-RULES.md b/dist/.straymark/00-governance/AGENT-RULES.md index 44cd0f16..b1c03be2 100644 --- a/dist/.straymark/00-governance/AGENT-RULES.md +++ b/dist/.straymark/00-governance/AGENT-RULES.md @@ -418,4 +418,4 @@ When a project accumulates a high volume of AILOGs across multiple Charters and --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/C4-DIAGRAM-GUIDE.md b/dist/.straymark/00-governance/C4-DIAGRAM-GUIDE.md index 3d88f596..cc5e6db6 100644 --- a/dist/.straymark/00-governance/C4-DIAGRAM-GUIDE.md +++ b/dist/.straymark/00-governance/C4-DIAGRAM-GUIDE.md @@ -234,4 +234,4 @@ Use a Level 1 (Context) diagram to illustrate: --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/DOCUMENTATION-POLICY.md b/dist/.straymark/00-governance/DOCUMENTATION-POLICY.md index c9ef0f42..26de040e 100644 --- a/dist/.straymark/00-governance/DOCUMENTATION-POLICY.md +++ b/dist/.straymark/00-governance/DOCUMENTATION-POLICY.md @@ -319,4 +319,4 @@ See also [ADR-2025-01-20-001] for architectural context. --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md b/dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md index 690ab972..d74c0744 100644 --- a/dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md +++ b/dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md @@ -370,4 +370,4 @@ Contributed via [issue #111](https://github.com/StrangeDaysTech/straymark/issues --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/QUICK-REFERENCE.md b/dist/.straymark/00-governance/QUICK-REFERENCE.md index c3288b43..bddfa63e 100644 --- a/dist/.straymark/00-governance/QUICK-REFERENCE.md +++ b/dist/.straymark/00-governance/QUICK-REFERENCE.md @@ -263,4 +263,4 @@ Mark `review_required: true` when: --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/es/AGENT-RULES.md b/dist/.straymark/00-governance/i18n/es/AGENT-RULES.md index 829aba4a..1fda6e6c 100644 --- a/dist/.straymark/00-governance/i18n/es/AGENT-RULES.md +++ b/dist/.straymark/00-governance/i18n/es/AGENT-RULES.md @@ -418,4 +418,4 @@ Cuando un proyecto acumula un volumen alto de AILOGs a lo largo de múltiples Ch --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/es/C4-DIAGRAM-GUIDE.md b/dist/.straymark/00-governance/i18n/es/C4-DIAGRAM-GUIDE.md index 750b73f2..18ccb03f 100644 --- a/dist/.straymark/00-governance/i18n/es/C4-DIAGRAM-GUIDE.md +++ b/dist/.straymark/00-governance/i18n/es/C4-DIAGRAM-GUIDE.md @@ -234,4 +234,4 @@ Usar un diagrama de Nivel 1 (Contexto) para ilustrar: --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/es/DOCUMENTATION-POLICY.md b/dist/.straymark/00-governance/i18n/es/DOCUMENTATION-POLICY.md index 90af2c7b..5e1f6c28 100644 --- a/dist/.straymark/00-governance/i18n/es/DOCUMENTATION-POLICY.md +++ b/dist/.straymark/00-governance/i18n/es/DOCUMENTATION-POLICY.md @@ -312,4 +312,4 @@ Ver también [ADR-2025-01-20-001] para contexto arquitectónico. --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md b/dist/.straymark/00-governance/i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md index 04989497..bd7abeea 100644 --- a/dist/.straymark/00-governance/i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md +++ b/dist/.straymark/00-governance/i18n/es/FOLLOW-UPS-BACKLOG-PATTERN.md @@ -356,4 +356,4 @@ Contribuido vía [issue #111](https://github.com/StrangeDaysTech/straymark/issue --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/es/QUICK-REFERENCE.md b/dist/.straymark/00-governance/i18n/es/QUICK-REFERENCE.md index 05941e7f..6cbbdac9 100644 --- a/dist/.straymark/00-governance/i18n/es/QUICK-REFERENCE.md +++ b/dist/.straymark/00-governance/i18n/es/QUICK-REFERENCE.md @@ -235,4 +235,4 @@ Marcar `review_required: true` cuando: --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/zh-CN/AGENT-RULES.md b/dist/.straymark/00-governance/i18n/zh-CN/AGENT-RULES.md index fdfd92a4..fec05278 100644 --- a/dist/.straymark/00-governance/i18n/zh-CN/AGENT-RULES.md +++ b/dist/.straymark/00-governance/i18n/zh-CN/AGENT-RULES.md @@ -413,4 +413,4 @@ confidence: high | medium | low --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/zh-CN/C4-DIAGRAM-GUIDE.md b/dist/.straymark/00-governance/i18n/zh-CN/C4-DIAGRAM-GUIDE.md index dd8e0779..543121db 100644 --- a/dist/.straymark/00-governance/i18n/zh-CN/C4-DIAGRAM-GUIDE.md +++ b/dist/.straymark/00-governance/i18n/zh-CN/C4-DIAGRAM-GUIDE.md @@ -234,4 +234,4 @@ Rel(api, db, "Reads/Writes", "SQL") --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/zh-CN/DOCUMENTATION-POLICY.md b/dist/.straymark/00-governance/i18n/zh-CN/DOCUMENTATION-POLICY.md index 81f2e287..71d4f85b 100644 --- a/dist/.straymark/00-governance/i18n/zh-CN/DOCUMENTATION-POLICY.md +++ b/dist/.straymark/00-governance/i18n/zh-CN/DOCUMENTATION-POLICY.md @@ -311,4 +311,4 @@ review_outcome: approved # approved | revisions_requested | rejec --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md b/dist/.straymark/00-governance/i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md index 0f17d12d..ef3e319a 100644 --- a/dist/.straymark/00-governance/i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md +++ b/dist/.straymark/00-governance/i18n/zh-CN/FOLLOW-UPS-BACKLOG-PATTERN.md @@ -356,4 +356,4 @@ straymark followups new --title "..." --origin "CHARTER-NN §Scope" [--bucket --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/.straymark/00-governance/i18n/zh-CN/QUICK-REFERENCE.md b/dist/.straymark/00-governance/i18n/zh-CN/QUICK-REFERENCE.md index 14d645da..b185e2f2 100644 --- a/dist/.straymark/00-governance/i18n/zh-CN/QUICK-REFERENCE.md +++ b/dist/.straymark/00-governance/i18n/zh-CN/QUICK-REFERENCE.md @@ -235,4 +235,4 @@ risk_level: low | medium | high | critical --- -*StrayMark fw-4.43.0 | [Strange Days Tech](https://strangedays.tech)* +*StrayMark fw-4.44.0 | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/STRAYMARK.md b/dist/STRAYMARK.md index f0462a1b..d94bf6d5 100644 --- a/dist/STRAYMARK.md +++ b/dist/STRAYMARK.md @@ -185,6 +185,11 @@ related: # nist_genai_risks: [] # iso_42001_clause: [] # observability_scope: none +# Remediation AILOGs only (charter amend, fw-4.44.0+ — see §15.B): +# trigger: external_audit | production_incident | deferred_implementation +# guard_closure: one item per closed finding — exactly one of +# guard: +# unguardable: --- ``` @@ -352,6 +357,8 @@ related: | `DPIA` | Data Protection Impact Assessment | `.straymark/07-ai-audit/ethical-reviews/` | | `Charter` | Bounded unit of work (filename `NN-slug.md`, not `TYPE-YYYY-…`) | `.straymark/charters/` | +An AILOG carrying `trigger:` is a **remediation document** (§15.B): it must close every finding in `guard_closure:` — either the mechanical `guard` that now prevents recurrence, or a specific `unguardable:` rationale. GUARD-001 (warn-first, fw-4.44.0+) nags when the field is missing, ambiguous, or generic. + --- ## 14. Regulatory Alignment @@ -452,6 +459,13 @@ straymark charter amend CHARTER-NN \ # scaffolds the new AILOG, edits th The command does not touch git — the operator decides when to commit. The `straymark charter audit --merge-reports --merge-into ` path tolerates `external_audit: []` placeholders in v0.2+ schema so the round-trip with `post_close_amendment` is smooth. See `.straymark/00-governance/CHARTER-CHAIN-EVOLUTION.md` Pattern 2 for full mechanics. +**Guard closure (fw-4.44.0+, #419).** A remediation that lives only in prose recurs: the audit lesson ("X had no callers but was declared dead", "the commit cited a phantom AILOG") must terminate in something mechanical, or be declared unguardable with a reason specific enough to act on. The amendment AILOG therefore carries `guard_closure:` — one item per finding being closed, each with exactly one of: + +- `guard:` — the mechanical check that now prevents recurrence (a command, a validate rule, a test). E.g. `guard: "validate --commit-msg blocks phantom AILOG citations (cli-3.46.0)"`. +- `unguardable:` — why no mechanical guard is possible, naming what a guard would have to observe and why it cannot. Stock phrases ("human review", "n/a") and one-liners under 30 chars trip **GUARD-001** (warn-first). + +`charter amend` scaffolds the field with a placeholder item per finding; `straymark validate` warns until every item is unambiguous and specific. + --- ## 16. Follow-ups backlog — the pending-work registry *(fw-4.21.0+)* diff --git a/dist/dist-manifest.yml b/dist/dist-manifest.yml index 05accd65..51cc86a8 100644 --- a/dist/dist-manifest.yml +++ b/dist/dist-manifest.yml @@ -1,4 +1,4 @@ -version: "4.43.0" +version: "4.44.0" description: "StrayMark distribution manifest" repository: "https://github.com/StrangeDaysTech/straymark" diff --git a/docs/adopters/CLI-REFERENCE.md b/docs/adopters/CLI-REFERENCE.md index 535a6d4b..15e1abb8 100644 --- a/docs/adopters/CLI-REFERENCE.md +++ b/docs/adopters/CLI-REFERENCE.md @@ -45,7 +45,7 @@ StrayMark uses **independent version tags** for each component: | Component | Tag prefix | Example | What it includes | |-----------|-----------|---------|------------------| -| Framework | `fw-` | `fw-4.43.0` | Templates (12 types), governance docs, directives, Charter template + schema | +| Framework | `fw-` | `fw-4.44.0` | Templates (12 types), governance docs, directives, Charter template + schema | | CLI | `cli-` | `cli-3.45.0` | The `straymark` binary | | Loom (EXPERIMENTAL) | `loom-` | `loom-0.4.2` | The `straymark-loom` visualization server, downloaded on demand by `straymark loom serve` | diff --git a/docs/i18n/es/README.md b/docs/i18n/es/README.md index 5299ffb8..58cb10af 100644 --- a/docs/i18n/es/README.md +++ b/docs/i18n/es/README.md @@ -241,7 +241,7 @@ StrayMark usa tags de versión independientes para cada componente: | Componente | Prefijo de tag | Ejemplo | Incluye | |------------|---------------|---------|---------| -| Framework | `fw-` | `fw-4.43.0` | Plantillas (12 tipos), gobernanza, directivas, plantilla + schema de Charter | +| Framework | `fw-` | `fw-4.44.0` | Plantillas (12 tipos), gobernanza, directivas, plantilla + schema de Charter | | CLI | `cli-` | `cli-3.45.0` | El binario `straymark` | | Loom (EXPERIMENTAL) | `loom-` | `loom-0.4.2` | El servidor de visualización `straymark-loom`, descargado bajo demanda por `straymark loom serve` | diff --git a/docs/i18n/es/adopters/CLI-REFERENCE.md b/docs/i18n/es/adopters/CLI-REFERENCE.md index 40649916..e2fa8c05 100644 --- a/docs/i18n/es/adopters/CLI-REFERENCE.md +++ b/docs/i18n/es/adopters/CLI-REFERENCE.md @@ -45,7 +45,7 @@ StrayMark usa **tags de versión independientes** para cada componente: | Componente | Prefijo de tag | Ejemplo | Qué incluye | |------------|---------------|---------|-------------| -| Framework | `fw-` | `fw-4.43.0` | Plantillas (12 tipos), docs de gobernanza, directivas | +| Framework | `fw-` | `fw-4.44.0` | Plantillas (12 tipos), docs de gobernanza, directivas | | CLI | `cli-` | `cli-3.45.0` | El binario `straymark` | | Loom (EXPERIMENTAL) | `loom-` | `loom-0.4.2` | El servidor de visualización `straymark-loom`, descargado bajo demanda por `straymark loom serve` | diff --git a/docs/i18n/zh-CN/README.md b/docs/i18n/zh-CN/README.md index dd633d7f..44959bc7 100644 --- a/docs/i18n/zh-CN/README.md +++ b/docs/i18n/zh-CN/README.md @@ -259,7 +259,7 @@ StrayMark 为每个组件使用独立的版本标签: | 组件 | 标签前缀 | 示例 | 包含内容 | |------|----------|------|----------| -| Framework | `fw-` | `fw-4.43.0` | 模板(12 种类型)、治理文档、指令、Charter 模板 + schema | +| Framework | `fw-` | `fw-4.44.0` | 模板(12 种类型)、治理文档、指令、Charter 模板 + schema | | CLI | `cli-` | `cli-3.45.0` | `straymark` 二进制文件 | | Loom(实验性) | `loom-` | `loom-0.4.2` | `straymark-loom` 可视化服务器,由 `straymark loom serve` 按需下载 | diff --git a/docs/i18n/zh-CN/adopters/CLI-REFERENCE.md b/docs/i18n/zh-CN/adopters/CLI-REFERENCE.md index 99f920a5..e799652d 100644 --- a/docs/i18n/zh-CN/adopters/CLI-REFERENCE.md +++ b/docs/i18n/zh-CN/adopters/CLI-REFERENCE.md @@ -45,7 +45,7 @@ StrayMark 为每个组件使用**独立的版本标签**: | 组件 | 标签前缀 | 示例 | 包含内容 | |------|----------|------|----------| -| Framework | `fw-` | `fw-4.43.0` | 模板(12 种类型)、治理文档、指令 | +| Framework | `fw-` | `fw-4.44.0` | 模板(12 种类型)、治理文档、指令 | | CLI | `cli-` | `cli-3.45.0` | `straymark` 二进制文件 | | Loom(实验性) | `loom-` | `loom-0.4.2` | `straymark-loom` 可视化服务器,由 `straymark loom serve` 按需下载 | diff --git a/experiment-baton/Cargo.toml b/experiment-baton/Cargo.toml index 83823841..53784119 100644 --- a/experiment-baton/Cargo.toml +++ b/experiment-baton/Cargo.toml @@ -18,7 +18,7 @@ name = "straymark-baton" path = "src/main.rs" [dependencies] -straymark-core = { version = "0.9.0", path = "../core" } +straymark-core = { version = "0.10.0", path = "../core" } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" diff --git a/experiment-loom/Cargo.toml b/experiment-loom/Cargo.toml index dc56ac9a..41b1a2ef 100644 --- a/experiment-loom/Cargo.toml +++ b/experiment-loom/Cargo.toml @@ -14,7 +14,7 @@ name = "straymark-loom" path = "src/main.rs" [dependencies] -straymark-core = { version = "0.9.1", path = "../core" } +straymark-core = { version = "0.10.0", path = "../core" } straymark-baton = { path = "../experiment-baton" } tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "signal", "fs"] } axum = { version = "0.8", features = ["ws"] } From 6389cd8a768b30ae82af1d37ec97daedab555e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:03:18 -0600 Subject: [PATCH 2/3] =?UTF-8?q?chore(charter):=20split=20stamp-sweep=20row?= =?UTF-8?q?=20into=20one=20row=20per=20glob=20=E2=80=94=20drift=20extracti?= =?UTF-8?q?on=20reads=20one=20path=20per=20row=20(#419)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02-mechanical-verifiers-for-the-markdown-layer-419.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md b/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md index d3017cd6..7f0425e4 100644 --- a/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md +++ b/.straymark/charters/02-mechanical-verifiers-for-the-markdown-layer-419.md @@ -103,7 +103,9 @@ PR 3 — guard-closure in remediation AILOGs (fw + cli): | `experiment-loom/Cargo.toml` | `straymark-core` pin → 0.10.0 (added during PR 3 per closure protocol) | | `experiment-baton/Cargo.toml` | `straymark-core` pin → 0.10.0 (added during PR 3 per closure protocol) | | `dist/dist-manifest.yml` | `version:` → 4.44.0 (added during PR 3 per closure protocol) | -| `README.md`, `docs/i18n/*/README.md`, `dist/.straymark/00-governance/**` (21 files) | Footer stamp sweep fw-4.43.0 → fw-4.44.0 — follows the framework release like Cargo.lock follows cli bumps (added during PR 3 per closure protocol) | +| `README.md` | Footer stamp fw-4.43.0 → fw-4.44.0 — follows the framework release like Cargo.lock follows cli bumps (added during PR 3 per closure protocol) | +| `docs/i18n/*/README.md` | Same stamp sweep, localized READMEs (added during PR 3 per closure protocol) | +| `dist/.straymark/00-governance/**` | Same stamp sweep, governance footers incl. i18n (added during PR 3 per closure protocol) | | `cli/tests/validate_test.rs` | `--commit-msg` pass/fail, REF-003 warning, REF-001 error | | `cli/tests/followups_test.rs` | `--claims` batch cases over a fixture tree | | `cli/tests/charter_amend_test.rs` | Template renders `guard_closure` | From 1ecf1a19cae2f4d248252444da6406913ee9209a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:04:12 -0600 Subject: [PATCH 3/3] chore(charter): record gitignored root STRAYMARK.md as R5 in the PR-3 AILOG (#419) --- .../AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md b/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md index dd30fa05..d8636267 100644 --- a/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md +++ b/.straymark/07-ai-audit/agent-logs/AILOG-2026-08-14-002-guard-closure-remediation-ailogs.md @@ -133,6 +133,11 @@ a **Guard** line per item, so external audit cycles feed the field upstream. and the 21-file footer stamp sweep. Same category as PR 1's Cargo.lock row: mechanical followers of a version bump, recorded here and added to the Charter table in this same PR per the drift-check protocol. +- R5 (new, not in Charter): `STRAYMARK.md` (repo root) is declared in the + Charter and was edited on disk — §8, §13, §15.B — but can never appear + in a git drift range: `.gitignore` excludes it (operator-local mirror + kept in sync by `straymark update`). The tracked half of the change is + `dist/STRAYMARK.md`, modified in this PR. ## Validation