From a462e7f0971cf7fb9798f95127442e54a27c49c1 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Tue, 2 Jun 2026 00:26:40 +0200 Subject: [PATCH] test(examples): guard docs against low-level plumbing Extend the canonical example contract to scan Markdown docs so they keep teaching generated ergonomic APIs instead of raw handles, registry/form types, or legacy HTML helpers. req: dx/002 req: dx/003 req: examples/003 --- slhx-test/tests/examples_contract.rs | 37 +++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/slhx-test/tests/examples_contract.rs b/slhx-test/tests/examples_contract.rs index 335cd79..9faf976 100644 --- a/slhx-test/tests/examples_contract.rs +++ b/slhx-test/tests/examples_contract.rs @@ -99,6 +99,41 @@ fn canonical_examples_do_not_author_low_level_resource_plumbing() { assert!(failures.is_empty(), "{}", failures.join("\n")); } +#[test] +fn canonical_example_docs_do_not_teach_low_level_plumbing() { + // req: dx/002 req: dx/003 req: examples/003 + let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap(); + let examples = root.join("examples"); + let forbidden = [ + "generated handle ids", + "numeric handle ids", + "manual registry", + "HandlerRegistry", + "InteractionForm", + "register_handle(", + "lower_html(", + "render_html(", + "SafeHtml::trusted", + ]; + let mut failures = Vec::new(); + scan_examples(&examples, &mut |path, text| { + if path.extension().and_then(|ext| ext.to_str()) != Some("md") { + return; + } + for (line_no, line) in text.lines().enumerate() { + if let Some(token) = forbidden.iter().find(|token| line.contains(*token)) { + failures.push(format!( + "{}:{}: example docs must teach generated ergonomic APIs, not `{token}`", + path.display(), + line_no + 1 + )); + } + } + }); + + assert!(failures.is_empty(), "{}", failures.join("\n")); +} + #[test] fn browser_e2e_does_not_shortcut_product_interactions() { // req: examples/005 @@ -135,7 +170,7 @@ fn scan_examples(dir: &Path, visit: &mut impl FnMut(&Path, &str)) { fn is_example_source(path: &PathBuf) -> bool { matches!( path.extension().and_then(|ext| ext.to_str()), - Some("rs" | "heml" | "html") + Some("rs" | "heml" | "html" | "md") ) }