{
  "summary": "YES: ordinary operator text containing literal &quot; reaches both Claude adapter report calls unchanged (state busy payload and now-signal --user-input). Ordinary actual quotes are NOT XML-escaped on that hook/report path. Peer-delivery-generated escaped quotes are blocked from state-busy USER_INPUT by explicit stub/<EVENT guards, but CAN reach now-signal --user-input through the real full-envelope delivery fallback/override, whose translator preserves XML entities and whose now-signal call lacks that guard. Normal peer delivery instead parks raw XML, types a stub, and decodes the body only into additionalContext. These are current-checkout source findings associated with a matching 0.41.2 manifest, not binary equivalence proof.",
  "files": [
    {
      "path": "C:/Users/decid/AppData/Local/spt-core/adapters/_github/SaberMage-claude-spt/manifest.toml",
      "description": "Installed version 0.41.2 at line 972; translation command at 1536–1537; hook binary mapping at 1747. Bundle contains claude-spt.exe, no Rust source in inspected listing."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/src/hook.rs",
      "description": "Hook JSON parsing 5362–5367; prompt extraction 223–224 and 2973–2975; park/poll rendering and busy report 3140–3219; now-signal call 3263–3277; report/cap/guard logic 4031–4148; byte-preserving process transport 4945–4967 and 5016–5029."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/src/translate.rs",
      "description": "Normal stub/park and full-envelope fallback implementation: 395–469, 500–552, 603–646; full_inject override 343–357; runtime 668–721. Full-envelope typing retains &quot;, not decoded."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/src/stub.rs",
      "description": "Whole-prompt stub recognition at 54–82; home-anchored park location at 29–51."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-claude-code/plugin/sptc/hooks/dispatch.sh",
      "description": "Checkout plugin resolver uses cached SPTC_HOOK_BIN or installed hook_cmd and execs binary with untouched hook stdin, lines 41–66."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-claude-code/plugin/sptc/hooks/hooks.json",
      "description": "Checkout UserPromptSubmit wiring to dispatch.sh at lines 6–7."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-core/crates/spt-proto/src/envelope.rs",
      "description": "Current core checkout body XML escaping at 38–45; amp-last inverse at 56–61."
    },
    {
      "path": "C:/Users/decid/Documents/projects/spt-core/crates/spt-proto/src/event.rs",
      "description": "Current core checkout EVENT serialization calls event_body_escape(body) at 446 and closes EVENT at 447."
    }
  ],
  "architecture": "CC hook JSON → unmodified prompt string → independent state-busy stdin and now-signal argv paths. Peer idle EVENT → normal raw-XML park + tiny prompt stub → hook render_frames decodes body into additionalContext; fallback/full_inject instead types framed raw EVENT. Only state-busy payload applies the peer-delivery guard.",
  "report": "## Provenance and scope\nInstalled manifest declares 0.41.2 (manifest.toml:972), message-idle-translation-binary.command = \"{adapter_dir}/claude-spt translate\" (1536–1537), and strings.hook_cmd = \"{adapter_dir}/claude-spt\" (1747). Installed directory lists claude-spt.exe and claude-spt.exe.old plus manifest/strings/state and a Linux binary subtree; no source was present in that inspected bundle listing. All behavioral Rust citations below are from C:/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/src, not decompiled/verified shipped code. Checkout adapter/claude-spt.toml also declares 0.41.2 at line 972; Cargo.toml package version is merely 0.1.0, not adapter release identity. Matching manifest versions do NOT establish installed binary/source equality. Existing resident translators can also lag a newly installed executable; installed manifest itself documents this at lines 289–293. Plugin wiring inspected is checkout wiring, not proof of which cached plugin/session resolver is currently active.\n\n## 1. Ordinary operator prompt: concrete YES for literal entities, NO adapter encoding of real quotes\nCheckout plugin hooks.json:6–7 dispatches UserPromptSubmit; dispatch.sh:41–66 resolves the cached SPTC_HOOK_BIN or adapter hook_cmd, strips a legacy trailing ` hook` from the binary name, and execs `\"$bin\" hook \"$event\" --host-pid \"$PPID\"`. It explicitly inherits hook stdin untouched; get-string uses /dev/null and cannot consume the payload.\n\nhook.rs:5362–5367 reads stdin into a String and serde_json::from_str parses it. field():223–224 selects the JSON string with as_str().to_string(); handle_user_prompt_submit():2973–2975 selects prompt. JSON escapes such as \\\" become ordinary quotes; XML entity strings such as &quot; are not JSON escapes and remain literal. There is no HTML/XML conversion on this input leg.\n\nProvided resolved_id proves a live owned endpoint (2996 onward), the busy report at 3175 calls report_busy_with_input with the original prompt, independently of peer additionalContext assembled alongside it. user_input_payload():4111–4117 trims only for its rejection predicate: empty, exact recognized stub, or starts_with(\"<EVENT\") returns None; otherwise it returns the ORIGINAL prompt unchanged, including original surrounding whitespace. report_busy_with_input():4130–4148 sends `spt api --adapter claude-spt state busy <id> --session-id <sid> --payload-stdin`, with that string as stdin. SysEnv::spt_strict_stdin():5016–5029 uses Command::args plus write_all(body.as_bytes()), with no XML transform. On refusal it retries a payload-free busy mark, rather than publishing alternate text.\n\nSeparately, hook.rs:3263–3277 polls now-signal when prompt is nonblank and identity exists. skill_key(prompt).is_empty() permits turn text; a recognized adapter skill invocation suppresses it. now_signal_arg():4089–4091 trims and takes at most 4000 Unicode characters; it does not decode or encode entities. now_signal_poll():4039–4043 passes that string as a single --user-input argv value. SysEnv::spt_with_env():4954–4955 uses Command::args, not shell interpolation.\n\nThus a normal prompt `Please inspect &quot;C:/work/a.txt&quot;` can supply exactly that entity-bearing text to BOTH calls (assuming text fits the now-signal cap and API accepts the report). A normal prompt `Please inspect \"C:/work/a.txt\"` supplies actual quotes to BOTH calls; this adapter does not manufacture &quot; from those quotes. This is a real authored-input path, distinct from message rendering.\n\n## 2. Core-origin peer message: normal stub route decodes only at additionalContext boundary\nCurrent core checkout event.rs:446 serializes body via event_body_escape; envelope.rs:38–45 encodes ampersand first, then < > and double quote, then normalizes CRLF/CR and encodes LF as <br>. A body containing actual `\"C:/work/a.txt\"` therefore becomes `&quot;C:/work/a.txt&quot;` inside EVENT. A sender literally typing `&quot;C:/work/a.txt&quot;` instead produces `&amp;quot;C:/work/a.txt&amp;quot;`.\n\nActual current Claude translator DOES NOT universally decode this before PTY injection. Normal message-like delivery follows translate.rs:634–638: stub_delivery() recognizes an EVENT with nonempty body and usable from attribute (395–412), park_envelope() writes the raw envelope to the home-anchored msgpark directory (452–469), then commands_for_msg_stub() types only `<msg from=\"sender\"/>` and discrete Enter (418–427). No filepath/body is in the hook prompt on this route.\n\nThe UserPromptSubmit hook drains parked raw envelopes (3140 onward) and renders them separately into `out`. render_frames():425–427 extracts the body then calls unescape; unescape():235–240 changes <br> to LF, &lt;/&gt;/&quot; to their characters, and &amp; LAST. The body therefore yields actual quotes when core encoded actual quotes, but preserves literal &quot; when that was sender-authored. render_frames():461–475 retains unconsumed attribute values in escaped wire form and emits `<sptc_messages ...>\\n{body}\\n</sptc_messages>`. This is hook additionalContext, not a replacement of the variable `prompt` and not a state USER_INPUT payload. Poll delivery similarly renders into `out` after the busy report.\n\nThe stub itself is rejected by user_input_payload, so state busy carries no USER_INPUT body for this peer turn. now-signal is NOT guarded by user_input_payload and normally gets the tiny `<msg from=\"sender\"/>` string, not the parked peer body. Wake similarly types `<wake/>`; its actual parked directive is injected separately into additionalContext.\n\n## 3. Real full-envelope route: escaped body CAN enter now-signal, cannot enter busy USER_INPUT\nThere is a concrete source-reachable full path, not just historical behavior:\n- Structured `json` attr carrying {\"full_inject\":\"v1\"} is decoded/recognized by translate.rs:350–357, and dispatch_event():634 bypasses stub+park.\n- Park failure (no initialized endpoint, no HOME/USERPROFILE, filesystem failure) also falls through at 639–645.\n- An ineligible stub envelope, e.g. empty body carrying attrs, likewise falls through.\n\ncommands_for_event():537–552 changes raw CR/LF to spaces, inserts structural newlines through frame_envelope(), and emits the complete EVENT as text. It NEVER entity-decodes the body. Thus actual quoted peer paths are still &quot;path&quot; in the physical prompt under this path. After Claude passes the prompt as hook JSON, serde_json preserves these entity bytes.\n\nState busy: user_input_payload():4113 rejects every trimmed prompt starting `<EVENT` (also catches `<EVENT-PART` prefixes), so it sends a payload-free busy mark. The report is bypassed, not decoded or stripped into bare body. Therefore this checked path does NOT provide adapter-generated escaped peer quotes to core's state-busy USER_INPUT extraction.\n\nnow-signal: the independent 3269 call uses now_signal_arg(prompt) without the stub/EVENT exclusion. A full EVENT is not an adapter skill command. Consequently its first 4000 trimmed characters, including `&quot;path&quot;` for a near-front path, can reach `spt api --adapter claude-spt now-signal <id> --session <sid> --spec-manifest --user-input <full EVENT>`. This is a concrete adapter-side consumer seam worth routing to the claude-spt hook/report owner if the intended now-signal contract forbids peer text there. It is NOT proof that now-signal republishes a core USER_INPUT event; the two API surfaces must not be conflated. Core consumer behavior/conclusion remains parent's investigation.\n\n## 4. Probe invocation, not executed\n`claude-spt translate` is documented in main.rs:89 and translate.rs:30–40 as a stdin/stdout JSON-lines filter. It is NOT generally non-mutating: init starts interrupt_watch (translate.rs:702–708) and ordinary initialized delivery writes park files. There is no dedicated dry-run flag in the inspected entry point (reads no argv).\n\nA specifically non-mutating, source-grounded offline full-envelope probe can omit init and use the full_inject override, avoiding both watcher and park; stdout is only choreography JSON, not applied to any PTY. Example PowerShell invocation for parent consideration:\n\n'{\"type\":\"event\",\"envelope\":\"<EVENT type=\\\"msg\\\" from=\\\"probe\\\" json=\\\"{&quot;full_inject&quot;:&quot;v1&quot;}\\\">Inspect &quot;C:/work/a.txt&quot;</EVENT>\"}' | & 'C:/Users/decid/AppData/Local/spt-core/adapters/_github/SaberMage-claude-spt/claude-spt.exe' translate\n\nExpected from CHECKOUT SOURCE, not observed executable output: a text command containing the framed EVENT with &quot;C:/work/a.txt&quot; intact, surrounded by stash/delay/Enter/commit commands. Do not use init merely to probe: it adds real effects.\n\n## Bounded conclusion\nThe source census establishes YES for literal operator-authored &quot; on both report calls; no XML encoding of ordinary operator quotes; NO peer body on state-busy USER_INPUT through the checked normal/stale/full-envelope routes due to explicit guard; and YES adapter-generated XML entities can survive a real full-envelope route into now-signal --user-input. Normal stub delivery decodes message bodies only in additionalContext. No tests, builds, formatters, runtime probes, edits, remote access, lifecycle operations, or fleet messages were performed. These findings are limited to the named Claude adapter paths and current source; they do not assert universal absence across harnesses or binary provenance."
}
