[pitboss/grind] deferred session-0013 (20260516T052512Z-20f8)

This commit is contained in:
pitboss 2026-05-16 07:53:03 -05:00
parent b8207a1d1c
commit a2cc5f7700
4 changed files with 270 additions and 7 deletions

View file

@ -83,14 +83,22 @@ impl LangEmitter for GoEmitter {
/// Phase 26 — Go chain-step harness.
///
/// Emits a `main.go` driver that reads `NYX_PREV_OUTPUT` and forwards it
/// on stdout. The Go probe shim (`__nyx_probe`) is top-level Go code
/// requiring extra stdlib imports; chain steps keep the harness minimal
/// and rely on the sandbox runner's outer probe channel to observe the
/// final sink fire. Wiring the probe shim into chain steps is tracked
/// alongside the Phase 15 emitter follow-up about probe shim splicing.
/// Splices the Go probe shim ([`probe_shim`]) ahead of a minimal driver
/// that reads `NYX_PREV_OUTPUT` and forwards it on stdout. The composite
/// re-verifier swaps the trailing forward for the next member's
/// payload-injection prologue when running a multi-step chain; the shim
/// has to be in the same compilation unit so a chain step that terminates
/// at a sink can drive the `__nyx_probe` channel directly.
///
/// Imports are the union of the driver imports (`fmt`, `os`) and the
/// shim's [`SHIM_IMPORTS`], deduped + sorted so `go run step.go`
/// compiles in a single command.
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
let source = "package main\n\nimport (\n \"fmt\"\n \"os\"\n)\n\nfunc main() {\n prev := os.Getenv(\"NYX_PREV_OUTPUT\")\n fmt.Print(prev)\n}\n".to_owned();
let imports = chain_step_imports();
let shim = probe_shim();
let driver =
"func main() {\n prev := os.Getenv(\"NYX_PREV_OUTPUT\")\n fmt.Print(prev)\n}\n";
let source = format!("package main\n\nimport (\n{imports})\n{shim}\n{driver}");
ChainStepHarness {
source,
filename: "step.go".to_owned(),
@ -106,6 +114,27 @@ fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
}
}
/// Sorted, deduped tab-prefixed import lines covering the driver's
/// `fmt` + `os` plus everything in [`SHIM_IMPORTS`].
fn chain_step_imports() -> String {
let driver_imports: &[&str] = &["fmt", "os"];
let mut all: Vec<&str> = driver_imports
.iter()
.copied()
.chain(SHIM_IMPORTS.iter().copied())
.collect();
all.sort_unstable();
all.dedup();
let mut out = String::new();
for path in &all {
out.push('\t');
out.push('"');
out.push_str(path);
out.push_str("\"\n");
}
out
}
// ── Phase 15: shape detector ─────────────────────────────────────────────────
/// Concrete per-file shape resolved by reading the entry source.
@ -846,4 +875,42 @@ mod tests {
);
}
}
#[test]
fn chain_step_splices_probe_shim_for_composite_reverify() {
let step = chain_step(Some(b"<prev>"));
assert!(
step.source.contains("__nyx_probe"),
"Go chain step must splice the probe shim"
);
assert!(
step.source.starts_with("package main"),
"Go chain step must open with package main"
);
assert!(
step.source.contains("os.Getenv(\"NYX_PREV_OUTPUT\")"),
"Go chain step must keep its NYX_PREV_OUTPUT forwarder"
);
let import_close = step.source.find(")\n").expect("import block must close");
let shim_pos = step.source.find("__nyx_probe").unwrap();
let main_pos = step.source.find("func main()").unwrap();
assert!(
import_close < shim_pos,
"probe shim must come after the import block",
);
assert!(
shim_pos < main_pos,
"probe shim must come before func main() so its helpers are in scope when a sink rewrite splices in",
);
for path in SHIM_IMPORTS {
let quoted = format!("\"{path}\"");
assert!(
step.source.contains(&quoted),
"Go chain step must merge shim-required import {quoted} into its import block",
);
}
// Driver imports preserved alongside the shim imports.
assert!(step.source.contains("\"fmt\""));
assert!(step.source.contains("\"os\""));
}
}

View file

@ -332,6 +332,26 @@ function __nyx_install_crash_guard(string $sinkCallee): void {
}
}
}
// Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
// publishes the queries-log path through NYX_SQL_LOG; a sink call site that
// wants the host-side stub to see its query appends one record-per-call. The
// helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
// runs under harness modes that didn't spawn a stub. Mirrors the Python and
// Node shims so the host-side SqlStub log-line format (hash-space-prefixed
// detail lines, then the query line) is identical across language emitters.
function __nyx_stub_sql_record($query, array $detail = []): void {
$p = getenv('NYX_SQL_LOG');
if ($p === false || $p === '') return;
$buf = '';
foreach ($detail as $k => $v) {
$buf .= '# ' . (string)$k . ': ' . (string)$v . "\n";
}
$q = (string)$query;
$buf .= $q;
if (substr($q, -1) !== "\n") $buf .= "\n";
@file_put_contents($p, $buf, FILE_APPEND);
}
"#
}
@ -718,6 +738,19 @@ mod tests {
);
}
#[test]
fn probe_shim_publishes_stub_sql_recorder() {
let shim = probe_shim();
assert!(
shim.contains("function __nyx_stub_sql_record"),
"PHP probe shim must define __nyx_stub_sql_record"
);
assert!(
shim.contains("NYX_SQL_LOG"),
"stub recorder must read NYX_SQL_LOG"
);
}
#[test]
fn chain_step_splices_probe_shim_for_composite_reverify() {
let step = chain_step(Some(b"<prev>"));