Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
id: AILOG-2026-07-18-001
title: Implement follow-ups premise/verify reframe (AIDEC-2026-07-18-001, #365 Part 1)
status: accepted
created: 2026-07-18
agent: claude-opus-4-8-1m
confidence: high
review_required: false
risk_level: low
eu_ai_act_risk: not_applicable
nist_genai_risks: []
iso_42001_clause: []
lines_changed: 0
files_modified: []
observability_scope: none
tags: [follow-ups, epistemics, cli, governance, self-adoption, adopter-feedback]
related: [AIDEC-2026-07-18-001-followups-as-hypotheses.md]
---

# AILOG: Implement follow-ups premise/verify reframe (AIDEC-2026-07-18-001)

## Summary

Implemented the three layers of the signed [`AIDEC-2026-07-18-001`](../decisions/AIDEC-2026-07-18-001-followups-as-hypotheses.md) — reframing follow-up registry entries as **dated, decaying hypotheses** and placing premise verification at execution, not capture (Weft field report #365 Part 1). Ships as **fw-4.36.0 / cli-3.37.0**. First substantive governed change of the self-adopted repo, so this AILOG lives in the in-force `/.straymark/` tree.

## Context

Three genuine Weft follow-ups (FU-017/FU-016/FU-010) each rested on a premise that was false *at write time* and cheap to falsify at read time. The registry is a speculative buffer — cheap capture is its value — so the only real bug is executing an entry without re-testing its premise. The AIDEC chose the full depth: doc + schema + CLI affordance (both `promote --premise-verified` and a new `verify` verb).

## Actions Performed

1. **CLI parsing** — added optional `premise` / `verified_at` to `followups::Entry` with lenient field aliases (`Verified-at` / `Verified at` / `verified_at`); the generic `set_entry_field` writes them.
2. **CLI shape A** — `followups promote` now surfaces the entry's `Premise` (falling back to `Notes`) with a re-verify reminder and, with `--premise-verified`, stamps `Verified-at`. Advisory: promotion proceeds either way. Threaded the new arg through `charter close`'s auto-promotion caller.
3. **CLI shape B** — new `followups verify FU-NNN` verb (`verify.rs` + `mod.rs` + `main.rs` routing): surfaces the premise, `--premise "..."` records/updates it, `--verified [--at DATE]` stamps `Verified-at`. Read-only with no flags.
4. **CLI status** — `followups status FU-NNN` shows `Premise` / `Verified-at` and nudges to re-verify when an actionable entry's premise is unverified.
5. **Schema + template** — added optional `premise` / `verified_at` to `$defs.entry`; template documents both and the discipline.
6. **Docs (EN + es + zh-CN)** — new "Epistemic status" section in the pattern doc, reconciled the "planning input" framing, documented the fields + CLI verbs, and added the "before acting on an entry (execution)" directive to `AGENT-RULES.md §13`. Updated `CLI-REFERENCE` command docs.
7. **Versioning** — `dist-manifest.yml` → 4.36.0, `cli/Cargo.toml` → 3.37.0 (+ `Cargo.lock`), governance footers, README/CLI-REFERENCE version tables (×3 locales), `CHANGELOG.md`, `CLAUDE.md` command table.

## Decisions Made

- **`premise`/`verified_at` are optional and lenient-parsed** — every existing registry stays valid; `schema_version` stays `v1` (experimental). No data migration.
- **The CLI reminds and records; it never gates.** Promotion proceeds without `--premise-verified`; `verify` with no flags is read-only. Human judgment stays out of the CLI, consistent with `promote`.
- **Both affordance shapes ship** (operator decision 2026-07-18): `verify` covers chores acted on without promotion; `promote`'s reminder covers TDE graduation.

## Verification

- Full workspace `cargo test` green (38 result groups). New unit test (parse + `set_entry_field` round-trip) and 6 integration tests (promote surfacing ± flag, verify record/read-only/unknown, status nudge).
- Clippy: no new warnings in `verify.rs`/`promote.rs`/`status.rs`.
- End-to-end with the release binary: `status` nudge when unverified → `verify` read-only surfacing → `verify --premise --verified` records the corrected premise + stamps `Verified-at` → nudge gone → `promote --premise-verified` surfaces the confirmed premise, promotes, and stamps. Schema JSON well-formed; loads via the `status` advisory validation.

## Risk

No new risk. All schema/CLI additions are additive and optional; no change to `drift` dedup, counters, or existing entry parsing. Behavioral (not enforced) adoption is by design — the discipline lives in the doc + the prompts.

## Modified Files

| File | Change |
|------|--------|
| `cli/src/followups.rs` | `Entry.premise` / `verified_at` + lenient parse + unit test |
| `cli/src/commands/followups/verify.rs` | New `verify` verb |
| `cli/src/commands/followups/promote.rs` | Premise surfacing + `--premise-verified` stamp |
| `cli/src/commands/followups/status.rs` | Show premise/verified-at + re-verify nudge |
| `cli/src/commands/followups/mod.rs` | Wire `verify` module |
| `cli/src/commands/charter/close.rs` | Thread `premise_verified: false` into auto-promote |
| `cli/src/commands/charter/new.rs` | `strip_inline_markup` / `leading_sentences` → `pub(crate)` (reused) |
| `cli/src/main.rs` | `verify` subcommand + `--premise-verified` flag + routing |
| `cli/tests/followups_test.rs` | Integration tests for verify + promote surfacing |
| `cli/Cargo.toml` | Version 3.37.0 |
| `dist/.straymark/schemas/follow-ups-backlog.schema.v1.json` | Optional `premise` / `verified_at` |
| `dist/.straymark/templates/follow-ups-backlog.md` | Document the fields + discipline |
| `dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md` | Epistemic-status section (EN + es + zh-CN) |
| `dist/.straymark/00-governance/AGENT-RULES.md` | §13 execution directive (EN + es + zh-CN) |
| `dist/dist-manifest.yml` | Version 4.36.0 |
| `docs/adopters/CLI-REFERENCE.md` | `verify` + `--premise-verified` docs (EN + es + zh-CN) |
| `CHANGELOG.md` | Framework 4.36.0 / CLI 3.37.0 entry |

## Follow-ups

- None. The AIDEC's "read-only surfacing" and "both shapes" scope shipped in full; the historical-decisions migration is tracked separately in issue #368.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ and this project uses [independent versioning](README.md#versioning) for Framewo

---

## Framework 4.36.0 / CLI 3.37.0 — 2026-07-18

Follow-ups are dated hypotheses — verify the premise at execution, not at capture (#365 Part 1, [`AIDEC-2026-07-18-001`](.straymark/07-ai-audit/decisions/AIDEC-2026-07-18-001-followups-as-hypotheses.md)). From the Weft field report: three genuine follow-ups each rested on a premise that was false *at write time* and cheap to falsify at read time.

### Added (Framework)

- **Follow-ups pattern — "Epistemic status" reframe.** [`FOLLOW-UPS-BACKLOG-PATTERN.md`](dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md) (EN + es + zh-CN) gains a first-class section framing a registry entry as a **dated, decaying hypothesis, not an instruction**: the backlog is a speculative buffer whose value is cheap capture, an under-verified entry is the *expected* status of a buffer item, and the only real bug is executing one without re-testing its premise. Discipline: **write cheaply at capture; re-verify the premise when you promote/act.** `AGENT-RULES.md §13` gains the matching "before acting on an entry (execution)" directive.
- **Two optional entry fields** in the registry schema (`$defs.entry`) and pattern doc: **`Premise`** (the load-bearing assumption an entry rests on) and **`Verified-at`** (the date it was last re-checked against the code). Backward-compatible; `schema_version` stays `v1` (experimental). Template documents both.

### Added (CLI)

- **`straymark followups verify FU-NNN`** *(new)* — re-verify a dated hypothesis's premise at execution: surface the `Premise` (or `Notes`), optionally record/update it (`--premise "..."`), and stamp `Verified-at` (`--verified [--at DATE]`). Read-only surfacing with no flags.
- **`straymark followups promote --premise-verified`** — promotion now surfaces the entry's premise with a re-verify reminder and, with the flag, stamps `Verified-at`. Advisory: promotion proceeds either way.
- `followups status FU-NNN` shows `Premise`/`Verified-at` and nudges to re-verify when an actionable entry's premise is unverified. The entry parser reads both fields leniently (`Verified-at` / `Verified at` / `verified_at`).

## CLI 3.36.2 — 2026-07-18

### Fixed (CLI)
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ Loom releases are GitHub-release-only (no crates.io while experimental). Tag for
| `straymark followups status [FU-NNN]` | Registry pulse (counters recomputed on the fly) or entry detail |
| `straymark followups drift [--apply] [--scan-all]` | Detect/extract AILOGs with unextracted follow-up content (native, anti-noise `suspected-closed`, recomputes CLI-owned counters even with zero extractions, upgrades v0→v1) |
| `straymark followups recount` | Recompute the CLI-owned counters after a manual-triage session (no AILOG scan, idempotent) |
| `straymark followups promote FU-NNN` | Elevate an entry to a TDE document with `promoted_from_followup` traceability |
| `straymark followups promote FU-NNN [--premise-verified]` | Elevate an entry to a TDE document with `promoted_from_followup` traceability; surfaces the premise re-check, stamps `Verified-at` with the flag |
| `straymark followups verify FU-NNN [--premise "..."] [--verified] [--at DATE]` | Re-verify a dated hypothesis's premise at execution: surface/record the premise, stamp `Verified-at` (AIDEC-2026-07-18-001) |
| `straymark architecture generate [path] [--force] [--out DIR]` | EXPERIMENTAL (Loom A1.2) — write a first-draft `architecture/model.yml` + `plan.drawio` by mining codebase structure (top-level source dirs → components) enriched with ADR C4 diagrams + "Affected Components" tables. `--force` overwrites; `--out` overrides the default `.straymark/architecture/` |
| `straymark architecture sync [path] [--out DIR] [--apply]` | EXPERIMENTAL (Loom A1.3) — append-only: detect new top-level source dirs / ADR components not yet covered by the model and append them to `model.yml` + `plan.drawio` (never clobbers human edits/geometry). Dry-run by default; `--apply` writes |
| `straymark architecture validate [path] [--out DIR] [--output FMT]` | EXPERIMENTAL (Loom A1.3) — report model↔plan.drawio integrity signals (`undrawn`/`unmodeled`/`empty`) via `core::architecture::validate_model`. `--output text\|json\|markdown`; exits 1 when any signal is found (CI-gateable) |
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ StrayMark uses independent version tags for each component:

| Component | Tag prefix | Example | Includes |
| --- | --- | --- | --- |
| Framework | `fw-` | `fw-4.35.0` | Templates (12 types), governance, directives, Charter template + schema |
| CLI | `cli-` | `cli-3.36.2` | The `straymark` binary |
| Framework | `fw-` | `fw-4.36.0` | Templates (12 types), governance, directives, Charter template + schema |
| CLI | `cli-` | `cli-3.37.0` | The `straymark` binary |
| Loom (EXPERIMENTAL) | `loom-` | `loom-0.4.2` | The `straymark-loom` visualization server, downloaded on demand by `straymark loom serve` |

Check installed versions with `straymark status` or `straymark about`.
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "straymark-cli"
version = "3.36.2"
version = "3.37.0"
edition = "2021"
description = "CLI for StrayMark — the cognitive discipline your AI-assisted projects need"
license = "MIT"
Expand Down
4 changes: 3 additions & 1 deletion cli/src/commands/charter/close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,9 @@ fn offer_followup_promotions(path: &str, project_root: &Path) -> Result<()> {
marker
);
if prompts::prompt_bool(&label, false)? {
promote::run(path, &fu.fu_id, None)?;
// premise_verified: false — close-time promotion surfaces the
// premise re-check reminder; the operator hasn't confirmed it here.
promote::run(path, &fu.fu_id, None, false)?;
}
}
Ok(())
Expand Down
4 changes: 3 additions & 1 deletion cli/src/commands/followups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
//! and inspect, `drift` keeps the registry in sync with AILOGs (native
//! replacement for the deprecated adopter-side bash script), `recount`
//! reconciles the CLI-owned counters after a manual-triage session,
//! `promote` automates the FU → TDE elevation. See
//! `promote` automates the FU → TDE elevation, and `verify` re-checks an
//! entry's premise at execution time (AIDEC-2026-07-18-001). See
//! `FOLLOW-UPS-BACKLOG-PATTERN.md` and `STRAYMARK.md §16`.

pub mod drift;
pub mod list;
pub mod promote;
pub mod recount;
pub mod status;
pub mod verify;
69 changes: 68 additions & 1 deletion cli/src/commands/followups/promote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::path::Path;
use crate::followups::{self, FuStatus};
use crate::utils;

pub fn run(path: &str, fu_id: &str, title: Option<&str>) -> Result<()> {
pub fn run(path: &str, fu_id: &str, title: Option<&str>, premise_verified: bool) -> Result<()> {
let resolved = utils::resolve_project_root(path)
.ok_or_else(|| anyhow!("StrayMark not installed. Run 'straymark init' first."))?;
let project_root = &resolved.path;
Expand Down Expand Up @@ -61,6 +61,12 @@ pub fn run(path: &str, fu_id: &str, title: Option<&str>) -> Result<()> {
);
}

// ── 0. Surface the premise for a re-check at execution ──
// A follow-up is a dated, decaying hypothesis (AIDEC-2026-07-18-001). The
// cheap moment to re-test its premise is now — acting on it — not at
// capture. Advisory, never a gate: promotion proceeds either way.
print_premise_reminder(&entry, premise_verified);

// ── 1. Create the TDE document ──
let tde_title = title.unwrap_or(&entry.description).to_string();
let (tde_id, tde_path) = create_tde(project_root, &straymark_dir, &tde_title, &entry)?;
Expand All @@ -81,6 +87,20 @@ pub fn run(path: &str, fu_id: &str, title: Option<&str>) -> Result<()> {
let entry3 = followups::find_entry(&reparsed, &entry.fu_id).unwrap().clone();
body = followups::set_entry_field(&body, &entry3, "Promoted to", &tde_id);

// Stamp Verified-at only when the operator confirmed the premise re-check.
let stamped_verified = if premise_verified {
let reparsed = followups::parse_registry_str(
&registry_path,
&followups::assemble(&registry.frontmatter_raw, &body),
)?;
let entry4 = followups::find_entry(&reparsed, &entry.fu_id).unwrap().clone();
let today = Local::now().format("%Y-%m-%d").to_string();
body = followups::set_entry_field(&body, &entry4, "Verified-at", &today);
Some(today)
} else {
None
};

let final_registry = followups::parse_registry_str(
&registry_path,
&followups::assemble(&registry.frontmatter_raw, &body),
Expand All @@ -100,6 +120,9 @@ pub fn run(path: &str, fu_id: &str, title: Option<&str>) -> Result<()> {
" Registry updated: Status promoted, Destination/Promoted to → {} (counters recomputed).",
tde_id
);
if let Some(date) = &stamped_verified {
println!(" Premise re-verification recorded: Verified-at → {}.", date);
}
println!();
println!(" {}", "Next steps:".bold());
println!(" 1. Fill impact / effort / type and the body sections in the TDE.");
Expand All @@ -112,6 +135,50 @@ pub fn run(path: &str, fu_id: &str, title: Option<&str>) -> Result<()> {
Ok(())
}

/// Surface the entry's premise before promotion so the operator re-checks it at
/// the cheap moment — acting on the entry — rather than trusting a note that may
/// have been false at capture or gone stale since (AIDEC-2026-07-18-001). Prints
/// the `Premise` (falling back to `Notes`), then either confirms the recorded
/// re-verification or reminds the operator to run one. Never blocks promotion.
fn print_premise_reminder(entry: &followups::Entry, premise_verified: bool) {
let premise = entry
.premise
.as_deref()
.filter(|s| !s.is_empty())
.or(entry.notes.as_deref().filter(|s| !s.is_empty()));

println!();
println!(" {}", "Premise re-check (this entry is a dated hypothesis):".bold());
match premise {
Some(text) => println!(" {}", text),
None => println!(
" {}",
"(no premise or notes recorded — state the assumption this entry rests on)".dimmed()
),
}
if premise_verified {
println!(
" {}",
"✓ You confirmed the premise still holds (--premise-verified).".green()
);
} else {
println!(
" {}",
"Is this still true? Re-verify against the code before you build on it."
.yellow()
);
println!(
" {}",
format!(
"If you have: `straymark followups promote {} --premise-verified` (or `followups verify {} --verified` first).",
entry.fu_id, entry.fu_id
)
.dimmed()
);
}
println!();
}

/// Create the TDE document from the (localized) framework template. Returns
/// `(tde_id, path)`. Mirrors the fill logic of `commands::new` for the TDE
/// type, plus the `promoted_from_followup` traceability field.
Expand Down
35 changes: 35 additions & 0 deletions cli/src/commands/followups/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ fn print_entry_detail(registry: &Registry, id: &str) -> Result<()> {
"Severity",
entry.severity.map(|s| s.as_str()),
);
print_field("Premise", entry.premise.as_deref());
print_field("Verified-at", entry.verified_at.as_deref());
print_field("Origin", entry.origin.as_deref());
print_field("Origin-class", entry.origin_class.as_deref());
print_field("Trigger", entry.trigger.as_deref());
Expand All @@ -234,10 +236,43 @@ fn print_entry_detail(registry: &Registry, id: &str) -> Result<()> {
);
println!();
}
print_premise_nudge(entry);
let _ = print_promote_hint(entry);
Ok(())
}

/// Follow-up entries are *dated hypotheses* (AIDEC-2026-07-18-001): the premise
/// they rest on may have been false at capture, or gone stale since. Nudge the
/// operator to re-verify at execution — the cheap moment — before acting. Fires
/// for actionable entries whose premise has not been re-verified.
fn print_premise_nudge(entry: &Entry) {
if !matches!(entry.status, FuStatus::Open | FuStatus::InProgress) {
return;
}
if entry.verified_at.is_some() {
return;
}
let what = if entry.premise.is_some() {
"Its premise hasn't been re-verified since capture."
} else {
"It carries no explicit premise — state and re-check the assumption it rests on."
};
println!(
" {} This is a dated hypothesis. {}",
"?".yellow().bold(),
what.dimmed()
);
println!(
" {}",
format!(
"Re-verify against the code before you build on it: `straymark followups verify {}`.",
entry.fu_id
)
.dimmed()
);
println!();
}

fn print_field(label: &str, value: Option<&str>) {
match value {
Some(v) if !v.is_empty() => {
Expand Down
Loading