104 lines
4.8 KiB
Text
104 lines
4.8 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
# scripts/witness.nu — run a SOW's contracts for one phase; append a JSONL receipt.
|
||
|
|
# Usage: nu witness.nu <sow.ncl> <receipt.jsonl> --phase static|runtime|lock --wo <slug>
|
||
|
|
#
|
||
|
|
# Dumb executor: understands nothing about the SOW's actual content, only its
|
||
|
|
# contract shape (Appendix B, .coder/2026-07-03_governed-delivery-mode.plan.md).
|
||
|
|
# NEVER signs anything itself (§7.5) — minisign has no password env var by
|
||
|
|
# design; a passphrase-protected key cannot be used non-interactively. This
|
||
|
|
# script prints the exact signing command and stops; a human runs it,
|
||
|
|
# separately, in their own shell.
|
||
|
|
|
||
|
|
def main [
|
||
|
|
sow_path: string,
|
||
|
|
receipt_path: string,
|
||
|
|
--phase: string = "static",
|
||
|
|
--wo: string = "",
|
||
|
|
--pubkey: string = "",
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let verify_result = (do { ^minisign -V -p $pubkey -m $sow_path -x $"($sow_path).minisig" } | complete)
|
||
|
|
if ($pubkey | is-not-empty) and $verify_result.exit_code != 0 {
|
||
|
|
print $"REJECTED: ($sow_path) signature does not verify against ($pubkey) — no witness, no start."
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if ($pubkey | is-not-empty) {
|
||
|
|
print $"Signature verified: ($sow_path)"
|
||
|
|
}
|
||
|
|
|
||
|
|
let sow = (nickel export $sow_path --format json | from json)
|
||
|
|
let ts = (date now | format date "%+")
|
||
|
|
let wo_id = (if ($wo | is-not-empty) { $wo } else { $sow.id })
|
||
|
|
|
||
|
|
let searched = ($sow.existing_mechanisms_searched? | default [])
|
||
|
|
if ($searched | is-empty) {
|
||
|
|
print "REJECTED: existing_mechanisms_searched is empty — a draft SOW must record what was searched for an existing mechanism before proposing a new one."
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
let matching = ($sow.contracts | where kind == $phase)
|
||
|
|
if ($matching | is-empty) {
|
||
|
|
print $"($sow.id) [($phase)] — no contracts for this phase, nothing to check"
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
# Checks run from the governed repository's root — the parent of the
|
||
|
|
# .governance/ directory the SOW lives in (§7.4 guarantees that location).
|
||
|
|
# A relative path in a check must never silently pass from an unrelated
|
||
|
|
# cwd: `! grep` on a file the cwd doesn't contain exits 0 (falsación F0).
|
||
|
|
let sow_dir = ($sow_path | path expand | path dirname)
|
||
|
|
let gov_root = if (($sow_dir | path basename) == ".governance") {
|
||
|
|
$sow_dir | path dirname
|
||
|
|
} else {
|
||
|
|
$env.PWD
|
||
|
|
}
|
||
|
|
|
||
|
|
let results = ($matching | each {|c|
|
||
|
|
let out = (do { cd $gov_root; ^bash -c $c.check } | complete)
|
||
|
|
let exempt_paths = ($c.exempt? | default [] | each {|e| $e.path })
|
||
|
|
if ($exempt_paths | is-empty) {
|
||
|
|
{
|
||
|
|
wo: $wo_id, contract: $c.id, phase: $c.kind,
|
||
|
|
exit: $out.exit_code,
|
||
|
|
verdict: (if $out.exit_code == 0 { "pass" } else { "fail" }),
|
||
|
|
detail: ($out.stdout | str trim | str substring 0..300),
|
||
|
|
ts: $ts,
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
# Exempt-bearing contract: the check must emit one violating path
|
||
|
|
# per stdout line. Exemption subtracts exact paths — the contract
|
||
|
|
# is exempt only when EVERY violation is exempt; any non-exempt
|
||
|
|
# remainder fails. Substring matching against the whole output
|
||
|
|
# would exempt unrelated violations (plan §9.0 N2).
|
||
|
|
let violations = ($out.stdout | lines | each {|l| $l | str trim } | where {|l| $l | is-not-empty })
|
||
|
|
let remaining = ($violations | where {|v| $v not-in $exempt_paths })
|
||
|
|
let counts = $"violations=($violations | length) exempt=(($violations | length) - ($remaining | length)) remaining=($remaining | length)"
|
||
|
|
{
|
||
|
|
wo: $wo_id, contract: $c.id, phase: $c.kind,
|
||
|
|
exit: $out.exit_code,
|
||
|
|
verdict: (if $out.exit_code == 0 { "pass" } else if ($remaining | is-empty) { "exempt" } else { "fail" }),
|
||
|
|
detail: (($counts + (if ($remaining | is-empty) { "" } else { ": " + ($remaining | str join " ") })) | str substring 0..300),
|
||
|
|
ts: $ts,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
let unsigned_lines = ($results | each {|r| $r | to json --raw } | str join "\n")
|
||
|
|
$"($unsigned_lines)\n" | save --append --raw $receipt_path
|
||
|
|
|
||
|
|
let failed = ($results | where verdict == "fail")
|
||
|
|
if ($failed | is-not-empty) {
|
||
|
|
print $"($sow.id) [($phase)] — REJECTED: ($failed | length) contract\(s) failed"
|
||
|
|
$failed | select contract exit detail | print
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
print $"($sow.id) [($phase)] — ACCEPTED: ($results | length) contract\(s) pass"
|
||
|
|
print ""
|
||
|
|
print "Receipt written, NOT signed. Sign it yourself, in your own shell:"
|
||
|
|
let sign_cmd = (" minisign -S -s ~/.minisign/witness.key -m " + $receipt_path
|
||
|
|
+ " -x " + $receipt_path + ".minisig"
|
||
|
|
+ " -t \"wo=" + $wo_id + " " + ($results | length | into string) + " contract(s) checked at " + $ts + "\""
|
||
|
|
+ " -c \"ontoref witness receipt\"")
|
||
|
|
print $sign_cmd
|
||
|
|
}
|