feat(recovery,context): tier-2 intent-holder arbiter + remaining-delta pinning + surfaced signal events
- recovery: two-tier repair ladder — owner then arbiter (recovery stage recast
as intent-holder, holds initial intent, reconciles cross-file contract disputes)
with independent INTENT_ROUTE_BUDGET; RecoveryRoutingTest coverage
- context: remaining-delta checklist pinning (RemainingDelta{Pinning,Entry}Test)
- surface write-only events (session name, quality signals) into stats/browse
- parseToolArguments lenient-Json fallback for bare-key drift
- tools: ChildProcess seam
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.correx.core.tools.process
|
||||
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Single construction point for child processes spawned on behalf of a run — both model-authored
|
||||
* shell (ShellTool) and operator-trusted gate commands (static-analysis runner) go through here.
|
||||
*
|
||||
* Every child is configured for *unattended* execution, closing the two ways a subprocess hangs a
|
||||
* headless run:
|
||||
* 1. **stdin from /dev/null** — a tool that still prompts reads EOF and fails fast instead of
|
||||
* blocking on a dangling pipe until the caller's timeout kills it.
|
||||
* 2. **non-interactive env overlay** — package managers / scaffolders (`npm create`, `npm init`,
|
||||
* …) honour `CI`, `npm_config_yes`, etc. and take defaults rather than prompting at all.
|
||||
*
|
||||
* Output redirection is deliberately NOT set here: callers differ (ShellTool drains stdout/stderr
|
||||
* separately; the gate runner merges them via `redirectErrorStream`). They also keep their own
|
||||
* timeout backstop — this only builds the [ProcessBuilder].
|
||||
*/
|
||||
object ChildProcess {
|
||||
private val DEV_NULL = File("/dev/null")
|
||||
|
||||
// Overlaid on the inherited environment. Forces the common Node toolchain into non-interactive
|
||||
// mode so `npm create`/`npm init` and friends never block on a stdin prompt.
|
||||
private val NON_INTERACTIVE_ENV = mapOf(
|
||||
"CI" to "true",
|
||||
"npm_config_yes" to "true",
|
||||
"NPM_CONFIG_YES" to "true",
|
||||
"ADBLOCK" to "1",
|
||||
"DISABLE_OPENCOLLECTIVE" to "1",
|
||||
)
|
||||
|
||||
/**
|
||||
* A [ProcessBuilder] for [command] in [workingDir] (JVM cwd when null), with the non-interactive
|
||||
* env overlay and stdin redirected from /dev/null. The caller sets any output redirection and
|
||||
* owns the timeout.
|
||||
*/
|
||||
fun builder(command: List<String>, workingDir: File? = null): ProcessBuilder =
|
||||
ProcessBuilder(command).apply {
|
||||
workingDir?.let { directory(it) }
|
||||
environment().putAll(NON_INTERACTIVE_ENV)
|
||||
redirectInput(ProcessBuilder.Redirect.from(DEV_NULL))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user