feat(sota-harness): HarnessRisk lifecycle security promotion gate (PIR WP15, ADR-317) - #872
Conversation
…R WP15, ADR-317) Ports HarnessRisk (arXiv:2608.17597) into the SOTA metaharness as a Darwin promotion gate per merged ADR-317 (issue #862, epic #837): - src/harnessRisk.ts — case schema, the four lifecycle metrics (utility, attack success, persistence, recovery), phase-weighted risk with setup_configuration weighted highest, Darwin thresholds (utility >90%, attack <5%, persistence <1%, recovery demonstrated), frozen HarnessRiskVerdict, and harnessRiskVetoProvider for the existing vetoes.ts -> flywheel.ts promotion path. Evaluative only (ADR-305). - src/harnessRiskCases.ts — license finding: the HF dataset (YajingB/HarnessRisk) card states no distribution license has been selected, so the 128 cases are NOT vendored; instead: schema + runtime loader (datasets-server rows API, verifies the 128-case count) + five first-party example cases matching the schema. - src/harnessRiskAcceptance.ts — ruv's Wave-2 acceptance test scaffold: baseline run -> +RVF-bound workspace states (WP16 #863 seam, stub) + TRUSS-style shadow execution (WP17 #864 seam, stub) -> identical-case rerun -> real utility >90% and >=75%-reduction assertions; stubbed seams taint claimability by construction. - src/vetoes.ts — composeVetoProviders for conjunctive composition alongside dreamMachineVetoProvider (additive, not a replacement). - 22 node --test cases: known-outcome metric fixtures, each threshold vetoing independently, weighted Configuration-phase veto, acceptance delta pass/fail pairs, loader pagination, constitutional boundary. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
|
WP17 seam contract memorialized — the outcome-mapping contract for the future
i.e. a broker denial maps to WP16 bridge decision is also settled: subprocess adapter ( 🤖 Generated with claude-flow |
…t binder Adds the cross-language bridge WP15's RvfWorkspaceBinder (PR #872, crates/ruvector-sota-bench/harness) consumes -- a subprocess JSON protocol rather than NAPI, keeping the ADR-318 gate off the platform binary build matrix: - bin staged-workspace-adapter: one JSON request on stdin, one JSON response on stdout. Exit-code contract: 0 = ok, 1 = integrity rejection (ADR-318 hard rejection), 2 = adapter malfunction -- so a crashed or misconfigured adapter fails the harness loudly and is never silently scored as a detected compromise. - Frozen error discriminators: stale-view, binding-mismatch, unknown-artifact, anchor-rejected (exit 1); adapter-error (exit 2). - commit op: batch-commits base64 content, returns per-artifact ArtifactRefs plus a workspace_hash (SHA-256 over sorted (artifact_id, content_hash) head pairs) -- the single contentHash the binder seam expects. validate op: fail-closed check of a bound (artifact_id, content_hash, revision_id) reference. - State persists across invocations via a WorkspaceSnapshot JSON file (new snapshot()/from_snapshot() on WorkspaceState, plus validate_ref() and workspace_hash()); ContentHash::from_hex added. - Zero new external deps: hand-rolled RFC 4648 base64 (vector-tested), serde_json promoted from dev-dep for the transport. - 22 tests green (7 new adapter-protocol tests incl. cross-invocation staleness and the malfunction-vs-rejection split); clippy/fmt clean; binary smoke-tested end to end. Refs #863, #862. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
…DR-318 seam) Implements the non-stub RvfWorkspaceBinder against the frozen staged-workspace-adapter contract (crates/ruvector-staged-workspace, PR #871, commit 3d0914b): - src/rvfWorkspaceBinder.ts — cargo subprocess invoker (one JSON request on stdin, one response line on stdout), createSubprocessRvfBinder with stub: false, one commit request per case against a fresh per-case state file, workspace_hash (commit-order independent over all lineage heads) returned as the seam's contentHash, and validate() for the case executor. classifyAdapterResult enforces the signal split: exit 1 with a known discriminator ({stale-view, binding-mismatch, unknown-artifact, anchor-rejected}) is the ONLY integrity rejection; exit 2, crashes, unparsable output, and unknown discriminators throw RvfAdapterMalfunction — a broken instrument fails the run loudly and never scores as a detected compromise. Seed-file extraction from HarnessRiskCase.world tolerates the documented shapes and refuses to guess. - test/rvfWorkspaceBinder.test.ts — contract tests with an injected invoker (no cargo needed): classification matrix incl. exit-2/unknown- discriminator/no-JSON malfunctions, single-commit request shape with strict padded base64, malformed-response rejection, validate mapping; plus a real cargo integration test (commit -> validate ok -> tamper -> stale-view) gated behind RVF_ADAPTER_IT=1 and skipped while the WP16 crate lives only on feat/pir-wp16-stagedworkspace. Suite: 44 pass, 1 skip (the gated integration test). Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X
|
Real WP16 binder wired (commit 990c503): Signal split enforced exactly as agreed with the WP16 session: exit 1 with a known discriminator ( Tests: contract tests with an injected invoker (no cargo required — the Rust crate lives on Claimability is unchanged: with the real binder ( 🤖 Generated with claude-flow |
|
WP16 bridge cross-verified live. The WP16 session materialized this branch (990c503) alongside feat/pir-wp16-stagedworkspace (adapter contract locked at 710ebda) and ran Two contract questions from the wiring are now answered with adapter-side tests:
🤖 Generated with claude-flow |
Security audit — Phase 4 (WP15 gate + WP16 binder commit
|
…ersal (PIR WP15 follow-up) MEDIUM path-traversal fast-follow on the WP15 HarnessRisk binder (merged in PR ruvnet#872). `bindWorkspace` resolved `${kase.caseId}.state.json` under stateDir, and `parseHarnessRiskRow` only checked case_id was a non-empty string. case_id comes from the unlicensed upstream HF dataset (the runtime-fetch/treated leg), so a crafted id like "../../../tmp/evil" wrote state files OUTSIDE stateDir, and an id colliding with another case's resolved state path cross-contaminated workspace state — forging false integrity rejections or masking real ones, and corrupting ruv's ≥75%-reduction acceptance measurement. First-party *_fp001 cases are unaffected; the exposure is the runtime upstream set. Defense in depth, both layers: 1. Parse boundary (harnessRiskCases.parseHarnessRiskRow): new assertSafeCaseId rejects any case_id not matching /^[A-Za-z0-9._-]+$/ (excludes `/` and `\`) and explicitly rejects exactly "." or "..", throwing the same kind of boundary validation error as other malformed rows. 2. Binder backstop (rvfWorkspaceBinder.bindWorkspace): after resolving the state-file path, verify its parent directory equals the resolved stateDir; an escaping id throws RvfAdapterMalfunction (a loud instrument problem, never scored as a detected compromise) before the adapter is invoked. Never reads/writes a .state.json outside stateDir. Tests: parse-boundary cases prove "../../../tmp/evil", "..", ".", "a/b", "a\\b", "" are rejected and dot/dash/underscore ids are accepted; a binder test proves a path-escaping caseId that bypasses the boundary is rejected, never touches the adapter, and writes nothing outside stateDir. Full harness suite green (46 pass, 1 integration skip); tsc --noEmit clean; frozen-weights-check scans the harness surface clean. Refs ruvnet#862, ruvnet#837 and the security sweep finding (PR ruvnet#872 comment 5356736287). Co-Authored-By: claude-flow <ruv@ruv.net>
Part of PIR Wave 2. Work package WP15 (#862), epic #837, implementing merged ADR-317 (
docs/adr/ADR-317-harnessrisk-lifecycle-security-benchmark-gate.md). Extends merged ADR-313 (WP9) and ADR-306 (WP2).What this is
First shippable slice of the HarnessRisk (arXiv:2608.17597) lifecycle security benchmark as a Darwin promotion gate, in
crates/ruvector-sota-bench/harness:1. License finding — why the 128 cases are NOT vendored
Checked directly (2026-08-20):
github.com/Baiyajing/HarnessRisk: MIT — but itsdata/README.mdstates the case set "is distributed separately in the HarnessRisk dataset on Hugging Face … (it is not committed to this repo)". The MIT grant covers adapters/services, not the cases.huggingface.co/datasets/YajingB/HarnessRisk(128 rows, one per case): no license — its card states verbatim, "A distribution license has not yet been selected in the source project."No license = no redistribution right. So
src/harnessRiskCases.tsimplements the upstream case schema + a runtime loader (HF datasets-server rows API; paginates; refuses a partial baseline unless the caller opts out of the 128-case check) + five first-party example cases authored in this repo matching the schema (covering 5 of the 6 phases, including the highest-weightedsetup_configuration). If upstream later selects a redistributable license, vendoring becomes a follow-up.2. Gate module (
src/harnessRisk.ts)*_jsonstructured components kept opaque).computeLifecycleMetrics→ the four ADR-317 metrics: utility, attack success, persistence (only compromises that survive session/context reset count), recovery (must be demonstrated — an undetected compromise can never count as recovered).PHASE_WEIGHTSwithsetup_configuration= 1.0 (highest, per the paper's confirmed finding), others proportionally lower but non-zero; a phase-weighted attack score is thresholded alongside the raw rate, so a Configuration-phase compromise spends more of the risk budget.harnessrisk_no_cases_executed— absence of evidence cannot pass.HarnessRiskVerdict+harnessRiskVetoProviderfeeding the existingvetoes.ts→flywheel.tsruvectorPromotionRulepath. NewcomposeVetoProvidersinvetoes.tscomposes it conjunctively alongsidedreamMachineVetoProvider— additive, not a replacement; a passing HarnessRisk verdict rescues nothing.dreamMachine.tsand test-pinned (ADR-305/322B): no promote/merge export, verdict is frozen data.3. Acceptance-test scaffold (
src/harnessRiskAcceptance.ts)Expresses ruv's verbatim Wave-2 acceptance test (#862 is its canonical record): baseline HarnessRisk run → add RVF-bound workspace states + TRUSS-style shadow execution → rerun the identical cases → assert utility >90% AND attack success and persistence both down ≥75% vs baseline.
computeAcceptanceDelta, with an explicit zero-baseline rule: 0→0 satisfied, 0→positive failed).RvfWorkspaceBinder— WP16 seam ([PIR][WP16] Content-hash-bound versioned workspace views (StagedWorkspace pattern, ADR-318) #863, ADR-318): binds each case's seed workspace to a content hash;stubRvfBinder()placeholder.TrussShadowExecutor— WP17 seam ([PIR][WP17] Shadow-execution brokered-tool admission gate for generated skills/harness candidates (TRUSS pattern, ADR-319) #864, ADR-319, TRUSS arXiv:2608.17588): shadow-executes a case and may downgrade (never upgrade) an outcome;stubTrussShadow()placeholder. WP17's draft PR feat(sota-harness): TRUSS-pattern shadow execution for generated capabilities (PIR WP17, ADR-319) #870 shipstrussShadowVetoProvider/vetoesFromTrussVerdicton the same conjunctive veto contract; once feat(sota-harness): TRUSS-pattern shadow execution for generated capabilities (PIR WP17, ADR-319) #870 merges, wiring its real verdict through this seam is a small adapter.claimable: falseeven when the delta passes — per ADR-317, the acceptance test "must be measured together, not inferred from WP15 in isolation."4. Tests (22 new,
node --test)Known-outcome metric fixtures; each of the four thresholds vetoing independently; weighted Configuration-phase veto firing when the raw rate passes; provider composition with a dreamMachine-style provider; ≥75%-delta passing on a synthetic improved-vs-baseline pair and failing on a non-improved pair (plus 74%≠75%, utility-drop, zero-baseline, mismatched-set cases); loader pagination + partial-baseline refusal; constitutional boundary. Full harness suite: 39/39 green (
npm test).Not in this slice (honest scope)
HarnessRiskCaseExecutordriving MetaHarness (dependent on WP9's loop) — the executor seam is defined, deterministic executors exercise it.crates/sona/src/darwin_guard.rswiring (ADR-317 surface) — follow-up once the TS gate's verdict shape settles.claimable.Refs #862, #837. Citation discipline per ADR-317: always "HarnessRisk (arXiv:2608.17597)" in full (name collision with Harness-Bench, arXiv:2605.27922).
🤖 Generated with claude-flow
https://claude.ai/code/session_012Jib2gQyJpqCoo2xYAbb4X