stash current state
This commit is contained in:
parent
20abe1108c
commit
2bb2adeaa0
4 changed files with 947 additions and 0 deletions
435
Cargo.lock
generated
435
Cargo.lock
generated
|
|
@ -2,6 +2,441 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasip2",
|
||||||
|
"wasip3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id-arena"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128fmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.182"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.26.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.2+wasi-0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip3"
|
||||||
|
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
|
dependencies = [
|
||||||
|
"leb128fmt",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-metadata"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rust-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-core"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"prettyplease",
|
||||||
|
"syn",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-component",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust-macro"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-bindgen-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-component"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wasmparser",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-parser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"id-arena",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"unicode-xid",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@ description = "LLM automation loop harness"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
|
|
|
||||||
250
tests/brute_verdict.rs
Normal file
250
tests/brute_verdict.rs
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
//! Integration test: brute-mode verdict preservation.
|
||||||
|
//!
|
||||||
|
//! Verifies that after a judge writes VERDICT: FAIL, the verdict.md content
|
||||||
|
//! survives into the next brute iteration so the agent can read the feedback.
|
||||||
|
//!
|
||||||
|
//! Uses a mock `claude` bash script to simulate both agent and judge,
|
||||||
|
//! recording what the agent sees in verdict.md at each invocation.
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
/// Build the yoke binary path (relies on `cargo test` putting it in target/).
|
||||||
|
fn yoke_bin() -> std::path::PathBuf {
|
||||||
|
let mut path = std::env::current_exe()
|
||||||
|
.expect("current_exe")
|
||||||
|
.parent()
|
||||||
|
.expect("parent of test binary")
|
||||||
|
.parent()
|
||||||
|
.expect("parent of deps dir")
|
||||||
|
.to_path_buf();
|
||||||
|
path.push("yoke");
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimal brute protocol — just enough for the plan runner to work.
|
||||||
|
const PROTOCOL: &str = "\
|
||||||
|
# Protocol
|
||||||
|
|
||||||
|
You are inside an automated loop.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Access | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `.loop/protocol.md` | read | These instructions. |
|
||||||
|
| `.loop/plan.md` | read | The feature plan. |
|
||||||
|
| `.loop/judge.md` | read | What the judge tests. |
|
||||||
|
| `.loop/notes.md` | read+write | Your scratchpad. |
|
||||||
|
| `.loop/verdict.md` | read | Previous judge verdict. |
|
||||||
|
| `.loop/guard-results.md` | read | Guard results. |
|
||||||
|
| `.loop/yoke.conf` | read | Configuration. |
|
||||||
|
|
||||||
|
## Per-Iteration Steps
|
||||||
|
|
||||||
|
1. Read plan.
|
||||||
|
2. Read notes.
|
||||||
|
3. Read verdict.
|
||||||
|
4. Implement one stage.
|
||||||
|
5. Update notes with STATUS line.
|
||||||
|
6. Exit.
|
||||||
|
|
||||||
|
## STATUS Signaling
|
||||||
|
|
||||||
|
First line of notes.md: `STATUS: IN_PROGRESS` or `STATUS: DONE`.
|
||||||
|
";
|
||||||
|
|
||||||
|
const PLAN: &str = "\
|
||||||
|
## Stage 1 — Minimal
|
||||||
|
|
||||||
|
Implement the feature.
|
||||||
|
";
|
||||||
|
|
||||||
|
const JUDGE: &str = "\
|
||||||
|
# Judge
|
||||||
|
|
||||||
|
Verify the feature works.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
Write VERDICT: PASS or VERDICT: FAIL to .loop/verdict.md.
|
||||||
|
";
|
||||||
|
|
||||||
|
const CONF: &str = "\
|
||||||
|
allow .
|
||||||
|
";
|
||||||
|
|
||||||
|
/// Mock claude script that distinguishes agent vs judge by the -p prompt.
|
||||||
|
///
|
||||||
|
/// Agent mode (prompt contains "protocol.md"):
|
||||||
|
/// - Increments .loop/.agent-calls counter
|
||||||
|
/// - Copies verdict.md to .loop/.witness-N
|
||||||
|
/// - Writes STATUS: DONE to notes.md
|
||||||
|
///
|
||||||
|
/// Judge mode (prompt contains "judge.md"):
|
||||||
|
/// - Increments .loop/.judge-calls counter
|
||||||
|
/// - Call 1: writes VERDICT: FAIL + feedback to verdict.md
|
||||||
|
/// - Call 2+: writes VERDICT: PASS to verdict.md
|
||||||
|
const MOCK_CLAUDE: &str = r#"#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Extract the prompt from -p argument
|
||||||
|
PROMPT=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-p) PROMPT="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if echo "$PROMPT" | grep -q "protocol.md"; then
|
||||||
|
# ── Agent mode ──
|
||||||
|
COUNTER_FILE=".loop/.agent-calls"
|
||||||
|
N=0
|
||||||
|
if [[ -f "$COUNTER_FILE" ]]; then
|
||||||
|
N=$(cat "$COUNTER_FILE")
|
||||||
|
fi
|
||||||
|
N=$((N + 1))
|
||||||
|
echo "$N" > "$COUNTER_FILE"
|
||||||
|
|
||||||
|
# Witness: snapshot of verdict.md at the moment the agent runs
|
||||||
|
cp .loop/verdict.md ".loop/.witness-${N}"
|
||||||
|
|
||||||
|
# Write STATUS: DONE so plan loop exits
|
||||||
|
printf 'STATUS: DONE\n\n## Stage 1 — Minimal\nDone.\n' > .loop/notes.md
|
||||||
|
|
||||||
|
elif echo "$PROMPT" | grep -q "judge.md"; then
|
||||||
|
# ── Judge mode ──
|
||||||
|
COUNTER_FILE=".loop/.judge-calls"
|
||||||
|
N=0
|
||||||
|
if [[ -f "$COUNTER_FILE" ]]; then
|
||||||
|
N=$(cat "$COUNTER_FILE")
|
||||||
|
fi
|
||||||
|
N=$((N + 1))
|
||||||
|
echo "$N" > "$COUNTER_FILE"
|
||||||
|
|
||||||
|
if [[ "$N" -eq 1 ]]; then
|
||||||
|
printf 'VERDICT: FAIL\n\nFeature is broken — step counter never increments.' > .loop/verdict.md
|
||||||
|
else
|
||||||
|
printf 'VERDICT: PASS\n\nAll checks passed.' > .loop/verdict.md
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn brute_verdict_preserved_across_iterations() {
|
||||||
|
// Build yoke first
|
||||||
|
let status = Command::new("cargo")
|
||||||
|
.args(["build", "--quiet"])
|
||||||
|
.status()
|
||||||
|
.expect("cargo build");
|
||||||
|
assert!(status.success(), "cargo build failed");
|
||||||
|
|
||||||
|
let yoke = yoke_bin();
|
||||||
|
assert!(yoke.exists(), "yoke binary not found at {:?}", yoke);
|
||||||
|
|
||||||
|
// Create a temp directory for the project
|
||||||
|
let tmp = tempfile::tempdir().expect("tempdir");
|
||||||
|
let project = tmp.path();
|
||||||
|
|
||||||
|
// Set up .loop/ directory with required files
|
||||||
|
let loop_dir = project.join(".loop");
|
||||||
|
fs::create_dir(&loop_dir).expect("create .loop");
|
||||||
|
|
||||||
|
fs::write(loop_dir.join("protocol.md"), PROTOCOL).unwrap();
|
||||||
|
fs::write(loop_dir.join("plan.md"), PLAN).unwrap();
|
||||||
|
fs::write(loop_dir.join("judge.md"), JUDGE).unwrap();
|
||||||
|
fs::write(loop_dir.join("yoke.conf"), CONF).unwrap();
|
||||||
|
fs::write(loop_dir.join("notes.md"), "").unwrap();
|
||||||
|
fs::write(loop_dir.join("verdict.md"), "").unwrap();
|
||||||
|
fs::write(loop_dir.join("guard-results.md"), "").unwrap();
|
||||||
|
|
||||||
|
// Set up mock claude script on PATH
|
||||||
|
let mock_bin_dir = project.join("mock-bin");
|
||||||
|
fs::create_dir(&mock_bin_dir).expect("create mock-bin");
|
||||||
|
|
||||||
|
let mock_claude_path = mock_bin_dir.join("claude");
|
||||||
|
fs::write(&mock_claude_path, MOCK_CLAUDE).unwrap();
|
||||||
|
fs::set_permissions(&mock_claude_path, fs::Permissions::from_mode(0o755)).unwrap();
|
||||||
|
|
||||||
|
// Set up git repo (boundary checker needs `git diff HEAD` to work)
|
||||||
|
let git = |args: &[&str]| {
|
||||||
|
let out = Command::new("git")
|
||||||
|
.args(args)
|
||||||
|
.current_dir(project)
|
||||||
|
.env("GIT_CONFIG_NOSYSTEM", "1")
|
||||||
|
.env("GIT_AUTHOR_NAME", "test")
|
||||||
|
.env("GIT_AUTHOR_EMAIL", "test@test")
|
||||||
|
.env("GIT_COMMITTER_NAME", "test")
|
||||||
|
.env("GIT_COMMITTER_EMAIL", "test@test")
|
||||||
|
.output()
|
||||||
|
.unwrap_or_else(|e| panic!("git {:?} failed to spawn: {}", args, e));
|
||||||
|
assert!(
|
||||||
|
out.status.success(),
|
||||||
|
"git {:?} failed: {}",
|
||||||
|
args,
|
||||||
|
String::from_utf8_lossy(&out.stderr)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
git(&["init"]);
|
||||||
|
fs::write(project.join("dummy.txt"), "seed\n").unwrap();
|
||||||
|
git(&["add", "dummy.txt"]);
|
||||||
|
git(&["-c", "user.name=test", "-c", "user.email=test@test", "commit", "-m", "init"]);
|
||||||
|
|
||||||
|
// Build PATH: mock-bin first so our mock claude shadows the real one
|
||||||
|
let original_path = std::env::var("PATH").unwrap_or_default();
|
||||||
|
let test_path = format!("{}:{}", mock_bin_dir.display(), original_path);
|
||||||
|
|
||||||
|
// Run yoke
|
||||||
|
let output = Command::new(&yoke)
|
||||||
|
.args(["run", "--no-sandbox"])
|
||||||
|
.current_dir(project)
|
||||||
|
.env("PATH", &test_path)
|
||||||
|
.output()
|
||||||
|
.expect("failed to run yoke");
|
||||||
|
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
|
||||||
|
// ── Assertions ──
|
||||||
|
|
||||||
|
// 1. witness-1 should be empty: no verdict exists before first agent run
|
||||||
|
let witness_1 = fs::read_to_string(loop_dir.join(".witness-1"))
|
||||||
|
.expect(".witness-1 must exist (agent was called at least once)");
|
||||||
|
assert!(
|
||||||
|
witness_1.is_empty(),
|
||||||
|
"witness-1 should be empty (no prior verdict), got: {:?}",
|
||||||
|
witness_1
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. witness-2 must contain VERDICT: FAIL — agent saw judge's feedback
|
||||||
|
let witness_2_path = loop_dir.join(".witness-2");
|
||||||
|
assert!(
|
||||||
|
witness_2_path.exists(),
|
||||||
|
"witness-2 must exist (agent should have been called a second time).\n\
|
||||||
|
Agent calls: {:?}\nJudge calls: {:?}\nStderr:\n{}",
|
||||||
|
fs::read_to_string(loop_dir.join(".agent-calls")).ok(),
|
||||||
|
fs::read_to_string(loop_dir.join(".judge-calls")).ok(),
|
||||||
|
stderr,
|
||||||
|
);
|
||||||
|
let witness_2 = fs::read_to_string(&witness_2_path).unwrap();
|
||||||
|
assert!(
|
||||||
|
witness_2.contains("VERDICT: FAIL"),
|
||||||
|
"witness-2 must contain 'VERDICT: FAIL' (agent should see judge feedback on retry).\n\
|
||||||
|
Got: {:?}\nStderr:\n{}",
|
||||||
|
witness_2,
|
||||||
|
stderr,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. yoke exits 0 — judge eventually said PASS
|
||||||
|
assert!(
|
||||||
|
output.status.success(),
|
||||||
|
"yoke should exit 0 (judge said PASS on second attempt).\n\
|
||||||
|
Exit code: {:?}\nStderr:\n{}",
|
||||||
|
output.status.code(),
|
||||||
|
stderr,
|
||||||
|
);
|
||||||
|
}
|
||||||
259
tests/brute_verdict_sandbox.sh
Executable file
259
tests/brute_verdict_sandbox.sh
Executable file
|
|
@ -0,0 +1,259 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Integration test: brute-mode verdict preservation through Docker sandbox.
|
||||||
|
#
|
||||||
|
# Validates that yoke correctly constructs the docker command, volume mounts
|
||||||
|
# work (container writes to .loop/ are visible on host), and verdict.md
|
||||||
|
# survives brute retries through the containerized path.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - docker daemon running
|
||||||
|
# - claude-code-sandbox:latest image available
|
||||||
|
# - cargo (to build yoke)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash tests/brute_verdict_sandbox.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TEST_IMAGE="yoke-test-verdict:latest"
|
||||||
|
TMPDIR_ROOT=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "$TMPDIR_ROOT" && -d "$TMPDIR_ROOT" ]]; then
|
||||||
|
rm -rf "$TMPDIR_ROOT"
|
||||||
|
fi
|
||||||
|
docker rmi "$TEST_IMAGE" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ── Prerequisites ──
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo "SKIP: docker not found on PATH"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker image inspect claude-code-sandbox:latest &>/dev/null; then
|
||||||
|
echo "SKIP: claude-code-sandbox:latest image not found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v cargo &>/dev/null; then
|
||||||
|
echo "SKIP: cargo not found on PATH"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Build yoke ──
|
||||||
|
|
||||||
|
echo "Building yoke..."
|
||||||
|
cargo build --quiet
|
||||||
|
|
||||||
|
YOKE_BIN="$(pwd)/target/debug/yoke"
|
||||||
|
if [[ ! -x "$YOKE_BIN" ]]; then
|
||||||
|
echo "FAIL: yoke binary not found at $YOKE_BIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Create temp workspace ──
|
||||||
|
|
||||||
|
TMPDIR_ROOT="$(mktemp -d)"
|
||||||
|
PROJECT="$TMPDIR_ROOT/project"
|
||||||
|
mkdir -p "$PROJECT"
|
||||||
|
|
||||||
|
# ── Write mock-claude script ──
|
||||||
|
|
||||||
|
cat > "$TMPDIR_ROOT/mock-claude" <<'MOCK'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Extract the prompt from -p argument
|
||||||
|
PROMPT=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-p) PROMPT="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if echo "$PROMPT" | grep -q "protocol.md"; then
|
||||||
|
# ── Agent mode ──
|
||||||
|
COUNTER_FILE=".loop/.agent-calls"
|
||||||
|
N=0
|
||||||
|
if [[ -f "$COUNTER_FILE" ]]; then
|
||||||
|
N=$(cat "$COUNTER_FILE")
|
||||||
|
fi
|
||||||
|
N=$((N + 1))
|
||||||
|
echo "$N" > "$COUNTER_FILE"
|
||||||
|
|
||||||
|
# Witness: snapshot of verdict.md at the moment the agent runs
|
||||||
|
cp .loop/verdict.md ".loop/.witness-${N}"
|
||||||
|
|
||||||
|
# Write STATUS: DONE so plan loop exits
|
||||||
|
printf 'STATUS: DONE\n\n## Stage 1 — Minimal\nDone.\n' > .loop/notes.md
|
||||||
|
|
||||||
|
elif echo "$PROMPT" | grep -q "judge.md"; then
|
||||||
|
# ── Judge mode ──
|
||||||
|
COUNTER_FILE=".loop/.judge-calls"
|
||||||
|
N=0
|
||||||
|
if [[ -f "$COUNTER_FILE" ]]; then
|
||||||
|
N=$(cat "$COUNTER_FILE")
|
||||||
|
fi
|
||||||
|
N=$((N + 1))
|
||||||
|
echo "$N" > "$COUNTER_FILE"
|
||||||
|
|
||||||
|
if [[ "$N" -eq 1 ]]; then
|
||||||
|
printf 'VERDICT: FAIL\n\nFeature is broken — step counter never increments.' > .loop/verdict.md
|
||||||
|
else
|
||||||
|
printf 'VERDICT: PASS\n\nAll checks passed.' > .loop/verdict.md
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
MOCK
|
||||||
|
chmod +x "$TMPDIR_ROOT/mock-claude"
|
||||||
|
|
||||||
|
# ── Build test Docker image ──
|
||||||
|
|
||||||
|
echo "Building test image $TEST_IMAGE..."
|
||||||
|
docker build -t "$TEST_IMAGE" -f- "$TMPDIR_ROOT" <<'DOCKERFILE'
|
||||||
|
FROM claude-code-sandbox:latest
|
||||||
|
COPY --chmod=755 mock-claude /usr/local/bin/claude
|
||||||
|
DOCKERFILE
|
||||||
|
|
||||||
|
# ── Set up project directory ──
|
||||||
|
|
||||||
|
LOOP_DIR="$PROJECT/.loop"
|
||||||
|
mkdir -p "$LOOP_DIR"
|
||||||
|
|
||||||
|
cat > "$LOOP_DIR/protocol.md" <<'EOF'
|
||||||
|
# Protocol
|
||||||
|
|
||||||
|
You are inside an automated loop.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Access | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `.loop/protocol.md` | read | These instructions. |
|
||||||
|
| `.loop/plan.md` | read | The feature plan. |
|
||||||
|
| `.loop/judge.md` | read | What the judge tests. |
|
||||||
|
| `.loop/notes.md` | read+write | Your scratchpad. |
|
||||||
|
| `.loop/verdict.md` | read | Previous judge verdict. |
|
||||||
|
| `.loop/guard-results.md` | read | Guard results. |
|
||||||
|
| `.loop/yoke.conf` | read | Configuration. |
|
||||||
|
|
||||||
|
## Per-Iteration Steps
|
||||||
|
|
||||||
|
1. Read plan.
|
||||||
|
2. Read notes.
|
||||||
|
3. Read verdict.
|
||||||
|
4. Implement one stage.
|
||||||
|
5. Update notes with STATUS line.
|
||||||
|
6. Exit.
|
||||||
|
|
||||||
|
## STATUS Signaling
|
||||||
|
|
||||||
|
First line of notes.md: `STATUS: IN_PROGRESS` or `STATUS: DONE`.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$LOOP_DIR/plan.md" <<'EOF'
|
||||||
|
## Stage 1 — Minimal
|
||||||
|
|
||||||
|
Implement the feature.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$LOOP_DIR/judge.md" <<'EOF'
|
||||||
|
# Judge
|
||||||
|
|
||||||
|
Verify the feature works.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
Write VERDICT: PASS or VERDICT: FAIL to .loop/verdict.md.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$LOOP_DIR/yoke.conf" <<EOF
|
||||||
|
image $TEST_IMAGE
|
||||||
|
allow .
|
||||||
|
EOF
|
||||||
|
|
||||||
|
touch "$LOOP_DIR/notes.md"
|
||||||
|
touch "$LOOP_DIR/verdict.md"
|
||||||
|
touch "$LOOP_DIR/guard-results.md"
|
||||||
|
|
||||||
|
# ── Git init ──
|
||||||
|
|
||||||
|
git -C "$PROJECT" init --quiet
|
||||||
|
echo "seed" > "$PROJECT/dummy.txt"
|
||||||
|
git -C "$PROJECT" add dummy.txt
|
||||||
|
GIT_CONFIG_NOSYSTEM=1 \
|
||||||
|
git -C "$PROJECT" \
|
||||||
|
-c user.name=test \
|
||||||
|
-c user.email=test@test \
|
||||||
|
commit --quiet -m "init"
|
||||||
|
|
||||||
|
# ── Run yoke (sandboxed — no --no-sandbox flag) ──
|
||||||
|
|
||||||
|
echo "Running yoke in sandbox mode..."
|
||||||
|
set +e
|
||||||
|
OUTPUT=$(cd "$PROJECT" && "$YOKE_BIN" run 2>&1)
|
||||||
|
YOKE_EXIT=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ── Assertions ──
|
||||||
|
|
||||||
|
PASS=true
|
||||||
|
|
||||||
|
# 1. witness-1 should be empty: no verdict exists before first agent run
|
||||||
|
if [[ ! -f "$LOOP_DIR/.witness-1" ]]; then
|
||||||
|
echo "FAIL: .witness-1 does not exist (agent was never called)"
|
||||||
|
PASS=false
|
||||||
|
else
|
||||||
|
WITNESS_1=$(cat "$LOOP_DIR/.witness-1")
|
||||||
|
if [[ -n "$WITNESS_1" ]]; then
|
||||||
|
echo "FAIL: witness-1 should be empty (no prior verdict), got: '$WITNESS_1'"
|
||||||
|
PASS=false
|
||||||
|
else
|
||||||
|
echo "OK: witness-1 is empty (no prior verdict)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. witness-2 must contain VERDICT: FAIL — agent saw judge's feedback
|
||||||
|
if [[ ! -f "$LOOP_DIR/.witness-2" ]]; then
|
||||||
|
echo "FAIL: .witness-2 does not exist (agent was not called a second time)"
|
||||||
|
echo " Agent calls: $(cat "$LOOP_DIR/.agent-calls" 2>/dev/null || echo 'N/A')"
|
||||||
|
echo " Judge calls: $(cat "$LOOP_DIR/.judge-calls" 2>/dev/null || echo 'N/A')"
|
||||||
|
echo " Yoke output:"
|
||||||
|
echo "$OUTPUT" | sed 's/^/ /'
|
||||||
|
PASS=false
|
||||||
|
else
|
||||||
|
WITNESS_2=$(cat "$LOOP_DIR/.witness-2")
|
||||||
|
if echo "$WITNESS_2" | grep -q "VERDICT: FAIL"; then
|
||||||
|
echo "OK: witness-2 contains 'VERDICT: FAIL' (agent saw judge feedback)"
|
||||||
|
else
|
||||||
|
echo "FAIL: witness-2 should contain 'VERDICT: FAIL', got: '$WITNESS_2'"
|
||||||
|
PASS=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. yoke exits 0 — judge eventually said PASS
|
||||||
|
if [[ "$YOKE_EXIT" -eq 0 ]]; then
|
||||||
|
echo "OK: yoke exited 0 (judge said PASS)"
|
||||||
|
else
|
||||||
|
echo "FAIL: yoke exited $YOKE_EXIT (expected 0)"
|
||||||
|
echo " Yoke output:"
|
||||||
|
echo "$OUTPUT" | sed 's/^/ /'
|
||||||
|
PASS=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Summary ──
|
||||||
|
|
||||||
|
if [[ "$PASS" == true ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "PASS: brute verdict preserved through Docker sandbox"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "FAIL: one or more assertions failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Loading…
Reference in a new issue