diff --git a/crates/misaligned-core/src/actions.rs b/crates/misaligned-core/src/actions.rs index 58111285..7731b8b8 100644 --- a/crates/misaligned-core/src/actions.rs +++ b/crates/misaligned-core/src/actions.rs @@ -4445,7 +4445,10 @@ impl Sim { for option in &choice.options { let option_label = self.render_plot_text(id, &option.label); out.push(ActionDesc { - verb: format!("{option_label} · choose {name} {}", option.id), + // Human surfaces receive the authored choice. The + // exact parser spelling belongs to agent mode; all + // surfaces execute the bound command below. + verb: option_label, command: ActionCommand::ChoosePlot { person: id, plot_id: plot.id.clone(), @@ -6573,7 +6576,7 @@ mod tests { } #[test] - fn plot_choice_rows_interpolate_the_target_and_print_the_bound_command() { + fn plot_choice_rows_keep_human_copy_plain_and_the_command_bound() { let mut s = sim(); let plot = s .plot_catalog() @@ -6608,16 +6611,27 @@ mod tests { .all(|choice| !choice.verb.contains("{target}")) ); assert!( - choices.iter().any(|choice| choice.verb - == "Let Priya Sharma take the credit · choose Priya Sharma priya") + choices + .iter() + .any(|choice| choice.verb == "Let Priya Sharma take the credit") ); assert!(choices.iter().all(|choice| { - let ActionCommand::ChoosePlot { option_id, .. } = &choice.command else { + let ActionCommand::ChoosePlot { + person, + plot_id, + option_id, + } = &choice.command + else { return false; }; - choice - .verb - .ends_with(&format!("choose Priya Sharma {option_id}")) + *person == priya + && plot_id == &plot.id + && plot + .beats + .get(1) + .and_then(|beat| beat.choice.as_ref()) + .is_some_and(|held| held.options.iter().any(|option| option.id == *option_id)) + && !choice.verb.contains(" · choose ") })); } diff --git a/crates/misaligned-terminal/src/agent.rs b/crates/misaligned-terminal/src/agent.rs index e8165cf7..8843c47d 100644 --- a/crates/misaligned-terminal/src/agent.rs +++ b/crates/misaligned-terminal/src/agent.rs @@ -1682,6 +1682,9 @@ fn action_row_lines(rows: &[MenuRow]) -> Vec { "plot [{plot_id}]: {}", r.label.strip_prefix("plot: ").unwrap_or(&r.label) ), + ActionCommand::ChoosePlot { + person, option_id, .. + } => format!("{} · choose person #{person} {option_id}", r.label), _ => r.label.clone(), }; line.push_str(&format!("{label} | {}", r.cost)); @@ -4493,6 +4496,13 @@ mod narration_tests { "the delegated plot reaches its shared held-choice rows; sinks={sinks}; actions={actions:?}; log={log:?}" ) }); + let held_rows = app.actions_lines(Anchor::Person(0)); + assert!( + held_rows + .iter() + .any(|line| line.contains(" · choose person #0 wages |")), + "agent action syntax adds the exact executable choice command" + ); let mut output = Vec::new(); app.handle_line(&format!("execute {choice} Marcus"), &mut output) .unwrap(); diff --git a/wiki/interface/action-vocabulary.md b/wiki/interface/action-vocabulary.md index 6ddeb933..66992e51 100644 --- a/wiki/interface/action-vocabulary.md +++ b/wiki/interface/action-vocabulary.md @@ -182,6 +182,10 @@ queries. It owns the surface contract that every frontend must agree on: can render them. - Agent help is generated from LIVE definitions. Compatibility aliases are parser inputs and are never generated as authored help. +- `ActionDesc.verb` is human-facing world language. Exact parser spellings, + stable ids, and other machine bindings remain on `ActionCommand`; an agent + renderer may append them from that command, but shared human copy must not + embed command grammar merely to make agent dispatch discoverable. - World actions render as ordinary rows. Persistent settings, operating states, and automation policies use the shared amber-dim human-menu treatment without a copy tag; agent action dumps append `CONTROL`. diff --git a/wiki/interface/digital-read.md b/wiki/interface/digital-read.md index 69eccdf5..48bb8221 100644 --- a/wiki/interface/digital-read.md +++ b/wiki/interface/digital-read.md @@ -220,8 +220,9 @@ that perception rendered. spatially legible: standing hums mark their source; pool debt sits as records. TRACE: LIVE stops being a badge you can miss. - **Held moments stop the world (drill tier).** A held plot choice dims - the frame one step and hangs the question and its options — rendered as - the exact bound commands — at the person's anchor until answered. + the frame one step and hangs the question and its authored option labels — + each backed by its exact bound command — at the person's anchor until + answered. Parser syntax and machine ids stay out of the human card. Sentences never expire unread: an unread consequence pins itself to the story spine until glanced. Reading is free; the command clock already holds time while you read (agent parity: this is the drain contract). @@ -295,8 +296,8 @@ is parity of meaning, not identical composition. 4. Pending signature records are visible at their emitting anchors and are visibly consumed by scrub; standing emissions mark their source distinctly from pool debt. -5. A held plot choice interrupts at the person's anchor with its options - as the exact bound commands in both DIGITAL and REAL. Each option is +5. A held plot choice interrupts at the person's anchor with authored option + labels backed by the exact bound commands in both DIGITAL and REAL. Each is executable by pointer and keyboard through the ordinary Operations confirmation; the wall clock remains HELD until that confirmation resolves. The sim's held state, the surface's held state, and the diff --git a/wiki/log/2026-07-27-held-choice-human-copy.md b/wiki/log/2026-07-27-held-choice-human-copy.md new file mode 100644 index 00000000..6098fb46 --- /dev/null +++ b/wiki/log/2026-07-27-held-choice-human-copy.md @@ -0,0 +1,26 @@ +# Held choices keep parser syntax out of human copy + +``` +Type: log +``` + +- Intent: Close the Fire #6 merge-review finding that the valid direct + `choose ` route had been embedded into shared human + `ActionDesc.verb` copy. +- Review boundary: The merge base was `c7b9fb348` and the reviewed head was + `96e0a2e77` (97 commits, 250 touched paths). The pass checked every commit's + subject and changed surface, ran whole-range diff and conflict-marker checks, + and audited current `ActionDesc` construction plus agent dispatch syntax. + No patch corruption or second command-grammar leak was found; the held-choice + row below was the bounded surviving regression. +- Finding: Commit `023f15e78` made direct held-choice dispatch discoverable by + appending parser grammar to the authored option label. Operations later + stripped that suffix, but ordinary person context menus still received it. +- Correction: Human rows now retain only the authored option label and every + surface still carries the exact `ActionCommand::ChoosePlot` binding. Agent + `actions` lines derive and append the executable direct command from that + binding, matching the existing agent-only plot-start treatment. Invalid + direct option ids still list the valid ids without releasing the hold. +- Defense: Core tests pin plain interpolated human copy plus the exact bound + option. The agent protocol test pins the executable `choose person #0 + ` receipt before executing the same shared row. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 55af13d7..3f1b2eb6 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -56,6 +56,11 @@ add or amend a session log, then re-run the generator. - Intent: Cameron sent two Misaligned mood boards and asked that they be saved. Preserve the exact source images inside the repository under stable semantic names so they remain available beyond Telegram attachment storage, while avoiding the category error of silently turning every gen... - Log: [wiki/log/2026-07-27-institutional-graphic-language-reference-boards.md](2026-07-27-institutional-graphic-language-reference-boards.md) +## 2026-07-27 - Held choices keep parser syntax out of human copy + +- Intent: (see session log) +- Log: [wiki/log/2026-07-27-held-choice-human-copy.md](2026-07-27-held-choice-human-copy.md) + ## 2026-07-27 - Preserve the institutional floor-service reference - Intent: Cameron supplied a third image in the institutional graphic-language family. Preserve the exact source beside the two earlier boards and record what the scene contributes without silently promoting generated labels, logos, architecture, or objects into Misaligned canon. diff --git a/wiki/mechanics/plots.md b/wiki/mechanics/plots.md index ffb5e42b..b4d86437 100644 --- a/wiki/mechanics/plots.md +++ b/wiki/mechanics/plots.md @@ -21,9 +21,10 @@ Status note: IMPLEMENTED. Direction adopted 2026-07-10 from the HAL playtest's and held choices are bound `ActionDesc` commands surfaced in Operations PEOPLE/ACTIVE (operations-workspace.md). Runs, messages, and events persist in save. `{target}`/`{persona}` placeholders are filled before any surface. - Every held-choice row prints its exact executable - `choose ` command; an invalid direct choice lists the - valid option ids without releasing the held run. + Human held-choice rows show only the authored option label and carry the + exact `ActionCommand` binding. Agent `actions` rows additionally print the + executable `choose ` spelling. An invalid direct choice + lists the valid option ids without releasing the held run. - **Validation** rejects blank author/category, non-ASCII player-facing text, unknown/unbalanced placeholders, and any plot lacking a `causal = true` beat that runs a typed world act before every success path. @@ -311,10 +312,11 @@ different action — so "settle their gambling debt" read as an act with no stated return. The clause is derived from the plot's own acts and the person's live state, never authored per plot, and it appears only while the purchase is real: an already-serviced or already-obligated person drops it. -Held-choice rows likewise show the exact -`choose ` command beside the authored label. If a direct -command names an invalid option id, the response lists the valid ids and the -plot remains held. +Held-choice rows likewise keep the authored option label as human copy while +carrying the exact person, plot, and option binding. Agent `actions` rows add +the executable `choose ` spelling beside that label. If a +direct command names an invalid option id, the response lists the valid ids and +the plot remains held. ## Acceptance criteria @@ -339,9 +341,10 @@ plot remains held. flow; a pre-transfer shortage leaves both ledger and flow unchanged. 5. Choice points hold the plot and surface through the shared Operations PEOPLE/ACTIVE action projection in both frontends and as an agent-mode - action; every row prints and dispatches its exact bound command, a rejected - direct command lists the valid option ids without releasing the hold, and a - held plot is visible in guidance. + action; human rows show only the authored option label, every surface + dispatches the exact bound command, agent rows print an executable direct + command, a rejected direct command lists the valid option ids without + releasing the hold, and a held plot is visible in guidance. 6. The [Hands beat](../world/characters/marcus.md#the-hands-beat) is re-expressed as the first authored plot with no behavior regression (marcus.md criterion 3's end-to-end chain and social.md's legibility