Skip to main content

kcode_k1_ktool_docs/
lib.rs

1#![forbid(unsafe_code)]
2
3use serde::{Deserialize, Serialize};
4
5const INVALID_ARGUMENTS: &str = "invalid KtoolDocs arguments";
6const UNKNOWN_KTOOL: &str = "unknown Ktool";
7
8struct Entry {
9    name: &'static str,
10    version: &'static str,
11    docs: &'static str,
12    replacement: Option<&'static str>,
13}
14
15impl Entry {
16    const fn new(name: &'static str, docs: &'static str) -> Self {
17        Self {
18            name,
19            version: "1.0.0",
20            docs,
21            replacement: None,
22        }
23    }
24}
25
26const RUST_CODE_CREATE: &str = r#"Invoke as RustCodeCreate with arguments matching exactly this JSON Schema: {"type":"object","properties":{"library":{"type":"string"},"version":{"type":"string"}},"required":["library","version"],"additionalProperties":false}. Example: {"library":"package-name","version":"1.2.3"}. library must be canonical lowercase-kebab and version must be a canonical stable version; both are exact and case-sensitive. The backend-selected profile supplies the authority; the model never supplies or chooses an authority. It starts one unpublished three-file library and emits editable Documentation.md, Cargo.toml, and ordered src/lib.rs Tool Messages. It fails if that exact authority-qualified library version is already published."#;
27const RUST_CODE_DOCS: &str = r#"Invoke as RustCodeDocs with arguments matching exactly this JSON Schema: {"type":"object","properties":{"library":{"type":"string"},"version":{"type":"string"}},"required":["library","version"],"additionalProperties":false}. Example: {"library":"package-name","version":"1.2.3"}. library must be canonical lowercase-kebab and version must be a canonical stable version; both are exact and case-sensitive. The backend-selected profile supplies the authority; the model never supplies or chooses an authority. It returns only the exact Documentation.md contents of that published supported three-file library. It does not establish an editable source snapshot, mutate source, or publish."#;
28const RUST_CODE_OPEN: &str = r#"Invoke as RustCodeOpen with arguments matching exactly this JSON Schema: {"type":"object","properties":{"library":{"type":"string"},"version":{"type":"string"}},"required":["library","version"],"additionalProperties":false}. Example: {"library":"package-name","version":"1.2.3"}. library must be canonical lowercase-kebab and version must be a canonical stable version; both are exact and case-sensitive. The backend-selected profile supplies the authority; the model never supplies or chooses an authority. It opens that published supported three-file library and emits editable Documentation.md, Cargo.toml, and ordered src/lib.rs Tool Messages. Those original Tool Message box IDs remain the edit targets for the active source snapshot."#;
29const RUST_CODE_OVERWRITE: &str = r#"Invoke as RustCodeOverwrite with arguments matching exactly this JSON Schema: {"type":"object","properties":{"box_id":{"type":"integer","minimum":1},"contents":{"type":"string"}},"required":["box_id","contents"],"additionalProperties":false}. Example: {"box_id":42,"contents":"complete replacement contents"}. box_id must be a positive JSON integer identifying one original editable Tool Message from the conversation's active RustCodeCreate or RustCodeOpen. contents completely replaces only that source segment. Copied, stale, overwrite-produced, or different-authority source IDs are rejected. Continue to address later replacements through the original active-source box ID. Before retaining the overwrite, if the affected complete file lacks a trailing LF, K1 silently appends exactly one. A file already ending in LF, including CRLF, remains byte-identical; no other bytes are changed. For segmented src/lib.rs, normalization applies only to the assembled file tail."#;
30const RUST_CODE_CHECK: &str = r#"Invoke as RustCodeCheck with arguments matching exactly this JSON Schema: {"type":"object","properties":{"box_id":{"type":"integer","minimum":1}},"required":["box_id"],"additionalProperties":false}. Example: {"box_id":42}. box_id must be a positive JSON integer from any original editable Tool Message in the conversation's active Rust source snapshot. Every call runs a fresh complete check. A different-authority or stale source snapshot is rejected. It returns only plain success or complete failure text and does not publish."#;
31const RUST_CODE_PUBLISH: &str = r#"Invoke as RustCodePublish with arguments matching exactly this JSON Schema: {"type":"object","properties":{"box_id":{"type":"integer","minimum":1}},"required":["box_id"],"additionalProperties":false}. Example: {"box_id":42}. box_id must be a positive JSON integer from any original editable Tool Message in the conversation's active Rust source snapshot. A different-authority or stale source snapshot is rejected. It reuses an unchanged valid success, replays an unchanged latest failure, or runs one fresh complete check when evidence is absent, changed, or stale. A failed check prevents publication. On success it preserves the exact source before one immutable public publication under the backend-selected profile authority."#;
32
33const WEB_CODE_DOCS: &str = r#"Invoke as WebCodeDocs with arguments matching exactly this JSON Schema: {"type":"object","properties":{"authority":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"}},"required":["authority","name","version"],"additionalProperties":false}. Example: {"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3"}. authority must be a canonical 24-character lowercase hexadecimal string, name must be canonical lowercase-kebab, and version must be a canonical stable version; all are exact and case-sensitive. With authority for the named private or public package version, it returns only that version's exact Documentation.md contents. It does not establish an Open, emit editable source, mutate source, or publish."#;
34const WEB_CODE_OPEN: &str = r#"Invoke as WebCodeOpen with arguments matching exactly this JSON Schema: {"type":"object","properties":{"authority":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"},"language":{"type":"string","enum":["javascript","html","css"]}},"required":["authority","name","version"],"additionalProperties":false}. Existing-package example: {"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3"}. Absent-package example: {"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3","language":"javascript"}. authority must be a canonical 24-character lowercase hexadecimal string, name must be canonical lowercase-kebab, and version must be a canonical stable version; all are exact and case-sensitive. language is required when the package is absent and, when supplied, must be exactly one of javascript, html, or css; for an existing package, it must match the existing language. It establishes the conversation's active Open for that identity. Documentation.md is editable Tool Message 1; code spans are editable in later Tool Messages and the terminal Tool Result."#;
35const WEB_CODE_OVERWRITE: &str = r#"Invoke as WebCodeOverwrite with arguments matching exactly this JSON Schema: {"type":"object","properties":{"box_id":{"type":"string"},"contents":{"type":"string"}},"required":["box_id","contents"],"additionalProperties":false}. Example: {"box_id":"42","contents":"complete replacement contents"}. box_id must be a canonical positive decimal string identifying an original editable output from the conversation's active WebCodeOpen; copied, discovered, stale, or overwrite-produced IDs are not accepted. contents completely replaces only that output. The box from Tool Message 1 is Documentation.md; boxes from later Tool Messages or the terminal Tool Result are code spans. Continue to address later replacements through the original active-Open box ID."#;
36const WEB_CODE_CHECK: &str = r#"Invoke as WebCodeCheck with arguments matching exactly this JSON Schema: {"type":"object","properties":{"authority":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"}},"required":["authority","name","version"],"additionalProperties":false}. Example: {"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3"}. authority must be a canonical 24-character lowercase hexadecimal string, name must be canonical lowercase-kebab, and version must be a canonical stable version; all are exact and case-sensitive and must match the conversation's active WebCodeOpen. Every call runs a fresh complete check of the active source. A successful check retains same-source success evidence for WebCodePublish; a failed check retains the failed-check state for that source."#;
37const WEB_CODE_PUBLISH: &str = r#"Invoke as WebCodePublish with arguments matching exactly this JSON Schema: {"type":"object","properties":{"authority":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"}},"required":["authority","name","version"],"additionalProperties":false}. Example: {"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3"}. authority must be a canonical 24-character lowercase hexadecimal string, name must be canonical lowercase-kebab, and version must be a canonical stable version; all are exact and case-sensitive and must match the conversation's active WebCodeOpen. It reuses retained successful check evidence only while the source is unchanged, suppresses publication after an unchanged retained failed check, and otherwise runs a fresh complete precheck. On success it creates the source Object before exactly one public immutable release. It performs no adoption, restart, deployment, or live-behavior change."#;
38
39static CATALOG: [Entry; 24] = [
40    Entry::new(
41        "KtoolDocs",
42        r#"Invoke as KtoolDocs {"name":"<exact Ktool name>"}. Arguments must be exactly one nonempty string field, name; matching is exact and case-sensitive. Success returns compact JSON with exactly name, latest_version, docs, deprecated, and replacement. Malformed arguments return invalid KtoolDocs arguments; an unrecognized exact name returns unknown Ktool. The operation is stateless metadata lookup only and does not list, search, suggest, authorize, register, or prove live availability."#,
43    ),
44    Entry::new(
45        "CurrentTime",
46        r#"Invoke as CurrentTime {}. Returns current system UTC in RFC3339 with exactly three fractional digits and Z. Any nonempty or nonobject arguments return invalid CurrentTime arguments. It is stateless and needs no Kmap authorization."#,
47    ),
48    Entry::new(
49        "KmapCreateNode",
50        r#"Invoke as KmapCreateNode {"title":string,"navigation_hint":string,"narrative":string,"connections":[string,...]}. Each connection must be an exact 24-character lowercase hexadecimal Kmap node ID. Using the active Kmap authorization, it creates one node under the bound profile and policy, adds the supplied Navigation connections, marks the new node loaded, and returns Node successfully created with id <id>. Malformed arguments or IDs return invalid KmapCreateNode arguments; missing authorization and Kmap dependency failures remain errors."#,
51    ),
52    Entry::new(
53        "KmapOpenNode",
54        r#"Invoke as KmapOpenNode {"node_id":string,"budget":number}. node_id must be an exact 24-character lowercase hexadecimal Kmap node ID and budget must be finite and nonnegative. It opens in Full mode at temperature 1, records loaded nodes and first-pull provenance, and returns only components not already returned during the session. If no new components remain, it returns No new Kmap node components. Malformed arguments return invalid KmapOpenNode arguments; missing authorization and Kmap dependency failures remain errors."#,
55    ),
56    Entry::new(
57        "KmapUpdateNode",
58        r#"Invoke as KmapUpdateNode {"node_id":string,"title":string,"navigation_hint":string,"narrative":string,"connections":[string,...]}. All IDs must be exact 24-character lowercase hexadecimal Kmap node IDs. It replaces the node text fields, additively upserts the supplied Navigation connections, marks the updated node loaded, and returns success. Naming another node does not itself load that node. Malformed arguments return invalid KmapUpdateNode arguments; missing authorization and Kmap dependency failures remain errors."#,
59    ),
60    Entry::new(
61        "KmapPenalizeNodes",
62        r#"Invoke as KmapPenalizeNodes {"node_ids":[string,...]}. Every ID must be an exact 24-character lowercase hexadecimal Kmap node ID. It deduplicates the input and applies one noncritical negative measurement to each newly penalized eligible loaded node with first-pull provenance. Penalized nodes are excluded from the session’s later KmapConnectNodes operation. Unknown, unloaded, or already penalized valid IDs have no additional effect. Success returns success. Malformed arguments return invalid KmapPenalizeNodes arguments; missing authorization and Kmap dependency failures remain errors."#,
63    ),
64    Entry::new(
65        "KmapConnectNodes",
66        r#"Invoke as KmapConnectNodes {}. Using the active Kmap authorization, it adds every missing directed Automated connection among loaded, unpenalized session nodes. Existing connections remain unchanged and self-connections are not added. Success returns success. Nonempty or nonobject arguments return invalid KmapConnectNodes arguments; missing authorization and Kmap dependency failures remain errors."#,
67    ),
68    Entry::new(
69        "SendMessage",
70        r#"Invoke as SendMessage {"message":string}. Arguments must contain exactly one nonempty message string. It durably appends one visible Agent Message to the authenticated current conversation between the Tool Call and Tool Result, returns success, and leaves the provider turn open for continued generation. It has no recipient argument, network effect, or cross-conversation capability. Malformed arguments return invalid SendMessage arguments without appending a message; persistence failures remain errors."#,
71    ),
72    Entry::new(
73        "WebSearch",
74        r#"Invoke as WebSearch {"query":string,"model":"codex/<model>","reasoning_effort":string?}. Arguments are strict: query and model are required strings, model must have the exact case-sensitive codex/ prefix, and reasoning_effort is optional, non-null, and defaults to medium. It starts one isolated asynchronous search with an absolute 60-minute deadline and emits no progress. Malformed arguments return invalid WebSearch arguments. This metadata does not authorize a provider call or prove runtime availability."#,
75    ),
76    Entry::new(
77        "SetLaunchNode",
78        r#"Invoke as SetLaunchNode {"target":string,"node_id":string}. Arguments are strict: target is validated as an exact target name and node_id must be a visible AccessId of exactly 24 lowercase hexadecimal characters. With current-user authority, it creates or updates that target’s launch-node reference. It makes no runtime-selection claim."#,
79    ),
80    Entry::new(
81        "ListContacts",
82        r#"Invoke as ListContacts {}. Arguments are strict and must be an empty object. It returns the complete caller-owned exact Known Contacts union. Every row has exactly user_id, username, person_id, full_name, and nullable kmap_launch_node. Authorization and dependency failures remain errors."#,
83    ),
84    Entry::new(
85        "ListGroups",
86        r#"Invoke as ListGroups {}. Arguments are strict and must be an empty object. It returns complete visible ordinary human memberships, excluding Known Contacts and filtered groups. Every row has exactly group_id, name, revision, caller_role, and kmap_launch_node. It returns no counts. Authorization and dependency failures remain errors."#,
87    ),
88    Entry::new(
89        "GetGroup",
90        r#"Invoke as GetGroup {"group_id":string}. Arguments are strict and require group_id. Mere user-or-active-model visibility is sufficient. It returns exactly top-level fields group_id, name, revision, kmap_launch_node, users, and models; each user row has user_id, username, person_id, full_name, role, and kmap_launch_node; each model row has model_id and nullable name. It returns no messages, history, narrative, or counts. Authorization and dependency failures remain errors."#,
91    ),
92    Entry::new("RustCodeCreate", RUST_CODE_CREATE),
93    Entry::new("RustCodeDocs", RUST_CODE_DOCS),
94    Entry::new("RustCodeOpen", RUST_CODE_OPEN),
95    Entry::new("RustCodeOverwrite", RUST_CODE_OVERWRITE),
96    Entry::new("RustCodeCheck", RUST_CODE_CHECK),
97    Entry::new("RustCodePublish", RUST_CODE_PUBLISH),
98    Entry::new("WebCodeDocs", WEB_CODE_DOCS),
99    Entry::new("WebCodeOpen", WEB_CODE_OPEN),
100    Entry::new("WebCodeOverwrite", WEB_CODE_OVERWRITE),
101    Entry::new("WebCodeCheck", WEB_CODE_CHECK),
102    Entry::new("WebCodePublish", WEB_CODE_PUBLISH),
103];
104
105#[derive(Deserialize)]
106#[serde(deny_unknown_fields)]
107struct Arguments {
108    name: String,
109}
110
111#[derive(Serialize)]
112struct Response<'a> {
113    name: &'a str,
114    latest_version: &'static str,
115    docs: &'a str,
116    deprecated: bool,
117    replacement: Option<&'a str>,
118}
119
120fn find_entry(name: &str) -> Option<&'static Entry> {
121    CATALOG.iter().find(|entry| entry.name == name)
122}
123
124fn render(entry: &Entry) -> Result<String, String> {
125    let response = Response {
126        name: entry.name,
127        latest_version: entry.version,
128        docs: entry.docs,
129        deprecated: entry.replacement.is_some(),
130        replacement: entry.replacement,
131    };
132    serde_json::to_string(&response)
133        .map_err(|error| format!("KtoolDocs response serialization failed: {error}"))
134}
135
136/// Returns whether `name` is an exact, case-sensitive catalog name.
137pub fn is_known_ktool(name: &str) -> bool {
138    find_entry(name).is_some()
139}
140
141/// Looks up the exact Ktool name supplied in a strict JSON argument object.
142pub fn ktool_docs(arguments: &str) -> Result<String, String> {
143    let arguments: Arguments =
144        serde_json::from_str(arguments).map_err(|_| INVALID_ARGUMENTS.to_owned())?;
145    if arguments.name.is_empty() {
146        return Err(INVALID_ARGUMENTS.to_owned());
147    }
148    let entry = find_entry(&arguments.name).ok_or_else(|| UNKNOWN_KTOOL.to_owned())?;
149    render(entry)
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use serde_json::Value;
156
157    fn arguments_for(name: &str) -> String {
158        format!("{{\"name\":{}}}", serde_json::to_string(name).unwrap())
159    }
160
161    #[test]
162    fn every_catalog_entry_has_an_exact_five_field_active_response() {
163        assert_eq!(CATALOG.len(), 24);
164        for entry in &CATALOG {
165            let output = ktool_docs(&arguments_for(entry.name)).unwrap();
166            let value: Value = serde_json::from_str(&output).unwrap();
167            let object = value.as_object().unwrap();
168            assert_eq!(object.len(), 5);
169            assert_eq!(object.get("name").and_then(Value::as_str), Some(entry.name));
170            assert_eq!(
171                object.get("latest_version").and_then(Value::as_str),
172                Some("1.0.0")
173            );
174            assert_eq!(object.get("docs").and_then(Value::as_str), Some(entry.docs));
175            assert_eq!(
176                object.get("deprecated").and_then(Value::as_bool),
177                Some(false)
178            );
179            assert!(object.get("replacement").is_some_and(Value::is_null));
180        }
181    }
182
183    #[test]
184    fn known_lookup_uses_the_same_exact_catalog() {
185        for entry in &CATALOG {
186            assert!(is_known_ktool(entry.name));
187        }
188        for name in ["websearch", " WebSearch", "GetGroup ", "Unknown"] {
189            assert!(!is_known_ktool(name));
190            assert_eq!(
191                ktool_docs(&arguments_for(name)),
192                Err(UNKNOWN_KTOOL.to_owned())
193            );
194        }
195        assert!(!is_known_ktool(""));
196        assert_eq!(
197            ktool_docs(&arguments_for("")),
198            Err(INVALID_ARGUMENTS.to_owned())
199        );
200    }
201
202    #[test]
203    fn new_contracts_state_the_approved_boundaries() {
204        let docs = |name| find_entry(name).unwrap().docs;
205        assert!(docs("WebSearch").contains("exact case-sensitive codex/ prefix"));
206        assert!(docs("WebSearch").contains("defaults to medium"));
207        assert!(docs("WebSearch").contains("one isolated asynchronous search"));
208        assert!(docs("WebSearch").contains("absolute 60-minute deadline"));
209        assert!(docs("SetLaunchNode").contains("visible AccessId"));
210        assert!(docs("SetLaunchNode").contains("current-user authority"));
211        assert!(docs("SetLaunchNode").contains("creates or updates"));
212        assert!(docs("ListContacts").contains("complete caller-owned exact Known Contacts union"));
213        assert!(docs("ListGroups").contains("excluding Known Contacts and filtered groups"));
214        assert!(docs("ListGroups").contains("no counts"));
215        let get_group = docs("GetGroup");
216        assert!(get_group.contains("Mere user-or-active-model visibility"));
217        assert!(get_group.contains(
218            "top-level fields group_id, name, revision, kmap_launch_node, users, and models"
219        ));
220        assert!(
221            get_group
222                .contains("user_id, username, person_id, full_name, role, and kmap_launch_node")
223        );
224        assert!(get_group.contains("model_id and nullable name"));
225        assert!(get_group.contains("no messages, history, narrative, or counts"));
226        assert!(!get_group.contains("caller_role"));
227
228        for name in ["RustCodeCreate", "RustCodeDocs", "RustCodeOpen"] {
229            let contract = docs(name);
230            assert!(contract.contains(r#""library":{"type":"string"}"#));
231            assert!(contract.contains(r#""version":{"type":"string"}"#));
232            assert!(contract.contains(r#""required":["library","version"]"#));
233            assert!(contract.contains(r#""additionalProperties":false"#));
234            assert!(contract.contains("backend-selected profile supplies the authority"));
235            assert!(contract.contains("model never supplies or chooses an authority"));
236            assert!(!contract.contains(r#""authority""#));
237        }
238        assert!(docs("RustCodeCreate").contains("emits editable"));
239        assert!(docs("RustCodeDocs").contains("returns only the exact Documentation.md"));
240        assert!(docs("RustCodeOpen").contains("ordered src/lib.rs Tool Messages"));
241        let overwrite = docs("RustCodeOverwrite");
242        assert!(overwrite.contains(r#""box_id":{"type":"integer","minimum":1}"#));
243        assert!(overwrite.contains("complete replacement"));
244        assert!(overwrite.contains("original active-source box ID"));
245        assert!(overwrite.contains("silently appends exactly one"));
246        assert!(overwrite.contains("including CRLF"));
247        assert!(overwrite.contains("assembled file tail"));
248        assert!(docs("RustCodeCheck").contains("Every call runs a fresh complete check"));
249        assert!(docs("RustCodePublish").contains("runs one fresh complete check"));
250        assert!(docs("RustCodePublish").contains("failed check prevents publication"));
251        assert!(docs("RustCodePublish").contains("backend-selected profile authority"));
252    }
253
254    #[test]
255    fn web_code_names_are_exact_and_near_misses_are_unknown() {
256        for name in [
257            "WebCodeDocs",
258            "WebCodeOpen",
259            "WebCodeOverwrite",
260            "WebCodeCheck",
261            "WebCodePublish",
262        ] {
263            assert!(is_known_ktool(name));
264            assert!(ktool_docs(&arguments_for(name)).is_ok());
265        }
266        for name in [
267            "webCodeDocs",
268            "WebcodeOpen",
269            "WebCodeOverWrite",
270            "WebCodeChecks",
271            "WebCodePublish ",
272            "WebCodeList",
273            "WebCodeSearch",
274        ] {
275            assert!(!is_known_ktool(name));
276            assert_eq!(
277                ktool_docs(&arguments_for(name)),
278                Err(UNKNOWN_KTOOL.to_owned())
279            );
280        }
281    }
282
283    #[test]
284    fn web_code_contracts_freeze_schemas_examples_and_lifecycle() {
285        let docs = |name| find_entry(name).unwrap().docs;
286        let identity_schema = r#"{"type":"object","properties":{"authority":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"}},"required":["authority","name","version"],"additionalProperties":false}"#;
287        let identity_example =
288            r#"{"authority":"0123456789abcdef01234567","name":"package-name","version":"1.2.3"}"#;
289        let invalid_identity_example =
290            r#"{"authority":"owner","name":"package","version":"1.2.3"}"#;
291        for name in ["WebCodeDocs", "WebCodeCheck", "WebCodePublish"] {
292            let contract = docs(name);
293            assert!(contract.contains(identity_schema));
294            assert!(contract.contains(identity_example));
295            assert!(!contract.contains(invalid_identity_example));
296        }
297        for name in [
298            "WebCodeDocs",
299            "WebCodeOpen",
300            "WebCodeCheck",
301            "WebCodePublish",
302        ] {
303            let contract = docs(name);
304            assert!(contract.contains("canonical 24-character lowercase hexadecimal string"));
305            assert!(contract.contains("name must be canonical lowercase-kebab"));
306            assert!(contract.contains("version must be a canonical stable version"));
307        }
308
309        let open = docs("WebCodeOpen");
310        assert!(
311            open.contains(r#""language":{"type":"string","enum":["javascript","html","css"]}"#)
312        );
313        assert!(open.contains(r#""language":"javascript""#));
314        assert!(!open.contains(r#""language":"rust""#));
315        assert!(open.contains("exactly one of javascript, html, or css"));
316        assert!(open.contains("language is required when the package is absent"));
317        assert!(open.contains("must match the existing language"));
318        assert!(open.contains("conversation's active Open"));
319        assert!(open.contains("Documentation.md is editable Tool Message 1"));
320        assert!(open.contains("terminal Tool Result"));
321
322        let overwrite = docs("WebCodeOverwrite");
323        assert!(overwrite.contains(r#""box_id":{"type":"string"}"#));
324        assert!(overwrite.contains(r#""contents":{"type":"string"}"#));
325        assert!(
326            overwrite.contains(r#"{"box_id":"42","contents":"complete replacement contents"}"#)
327        );
328        assert!(!overwrite.contains("box-id-from-open"));
329        assert!(overwrite.contains("canonical positive decimal string"));
330        assert!(overwrite.contains("original editable output"));
331        assert!(overwrite.contains("Tool Message 1 is Documentation.md"));
332        assert!(overwrite.contains("later Tool Messages or the terminal Tool Result"));
333
334        let check = docs("WebCodeCheck");
335        assert!(check.contains("Every call runs a fresh complete check"));
336        assert!(check.contains("same-source success evidence"));
337        assert!(check.contains("failed-check state"));
338
339        let publish = docs("WebCodePublish");
340        assert!(publish.contains("source is unchanged"));
341        assert!(publish.contains("unchanged retained failed check"));
342        assert!(publish.contains("fresh complete precheck"));
343        assert!(publish.contains("source Object before exactly one public immutable release"));
344        assert!(publish.contains("no adoption, restart, deployment, or live-behavior change"));
345    }
346
347    #[test]
348    fn malformed_arguments_are_strictly_rejected() {
349        for input in [
350            "",
351            "null",
352            "[]",
353            "{}",
354            r#"{"name":""}"#,
355            r#"{"name":1}"#,
356            r#"{"name":"KtoolDocs","extra":false}"#,
357            r#"{"name":"KtoolDocs","name":"CurrentTime"}"#,
358        ] {
359            assert_eq!(
360                ktool_docs(input),
361                Err(INVALID_ARGUMENTS.to_owned()),
362                "{input:?}"
363            );
364        }
365    }
366
367    #[test]
368    fn response_serialization_escapes_strings() {
369        let entry = Entry {
370            name: "quote\"",
371            version: "1.0.0",
372            docs: "line\n",
373            replacement: None,
374        };
375        let output = render(&entry).unwrap();
376        assert!(output.contains("\\\""));
377        assert!(output.contains("\\n"));
378        assert_eq!(
379            serde_json::from_str::<Value>(&output).unwrap()["docs"],
380            "line\n"
381        );
382    }
383}