diff --git a/.agents/AGENTS.reference.md b/.agents/AGENTS.reference.md index c5d9f89c..3f111089 100644 --- a/.agents/AGENTS.reference.md +++ b/.agents/AGENTS.reference.md @@ -6,6 +6,8 @@ This is the canonical, harness-agnostic guidance for agents working in this repo The human keeps delivery state in `.agents/work.toml`. It contains at most five ordered steps and uses only `active`, `pending`, and `complete` statuses. While work remains, `selected_action` names exactly one active step; it is omitted only after every step is complete. Before work starts, replace the starter text with a bounded user problem, change, acceptance criteria, and why-next rationale. Run `agent-flow next` for the current brief. +The four prose fields (`user_problem`, `change`, each `acceptance` item, and `why_next`) may hold paragraphs, so write them with a TOML multi-line string (`"""`) when one line will not state the problem honestly. The structural values stay on one line each: `selected_action`, every step `id`, every `blocked_by` id, and every status. Tabs, carriage returns, other control characters, and Unicode line or paragraph separators are rejected everywhere, in prose as well; only the line-feed paragraph break is prose-only. The `next` brief indents every continuation line, so prose cannot forge a heading of its own. + To start the selected action, copy `.agents/user-prompts/kickoff.md`, fill in its optional context, and paste it to the agent. `.agents/work.toml` is the only workflow task-state file. Do not create a plan tree, ledger, round log, findings directory, or review record. Product-development checks under `.agents/checks.toml`, `.agents/checks/`, and `.agents/hooks/` are optional tooling, not task state or proof of review. diff --git a/.agents/work.toml b/.agents/work.toml index 45bbf4b2..5926bc88 100644 --- a/.agents/work.toml +++ b/.agents/work.toml @@ -1,5 +1,5 @@ version = 1 -selected_action = "support-safe-work-paragraphs" +selected_action = "explore-general-review-surface" [[step]] id = "enforce-reset-guardrails" @@ -20,7 +20,7 @@ why_next = "Every later action is safer once a mechanical gate, not a promise, h [[step]] id = "support-safe-work-paragraphs" -status = "active" +status = "complete" blocked_by = ["enforce-reset-guardrails"] user_problem = "Every work field must fit one line, so a step cannot explain its problem without cramming it." change = "Let prose fields carry paragraphs while ids and structural fields stay single-line, and keep both projections safe." @@ -35,7 +35,7 @@ why_next = "Bounded state is only worth trusting if a step can state its problem [[step]] id = "explore-general-review-surface" -status = "pending" +status = "active" blocked_by = ["support-safe-work-paragraphs"] user_problem = "The scaffold supports delivery review, but not a standalone review of a current tree or a diff between two states." change = "Compare a compact prompt, a dedicated CLI command and no product surface, then recommend one." diff --git a/AGENTS.md b/AGENTS.md index c5d9f89c..3f111089 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,8 @@ This is the canonical, harness-agnostic guidance for agents working in this repo The human keeps delivery state in `.agents/work.toml`. It contains at most five ordered steps and uses only `active`, `pending`, and `complete` statuses. While work remains, `selected_action` names exactly one active step; it is omitted only after every step is complete. Before work starts, replace the starter text with a bounded user problem, change, acceptance criteria, and why-next rationale. Run `agent-flow next` for the current brief. +The four prose fields (`user_problem`, `change`, each `acceptance` item, and `why_next`) may hold paragraphs, so write them with a TOML multi-line string (`"""`) when one line will not state the problem honestly. The structural values stay on one line each: `selected_action`, every step `id`, every `blocked_by` id, and every status. Tabs, carriage returns, other control characters, and Unicode line or paragraph separators are rejected everywhere, in prose as well; only the line-feed paragraph break is prose-only. The `next` brief indents every continuation line, so prose cannot forge a heading of its own. + To start the selected action, copy `.agents/user-prompts/kickoff.md`, fill in its optional context, and paste it to the agent. `.agents/work.toml` is the only workflow task-state file. Do not create a plan tree, ledger, round log, findings directory, or review record. Product-development checks under `.agents/checks.toml`, `.agents/checks/`, and `.agents/hooks/` are optional tooling, not task state or proof of review. diff --git a/README.md b/README.md index 7f50529b..3dbea83b 100644 --- a/README.md +++ b/README.md @@ -178,10 +178,14 @@ agent-flow render --check --strict docs/plans/my-task.plan.toml When `.agents/work.toml` exists, bounded work mode is the default. The same source can be selected explicitly with `--source .agents/work.toml`. The version-1 file is at most 4,096 bytes, contains at most five total ordered `[[step]]` entries, and uses only `active`, `pending`, and `complete`. While any step remains active or pending, `selected_action` is required and names an active step; it may be absent only when every step is complete. Several steps may be active, but every blocker id must exist and an active step's blockers must already be complete; pending steps may depend on active or pending predecessors. +Fields split into prose and structure, and only prose may span lines. The four prose fields (`user_problem`, `change`, each `acceptance` item, and `why_next`) accept TOML multi-line strings, so a step can state its problem in paragraphs instead of cramming it onto one line. The structural values stay one line each: `selected_action`, every step `id`, every `blocked_by` id, and every status. Everything else stays unsafe in both kinds of field, prose included: tabs, carriage returns, any other control character, and the Unicode line (U+2028) and paragraph (U+2029) separators are rejected with the offending field named. Only the line-feed paragraph break is prose-only. + `validate` checks those invariants and exits nonzero with source-prefixed diagnostics on a violation. For an all-complete file it reports valid completion. It does not read plans, ledgers, workflow specs, review directories, or metrics logs in work mode, so a self-authored review record cannot change the result. `status` projects every ordered step, its status, and dependency ids with their current statuses, plus the selected action or no action after completion. Human and `--json` forms are deterministic and fail rather than truncate above 16,384 bytes. `next` lists every active unit in file order and one selected action with its user problem, change, acceptance criteria, and why-next rationale; after completion it lists no active units, no selected action, and an explicit completed result (`selected_action` is `null` in JSON). Pending-step prose and all legacy process files are not read by this path. Both `next` formats retain their 8,192-byte fail-rather-than-truncate limit. +Multiline prose keeps its paragraphs in both formats. The human brief prints the first line beside its label and indents every continuation line behind a ` |` gutter, blank paragraph lines included; since no top-level line of that output begins with a space, prose reading `SELECTED ACTION`, `acceptance:`, `- forged` or `why next:` arrives as the indented continuation it is and cannot forge a heading, an active-unit row, or an acceptance item. `--json` needs no gutter and preserves each accepted string exactly, line feeds and blank lines included. + ```sh # Bounded human brief from .agents/work.toml: agent-flow next diff --git a/pack/AGENTS.md b/pack/AGENTS.md index 9f3215db..145b0b8a 100644 --- a/pack/AGENTS.md +++ b/pack/AGENTS.md @@ -6,6 +6,8 @@ This is the canonical, harness-agnostic guidance for agents working in this repo The human keeps delivery state in `.agents/work.toml`. It contains at most five ordered steps and uses only `active`, `pending`, and `complete` statuses. While work remains, `selected_action` names exactly one active step; it is omitted only after every step is complete. Before work starts, replace the starter text with a bounded user problem, change, acceptance criteria, and why-next rationale. Run `agent-flow next` for the current brief. +The four prose fields (`user_problem`, `change`, each `acceptance` item, and `why_next`) may hold paragraphs, so write them with a TOML multi-line string (`"""`) when one line will not state the problem honestly. The structural values stay on one line each: `selected_action`, every step `id`, every `blocked_by` id, and every status. Tabs, carriage returns, other control characters, and Unicode line or paragraph separators are rejected everywhere, in prose as well; only the line-feed paragraph break is prose-only. The `next` brief indents every continuation line, so prose cannot forge a heading of its own. + To start the selected action, copy `.agents/user-prompts/kickoff.md`, fill in its optional context, and paste it to the agent. `.agents/work.toml` is the only workflow task-state file. Do not create a plan tree, ledger, round log, findings directory, or review record. Product-development checks under `.agents/checks.toml`, `.agents/checks/`, and `.agents/hooks/` are optional tooling, not task state or proof of review. diff --git a/src/next.rs b/src/next.rs index 64c3a914..888142a9 100644 --- a/src/next.rs +++ b/src/next.rs @@ -152,20 +152,62 @@ pub(crate) fn render_work_human(projection: &WorkProjection) -> String { out.push_str("\nSELECTED ACTION\n"); if let Some(action) = &projection.selected_action { + // The id is structural and single-line by construction (`work::reject_structural_text`), + // so it stays an ordinary inline substitution. out.push_str(&format!("id: {}\n", action.id)); - out.push_str(&format!("user problem: {}\n", action.user_problem)); - out.push_str(&format!("change: {}\n", action.change)); + push_prose(&mut out, "user problem:", &action.user_problem); + push_prose(&mut out, "change:", &action.change); out.push_str("acceptance:\n"); for criterion in &action.acceptance { - out.push_str(&format!("- {criterion}\n")); + push_prose(&mut out, "-", criterion); } - out.push_str(&format!("why next: {}", action.why_next)); + push_prose(&mut out, "why next:", &action.why_next); + // `why_next` is the last block, and this projection ends without a trailing + // newline (`emit_next_output` adds the one). Same idiom as `render_status_human`. + out.pop(); } else { out.push_str("none\n\nRESULT\ncompleted"); } out } +/// The gutter every continuation line of a multiline prose value carries. +/// +/// This is the whole heading-injection defence, and it works by exhausting the +/// alternative: EVERY top-level line of this projection starts at column zero with +/// `source:`, `ACTIVE UNITS`, `SELECTED ACTION`, `RESULT`, `id:`, `user problem:`, +/// `change:`, `acceptance:`, `why next:`, `none`, `completed`, `- ` or nothing at all. +/// None of those begins with a space, so a line that begins with this gutter is a +/// continuation and can be nothing else. The bar keeps a blank paragraph line visible +/// (and keeps it from being bare trailing whitespace) so the break survives a reader, +/// a copy, and a formatter. +const PROSE_CONTINUATION: &str = " |"; + +/// Append one labelled prose value: `