prompt: matched-close check + drop dead-code guard (argus #527 nits)
This commit is contained in:
parent
1c8e72c81f
commit
800d9925a5
1 changed files with 44 additions and 9 deletions
|
|
@ -52,7 +52,11 @@ pub fn render(flavor: Flavor, label: &str, operator_pronouns: &str) -> String {
|
||||||
/// Walk `template` line-by-line. Inside a `<!-- role:X -->` block,
|
/// Walk `template` line-by-line. Inside a `<!-- role:X -->` block,
|
||||||
/// suppress all lines unless `X == target`. Marker lines themselves are
|
/// suppress all lines unless `X == target`. Marker lines themselves are
|
||||||
/// always elided from the output. Unbalanced openers (no matching
|
/// always elided from the output. Unbalanced openers (no matching
|
||||||
/// closer) hold the suppression state until end-of-file.
|
/// closer) hold the suppression state until end-of-file. A mismatched
|
||||||
|
/// closer (`<!-- /role:manager -->` inside a `role:agent` block) is
|
||||||
|
/// elided from the output but does NOT reset the active role — keeps
|
||||||
|
/// the suppression conservative so a typo can't dump wrong-flavor
|
||||||
|
/// content (per argus #527 review nit).
|
||||||
fn filter_role_blocks(template: &str, target: &str) -> String {
|
fn filter_role_blocks(template: &str, target: &str) -> String {
|
||||||
let mut out = String::with_capacity(template.len());
|
let mut out = String::with_capacity(template.len());
|
||||||
// None = outside any block; Some(role) = inside role-tagged block.
|
// None = outside any block; Some(role) = inside role-tagged block.
|
||||||
|
|
@ -63,8 +67,12 @@ fn filter_role_blocks(template: &str, target: &str) -> String {
|
||||||
active_role = Some(role);
|
active_role = Some(role);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if parse_close_marker(trimmed).is_some() {
|
if let Some(close_role) = parse_close_marker(trimmed) {
|
||||||
active_role = None;
|
if active_role == Some(close_role) {
|
||||||
|
active_role = None;
|
||||||
|
}
|
||||||
|
// Mismatched close: elide the marker line but keep the
|
||||||
|
// active role intact so wrong-flavor content stays gated.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let include = match active_role {
|
let include = match active_role {
|
||||||
|
|
@ -82,15 +90,12 @@ fn filter_role_blocks(template: &str, target: &str) -> String {
|
||||||
/// `<!-- role:agent -->` → `Some("agent")`. Anything else returns
|
/// `<!-- role:agent -->` → `Some("agent")`. Anything else returns
|
||||||
/// None. Whitespace inside the marker is tolerated so a future
|
/// None. Whitespace inside the marker is tolerated so a future
|
||||||
/// author's `<!--role:foo-->` (no spaces) still parses; the dashboard
|
/// author's `<!--role:foo-->` (no spaces) still parses; the dashboard
|
||||||
/// markdown renderer is equally lenient.
|
/// markdown renderer is equally lenient. Close tags (`/role:...`)
|
||||||
|
/// can't accidentally match — the `strip_prefix("role:")` rejects
|
||||||
|
/// the leading slash before we'd ever see it.
|
||||||
fn parse_open_marker(line: &str) -> Option<&str> {
|
fn parse_open_marker(line: &str) -> Option<&str> {
|
||||||
let inside = line.strip_prefix("<!--")?.strip_suffix("-->")?.trim();
|
let inside = line.strip_prefix("<!--")?.strip_suffix("-->")?.trim();
|
||||||
let role = inside.strip_prefix("role:")?.trim();
|
let role = inside.strip_prefix("role:")?.trim();
|
||||||
// Reject closer-looking content ("/role:..." would start with "/")
|
|
||||||
// so `/role:agent` doesn't accidentally match here.
|
|
||||||
if role.starts_with('/') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(role)
|
Some(role)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,6 +187,36 @@ shared closer
|
||||||
assert_eq!(parse_close_marker("just text"), None);
|
assert_eq!(parse_close_marker("just text"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mismatched_close_keeps_active_role() {
|
||||||
|
// `<!-- role:agent -->` block with a stray `<!-- /role:manager -->`
|
||||||
|
// closer inside: the manager-tagged close must NOT pop the agent
|
||||||
|
// gate, else manager-target output would leak the agent block's
|
||||||
|
// text (or vice-versa). Stray marker line itself is still elided.
|
||||||
|
// Per argus #527 review nit.
|
||||||
|
let template = "shared\n\
|
||||||
|
<!-- role:agent -->\n\
|
||||||
|
agent line 1\n\
|
||||||
|
<!-- /role:manager -->\n\
|
||||||
|
agent line 2\n\
|
||||||
|
<!-- /role:agent -->\n\
|
||||||
|
shared end\n";
|
||||||
|
let manager = filter_role_blocks(template, "manager");
|
||||||
|
// Both agent lines stay gated out for the manager target; the
|
||||||
|
// stray close didn't accidentally pop the role. Stray marker
|
||||||
|
// itself elided from the output.
|
||||||
|
assert!(!manager.contains("agent line 1"));
|
||||||
|
assert!(!manager.contains("agent line 2"));
|
||||||
|
assert!(!manager.contains("<!--"));
|
||||||
|
assert!(manager.contains("shared"));
|
||||||
|
assert!(manager.contains("shared end"));
|
||||||
|
// Agent target still sees both lines (matched closer pops at the end).
|
||||||
|
let agent = filter_role_blocks(template, "agent");
|
||||||
|
assert!(agent.contains("agent line 1"));
|
||||||
|
assert!(agent.contains("agent line 2"));
|
||||||
|
assert!(agent.contains("shared end"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unbalanced_opener_suppresses_until_eof() {
|
fn unbalanced_opener_suppresses_until_eof() {
|
||||||
// Stray opener with no closer — content stays suppressed for
|
// Stray opener with no closer — content stays suppressed for
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue