Skip to content

feat(bin): add opt-in Jev PR risk level, failed-check sort, and worker commit check - #50

Merged
talktejas merged 8 commits into
mainfrom
fm/fm-jev-batch-integration
Oct 5, 2026
Merged

talktejas merged 8 commits into
mainfrom
fm/fm-jev-batch-integration

Conversation

@talktejas

@talktejas talktejas commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Supersedes

This one pull request carries the final content of four that could not land one by one, and replaces them:

Intent

A set of Jev (TypeSafe decision-only model) integrations was ordered built ("implement" on each row of a ten-row proposal table) and then ordered merged: "merge all jev items". Five have landed on main. Four more are finished - each passed its own review, tests, documentation and lint steps - but cannot get onto main one by one:

  • merge-request risk level - pull request 45 (branch fm/fm-jev-pr-risk-level-2); its final validated head 5ead1ece exists only in the gate repository ~/.no-mistakes/repos/3605d2c32b02.git because the pipeline twice refused to publish a conflict-resolved rebase.
  • sorting a failed check into code bug, flaky test or broken environment - pull request 47 (branch fm/fm-jev-failed-check-sort).
  • routine-check fix (a finished, exited worker's open pull request is a routine wait; "waiting for the merge word" is not a person waiting) - pull request 48 (branch fm/fm-jev-wake-triage-no-checks-2).
  • commit-message check - pull request 49 (branch fm/fm-jev-commit-message-check).

Each touches the same shared files (bin/fm-jev-lib.sh, docs/configuration.md, AGENTS.md), so each one that lands puts the next into conflict, and GitHub's hosted runners are cancelling jobs unstarted tonight, so every separate run costs several re-runs. The standing delivery rule for a multi-part effort is one integration branch that merges once: "You should be putting everything in a different work tree. Check everything in a different work tree. When everything is ready to be merged, do so only then."

Rulings given during those four reviews, which the combined result must still satisfy: nothing a worker typed in its shell is sent to TypeSafe; no project's source or diff is sent unless that project is explicitly named; for a caller keyed by repository an entry matches only as owner/repo exactly, and a bare name matches only a caller passing a firstmate project name; a line that looks like a credential is never sent; the commit check touches a worker only on a named project and acts only on the task's own worktree; the failed-check sort never runs unless its caller asks for it; the risk level is rated once at registration and never during a merge; Jev advises and never becomes the only thing that blocks, merges or approves anything.

What Changed

  • Three new advisory Jev checks, all off without TYPESAFE_API_KEY:
    • bin/fm-pr-check.sh prints one risk: low|medium|high|not rated line after arming the merge poll (bin/fm-pr-risk-lib.sh). bin/fm-pr-merge.sh re-registers with the new --no-risk flag, so the level is never rated during a merge.
    • bin/fm-pr-state.sh --sort-failed-checks adds a FAILED CHECK SORT: line per failed required check, labelling it code bug, flaky, environment, or unknown (bin/fm-check-sort-lib.sh). The sort runs only under that flag, and the watcher's triage read does not pass it.
    • bin/fm-commit-check.sh is installed by bin/fm-spawn.sh as a commit-msg hook, through GIT_CONFIG_PARAMETERS in the worker's pane only, for ship and scout workers on a listed project. It acts only on commits to the task's own worktree. It refuses a commit whose added lines match a recognised credential format, and otherwise asks Jev about the message and staged file names alone.
  • Routine-wake triage fix in bin/fm-watch.sh: a paused task whose agent has exited is now offered to triage, with worker_agent in the evidence, instead of always being delivered. The reworded question treats a wait for the merge of the open pull request as routine. A repository that reports no checks is no longer counted as a blocker.
  • Shared guards in bin/fm-jev-lib.sh:
    • config/jev-code-projects is now the single opt-in for every code-sending check. It is read from FM_CONFIG_OVERRIDE when set, and matched as a whole line: <owner>/<repo> for repository-keyed callers, a firstmate project name for the rest.
    • bin/fm-house-rules-check.sh now gates on that list instead of the projects array in config/house-rules.json, which now holds only optional rules.
    • Every request has credential-looking lines and private key blocks replaced with a withheld marker before it is sent, and the new fm_jev_secret_path keeps secret-named files out of the content callers send.
    • Tests, docs/configuration.md, the docs/verification/ notes, and AGENTS.md are updated to match.

Risk Assessment

✅ Low: Both earlier fixes hold when traced through the code: every string in the state of all eight Jev callers passes the single withhold filter in _fm_jev_ask (including a key tail cut from its BEGIN line), the project gate and the house rules read the same config directory, and every new check stays opt-in and advisory, with only a rare unreachable-in-practice mid-key slice left unfiltered.

Testing

Ran the nine targeted suites for the changed Jev callers (all pass), then drove the real bin/ scripts by hand with real git, real jq and real installed commit hooks in a throwaway home. The only replacements were curl (a recorder that saves each request body and answers in the API's shape) and gh (a fixture forge). The drive covered real commits through the installed hooks, real pull request registrations, real fm-pr-state.sh reads with and without --sort-failed-checks, adversarial private-key inputs to the one Jev caller, and a house-rules run over a key split across two 80-line blocks; all 54 checks passed. The watcher's routine-wake fix, the finished check's "no shell command is sent" rule and the merge path were exercised only through their existing suites, not by a separate manual drive, so the watcher and finished-check scenarios are reported as untested rather than as live passes. Nothing was sent to the real TypeSafe endpoint, so real model answers are untested. No UI surface is involved, so there is no visual evidence; the transcript is the evidence.

  • Live validation: ✅ go - 14 of 17 scenarios driven live against the product
Scenario Result Live Evidence
A worker on a listed project commits with a GitHub token pasted into the commit message: the commit is made, one request is sent, and it holds the message with that line replaced by the placeholder pl… ✅ pass live jev-batch-drive-transcript.txt section A (state sent to Jev shown); TypeSafe endpoint replaced by a local recorder
A worker stages a line holding a GitHub token: the commit is refused by pattern alone, naming file:line and kind but not the value, and nothing is sent ✅ pass live jev-batch-drive-transcript.txt section A: rc=1, 'src/conf.py:1: GitHub token', requests=0, HEAD unchanged
Commit check stays off for an unlisted project, for an owner/repo spelling of a bare entry, for another repository under the same exported hooks setting, and as soon as the project is delisted ✅ pass live jev-batch-drive-transcript.txt section A: --install exits 1 with no output and no hooks directory; commits elsewhere go through silently with requests=0
Jev answering yes to all three commit questions, or timing out, never stops a commit ✅ pass live jev-batch-drive-transcript.txt section A: rc=0 with three 'advisory, the commit goes through' lines; rc=0 and silence on a timeout
Registering a pull request for a listed project prints one risk line beside the unchanged registration, and the diff excerpt sent leaves out an AWS key line and the contents of .env and .pem files ✅ pass live jev-batch-drive-transcript.txt section B (diff_start sent to Jev shown); pr= recorded and merge poll armed
A firstmate project is not listed by an owner/repo entry, a similar name or a commented name: the registration still succeeds, nothing is sent and the forge description is not read ✅ pass live jev-batch-drive-transcript.txt section B: list 'acme/alpha, alpha-web, # alpha' gives requests=0 and 'project not listed'
The risk level only advises: a worst-case Jev answer or a Jev timeout still records the pull request and arms the merge poll ✅ pass live jev-batch-drive-transcript.txt section B: 'risk: high ...' and 'risk: not rated ... (Jev error)' both with rc=0 and the poll armed
The merge-time registration (--no-risk, what fm-pr-merge.sh runs) makes no Jev request and prints no risk line ✅ pass live jev-batch-drive-transcript.txt section B: requests=0, output is the arming line only; fm-pr-merge.sh itself covered by tests/fm-pr-merge.test.sh test_merge_registration_never_rates_risk
fm-pr-state.sh without --sort-failed-checks, with a key and the repository listed, sends nothing, reads no check runs or job log, and prints no label ✅ pass live jev-batch-drive-transcript.txt section C: requests=0, only the two REQUIRED CHECK lines
fm-pr-state.sh --sort-failed-checks on a listed owner/repo adds one label under unchanged blocker lines, and a token in the job log is withheld from the request ✅ pass live jev-batch-drive-transcript.txt section C (state sent to Jev shown: log_tail starts with the placeholder)
For the repository-keyed sort, a bare name, another owner, different case, a longer name and a commented entry do not allow o/r: label is unknown and nothing is sent ✅ pass live jev-batch-drive-transcript.txt section C: five list entries, each 'unknown' with requests=0
With Jev unreachable or no key, the failed-check report keeps its lines and exit status ✅ pass live jev-batch-drive-transcript.txt section C: rc=0 with 'unknown' when Jev is down; output identical to the plain report with no key
The one Jev caller withholds a private key tail that arrives without its BEGIN line, a whole nested key block, a block with no END line, and GitHub, AWS, Slack and quoted-password lines, while ordinar… ✅ pass live jev-batch-drive-transcript.txt section D (each sent state shown)
House-rules check on a listed project whose change embeds a private key spanning two 80-line blocks sends no key body, BEGIN or END line in any request ✅ pass live jev-batch-drive-transcript.txt section E: 4 requests over 2 blocks, 0 key lines in any body, rc=0
The watcher offers the pause recheck of an exited worker with an open pull request to triage, absorbs it on a routine answer, delivers on any other answer or error, and withholds a credential in a sta… ⏸️ untested no The prior payload did not establish a live result: this was exercised only through the existing suite bash tests/fm-jev-wake-triage.test.sh (which passed) on a fake terminal backend with a fake curl…
The finished check sends only the worker's closing message, never a shell command the worker ran ⏸️ untested no The prior payload did not establish a live result: this was exercised only through the existing suite bash tests/fm-finished-check.test.sh (which passed), not by a separate manual drive of the finis…
The real TypeSafe model rates 'waiting for the merge word' from an exited worker as routine, and answers the commit-message and failed-check questions sensibly ⏸️ untested no No TYPESAFE_API_KEY is available in this environment (not in the environment, no firstmate home configured). To test, provide a key in the environment or a home's .env and re-run the callers against h…
Evidence: Manual drive transcript (54 checks, with the exact state sent to Jev for each caller)

Source: Manual drive transcript (54 checks, with the exact state sent to Jev for each caller)


=== A. commit check: real git commits through the hooks --install writes
$ fm-commit-check.sh --install <hooks> <home> notlisted <wt>   -> rc=1 output=[]
PASS  an unlisted project gets no hooks: exit 1, nothing printed, no directory
PASS  an owner/repo spelling does not match the bare project entry
$ fm-commit-check.sh --install <hooks> <home> listed <wt>      -> rc=0 setting=['core.hooksPath=<tmp>/hooks']
PASS  a listed project with a key gets the hooks setting
$ git commit -m "<message holding a GitHub token line>"   -> rc=0 stderr=[] requests=1
state sent to Jev:
{
  "commit": {
    "message": "Parse trailing commas\n\nReproduced with the staging token:\n[line withheld: looks like a credential]\nNothing else changed.",
    "files": [
      "notes.md",
      "src/parser.js"
    ]
  }
}
PASS  the commit is made
PASS  exactly one request per commit
PASS  the token pasted into the message is not in the request
PASS  its line is replaced by the fixed placeholder
PASS  no line of staged content is sent, only names
PASS  the key is not on curl argv
$ git commit -m update   (Jev answers yes to every question)   -> rc=0
commit-check: advisory, the commit goes through: the message does not say what changed or why. (Jev confidence 0.9)
commit-check: advisory, the commit goes through: the message appears to contradict the staged file names. (Jev confidence 0.9)
commit-check: advisory, the commit goes through: a staged file is not accounted for by the message; work outside the described change belongs in its own commit. (Jev confidence 0.9)
PASS  three confident yes answers only advise: the commit still goes through
PASS  the advice says the commit goes through
$ git commit   (Jev times out)   -> rc=0 stderr=[]
PASS  a Jev timeout neither stops the commit nor says anything
$ git commit   (a staged line holds a GitHub token)   -> rc=1
commit-check: commit stopped, an added line looks like a credential:
  src/conf.py:1: GitHub token
Remove the credential from the change and commit again.
PASS  the pattern stop refuses the commit by code alone
PASS  the stop names file:line and kind, never the value
PASS  nothing is sent for a stopped commit
$ git commit in ANOTHER repository under the same exported setting   -> rc=0 stderr=[] requests=0
PASS  a repository that is not the task worktree is untouched: committed, silent, nothing sent
$ git commit after the project was removed from the list   -> rc=0 stderr=[] requests=0
PASS  delisting the project stops every send at once

=== B. risk level: real bin/fm-pr-check.sh registration
$ fm-pr-check.sh task-a https://github.com/acme/alpha/pull/8   [alpha listed, key present]
armed: state/task-a.check.sh
risk: low - no risk fact found and all three questions answered no
(rc=0, requests=3)
diff_start sent to Jev:
diff --git a/src/client.js b/src/client.js
new file mode 100644
index 0000000..b908222
--- /dev/null
+++ b/src/client.js
@@ -0,0 +1,2 @@
+export const retries = 3
[line withheld: looks like a credential]

PASS  the registration succeeds and prints one risk line after the arming
PASS  the pull request is recorded and the merge poll armed
PASS  the ordinary source line is sent
PASS  the AWS key line in an ordinary file is in no request
PASS  the .env file's content is in no request
PASS  the .pem file's content is in no request
$ fm-pr-check.sh task-a https://github.com/acme/alpha/pull/8   [list holds acme/alpha, alpha-web, '# alpha']
armed: state/task-a.check.sh
risk: not rated - no risk fact found; unanswered: untested (project not listed), mismatch (project not listed), irreversible (project not listed)
(rc=0, requests=0)
PASS  an owner/repo entry, a similar name, and a commented name do not list a firstmate project: nothing sent
PASS  the unlisted project still registers and says why it is not rated
PASS  the forge description is not read for an unlisted project
$ fm-pr-check.sh task-a https://github.com/acme/alpha/pull/8   [Jev answers yes to everything]
armed: state/task-a.check.sh
risk: high - behaviour changed with no test, description does not match the change, something hard to undo
(rc=0, requests=3)
PASS  a worst-case Jev answer still registers and arms: the level is a printed line only
$ fm-pr-check.sh task-a https://github.com/acme/alpha/pull/8   [Jev times out]
armed: state/task-a.check.sh
risk: not rated - no risk fact found; unanswered: untested (Jev error), mismatch (Jev error), irreversible (Jev error)
(rc=0, requests=1)
PASS  a Jev timeout still registers and arms
$ fm-pr-check.sh task-a https://github.com/acme/alpha/pull/8 --no-risk   [what fm-pr-merge.sh runs]
armed: state/task-a.check.sh
(rc=0, requests=0)
PASS  the merge-time registration makes no request and prints no risk line

=== C. failed check sort: real bin/fm-pr-state.sh over a fixture forge
$ fm-pr-state.sh https://github.com/o/r/pull/7   [o/r listed, key present, option NOT given]
REQUIRED CHECK: CI Status (FAILURE)
REQUIRED CHECK: slow (IN_PROGRESS)
(rc=0, requests=0)
PASS  without --sort-failed-checks nothing is sent and no label is printed
PASS  without the option no check-run or job-log read is made
$ fm-pr-state.sh --sort-failed-checks https://github.com/o/r/pull/7   [o/r listed]
REQUIRED CHECK: CI Status (FAILURE)
REQUIRED CHECK: slow (IN_PROGRESS)
FAILED CHECK SORT: CI Status: code bug (jev, confidence 0.9)
(rc=0, requests=1)
state sent to Jev:
{
  "check": "CI Status",
  "log_tail": "[line withheld: looks like a credential]\nassert failed in the parser"
}
PASS  the failed check gains one label and the blocker lines are unchanged
PASS  the token in the job log is not in the request
PASS  the failure line beside it is sent
$ list entry [r] -> FAILED CHECK SORT: CI Status: unknown (requests=0)
PASS  list entry 'r' does not allow o/r: unknown, nothing sent
$ list entry [x/r] -> FAILED CHECK SORT: CI Status: unknown (requests=0)
PASS  list entry 'x/r' does not allow o/r: unknown, nothing sent
$ list entry [O/R] -> FAILED CHECK SORT: CI Status: unknown (requests=0)
PASS  list entry 'O/R' does not allow o/r: unknown, nothing sent
$ list entry [o/r-web] -> FAILED CHECK SORT: CI Status: unknown (requests=0)
PASS  list entry 'o/r-web' does not allow o/r: unknown, nothing sent
$ list entry [# o/r] -> FAILED CHECK SORT: CI Status: unknown (requests=0)
PASS  list entry '# o/r' does not allow o/r: unknown, nothing sent
$ fm-pr-state.sh --sort-failed-checks https://github.com/o/r/pull/7   [Jev unreachable]
REQUIRED CHECK: CI Status (FAILURE)
REQUIRED CHECK: slow (IN_PROGRESS)
FAILED CHECK SORT: CI Status: unknown
(rc=0, requests=1)
PASS  with Jev down the report is intact, exit 0, label unknown
PASS  without a key the option prints exactly the plain report

=== D. the one caller: adversarial inputs to the credential filter
key tail with no BEGIN line   -> sent: {"block":"[line withheld: looks like a credential]\nplain line after"}
PASS  a key tail cut off from its BEGIN line sends no body line and no END marker
PASS  the ordinary line after the block is still sent
whole block, nested in the state -> sent: {"a":{"deep":["before\n[line withheld: looks like a credential]\nafter"]}}
PASS  a whole private key block nested deep in the state is replaced by one placeholder
block with no END line          -> sent: {"t":"x\n[line withheld: looks like a credential]"}
PASS  a block with no END line is withheld to the end of the string
one string per credential kind  -> sent: {"lines":["ok line","[line withheld: looks like a credential]","[line withheld: looks like a credential]","[line withheld: looks like a credential]","[line withheld: looks like a credential]","the token type is bearer"]}
PASS  GitHub, AWS, Slack and quoted-password lines are each withheld; ordinary lines pass

=== E. house-rules check: an embedded private key spanning two 80-line blocks
$ fm-house-rules-check.sh hr   -> rc=0 requests=4
house-rules-check: 0 advisory flag(s) from 4 question(s) over 2 changed block(s) and 2 rule(s) since main
key lines in any request body: 0
PASS  the change was actually sent in more than one block
PASS  ordinary lines of both blocks are sent
PASS  no key body line, BEGIN line or END line is in any request
PASS  the check exits 0 whatever Jev says

0 check(s) failed
Evidence: Drive script that produced the transcript

Source: Drive script that produced the transcript

#!/usr/bin/env bash
# Manual drive of the real firstmate scripts for the Jev batch. The only thing
# replaced is the remote TypeSafe endpoint: `curl` on PATH is a recorder that
# saves each request body and answers in the API's shape. git, jq, the hooks,
# and every bin/ script are the real ones. `gh` is a fixture forge.
set -u
ROOT=$(pwd)
. "$ROOT/tests/lib.sh"
T=$(fm_test_tmproot fm-jev-drive)
BIN="$T/bin"; LOG="$T/log"; mkdir -p "$BIN" "$LOG"
KEY='drive-key-77aa-never-on-argv'
GHP="ghp_$(printf 'Z%.0s' $(seq 1 36))"
AWS="AKIA$(printf 'Q%.0s' $(seq 1 16))"
FAILS=0

cat > "$BIN/curl" <<'SH'
#!/usr/bin/env bash
out=''
while [ $# -gt 0 ]; do
  case "$1" in -o) out=$2; shift 2 ;; *) printf '%s\n' "$1" >> "$DRIVE_LOG/argv"; shift ;; esac
done
n=$(( $(cat "$DRIVE_LOG/count" 2>/dev/null || echo 0) + 1 )); echo "$n" > "$DRIVE_LOG/count"
cat > "$DRIVE_LOG/body"; cp "$DRIVE_LOG/body" "$DRIVE_LOG/body.$n"
[ "${DRIVE_CURL_FAIL:-0}" = 0 ] || exit "$DRIVE_CURL_FAIL"
jq -c --arg want "${DRIVE_CHOICE:-no}" '{model: "jev-recorder", answers: (.questions | map_values(
  (.criteria | keys) as $ks | (if ($ks | index($want)) then $want else $ks[0] end) as $c |
  {choice: $c, confidence: 0.9, probabilities: ($ks | map({key: ., value: (if . == $c then 0.9 else (0.1 / (($ks | length) - 1)) end)}) | from_entries)}))}' \
  "$DRIVE_LOG/body" > "$out"
printf 200
SH
chmod +x "$BIN/curl"
export DRIVE_LOG="$LOG" PATH="$BIN:$PATH"
reset_log() { rm -f "$LOG"/*; }
requests() { cat "$LOG/count" 2>/dev/null || echo 0; }
say() { printf '\n=== %s\n' "$*"; }
expect() {  # <description> <command...>
  local d=$1; shift
  if "$@" >/dev/null 2>&1; then printf 'PASS  %s\n' "$d"; else printf 'FAIL  %s\n' "$d"; FAILS=$((FAILS + 1)); fi
}
body_has() { grep -qF -- "$1" "$LOG"/body* 2>/dev/null; }
body_lacks() { ! body_has "$1"; }
eq() { [ "$1" = "$2" ]; }

fm_git_identity

###########################################################################
say "A. commit check: real git commits through the hooks --install writes"
H="$T/home"; mkdir -p "$H/config" "$H/state"
printf 'TYPESAFE_API_KEY=%s\n' "$KEY" > "$H/.env"
printf '# opted in\nlisted\n' > "$H/config/jev-code-projects"
WT="$T/wt"; fm_git_init_commit "$WT"
OTHER="$T/other"; fm_git_init_commit "$OTHER"

out=$("$ROOT/bin/fm-commit-check.sh" --install "$T/hooks-unlisted" "$H" notlisted "$WT" 2>&1); rc=$?
printf '$ fm-commit-check.sh --install <hooks> <home> notlisted <wt>   -> rc=%s output=[%s]\n' "$rc" "$out"
expect "an unlisted project gets no hooks: exit 1, nothing printed, no directory" \
  bash -c "[ $rc -eq 1 ] && [ -z '$out' ] && [ ! -e '$T/hooks-unlisted' ]"
out=$("$ROOT/bin/fm-commit-check.sh" --install "$T/hooks-other-owner" "$H" someone/listed "$WT" 2>&1); rc=$?
expect "an owner/repo spelling does not match the bare project entry" eq "$rc" 1

GCP=$("$ROOT/bin/fm-commit-check.sh" --install "$T/hooks" "$H" listed "$WT"); rc=$?
printf '$ fm-commit-check.sh --install <hooks> <home> listed <wt>      -> rc=%s setting=[%s]\n' "$rc" "${GCP//$T/<tmp>}"
expect "a listed project with a key gets the hooks setting" bash -c "[ $rc -eq 0 ] && [ -n \"\$1\" ]" _ "$GCP"

reset_log
mkdir -p "$WT/src"
printf 'const SOURCE_BODY_MARKER = 41\n' > "$WT/src/parser.js"
printf 'SOURCE_BODY_MARKER docs\n' > "$WT/notes.md"
git -C "$WT" add -A
msg=$(printf 'Parse trailing commas\n\nReproduced with the staging token:\nGH_TOKEN=%s\nNothing else changed.' "$GHP")
err=$(GIT_CONFIG_PARAMETERS=$GCP git -C "$WT" commit -q -m "$msg" 2>&1); rc=$?
printf '$ git commit -m "<message holding a GitHub token line>"   -> rc=%s stderr=[%s] requests=%s\n' "$rc" "$err" "$(requests)"
printf 'state sent to Jev:\n'; jq .state "$LOG/body"
expect "the commit is made" eq "$(git -C "$WT" log -1 --format=%s)" 'Parse trailing commas'
expect "exactly one request per commit" eq "$(requests)" 1
expect "the token pasted into the message is not in the request" body_lacks "$GHP"
expect "its line is replaced by the fixed placeholder" body_has 'line withheld: looks like a credential'
expect "no line of staged content is sent, only names" body_lacks SOURCE_BODY_MARKER
expect "the key is not on curl argv" bash -c "! grep -qF '$KEY' '$LOG/argv'"

reset_log
printf 'x = 2\n' > "$WT/src/a.py"; printf 'y = 2\n' > "$WT/src/b.py"; git -C "$WT" add -A
before=$(git -C "$WT" rev-parse HEAD)
err=$(DRIVE_CHOICE=yes GIT_CONFIG_PARAMETERS=$GCP git -C "$WT" commit -q -m 'update' 2>&1); rc=$?
printf '$ git commit -m update   (Jev answers yes to every question)   -> rc=%s\n%s\n' "$rc" "$err"
expect "three confident yes answers only advise: the commit still goes through" \
  bash -c "[ $rc -eq 0 ] && [ '$(git -C "$WT" rev-parse HEAD)' != '$before' ]"
expect "the advice says the commit goes through" bash -c "printf '%s' \"\$1\" | grep -q 'advisory, the commit goes through'" _ "$err"

reset_log
printf 'z = 3\n' > "$WT/src/c.py"; git -C "$WT" add -A
err=$(DRIVE_CURL_FAIL=28 GIT_CONFIG_PARAMETERS=$GCP git -C "$WT" commit -q -m 'Add c module' 2>&1); rc=$?
printf '$ git commit   (Jev times out)   -> rc=%s stderr=[%s]\n' "$rc" "$err"
expect "a Jev timeout neither stops the commit nor says anything" bash -c "[ $rc -eq 0 ] && [ -z \"\$1\" ]" _ "$err"

reset_log
printf 'token = "%s"\n' "$GHP" > "$WT/src/conf.py"; git -C "$WT" add -A
before=$(git -C "$WT" rev-parse HEAD)
err=$(GIT_CONFIG_PARAMETERS=$GCP git -C "$WT" commit -q -m 'Add the client config' 2>&1); rc=$?
printf '$ git commit   (a staged line holds a GitHub token)   -> rc=%s\n%s\n' "$rc" "$err"
expect "the pattern stop refuses the commit by code alone" bash -c "[ $rc -ne 0 ] && [ '$(git -C "$WT" rev-parse HEAD)' = '$before' ]"
expect "the stop names file:line and kind, never the value" bash -c "printf '%s' \"\$1\" | grep -q 'src/conf.py:1: GitHub token' && ! printf '%s' \"\$1\" | grep -qF '$GHP'" _ "$err"
expect "nothing is sent for a stopped commit" eq "$(requests)" 0
git -C "$WT" reset -q --hard

reset_log
printf 'token = "%s"\n' "$GHP" > "$OTHER/conf.py"; git -C "$OTHER" add -A
err=$(GIT_CONFIG_PARAMETERS=$GCP git -C "$OTHER" commit -q -m 'x' 2>&1); rc=$?
printf '$ git commit in ANOTHER repository under the same exported setting   -> rc=%s stderr=[%s] requests=%s\n' "$rc" "$err" "$(requests)"
expect "a repository that is not the task worktree is untouched: committed, silent, nothing sent" \
  bash -c "[ $rc -eq 0 ] && [ -z \"\$1\" ] && [ '$(requests)' = 0 ]" _ "$err"

reset_log
: > "$H/config/jev-code-projects"
printf 'w = 4\n' > "$WT/src/d.py"; git -C "$WT" add -A
err=$(GIT_CONFIG_PARAMETERS=$GCP git -C "$WT" commit -q -m 'update' 2>&1); rc=$?
printf '$ git commit after the project was removed from the list   -> rc=%s stderr=[%s] requests=%s\n' "$rc" "$err" "$(requests)"
expect "delisting the project stops every send at once" bash -c "[ $rc -eq 0 ] && [ '$(requests)' = 0 ]"

###########################################################################
say "B. risk level: real bin/fm-pr-check.sh registration"
cat > "$BIN/gh" <<'SH'
#!/usr/bin/env bash
printf '%s\n' "$*" >> "$DRIVE_LOG/gh-calls"
[ -n "${DRIVE_GH:-}" ] && exec "$DRIVE_GH" "$@"
[ -n "${FAKE_PR_JSON:-}" ] || exit 1
printf '%s\n' "$FAKE_PR_JSON"
SH
chmod +x "$BIN/gh"
PROJ="$T/alpha"; fm_git_init_commit "$PROJ"
PWT="$T/alpha-wt"; git -C "$PROJ" worktree add -q -b task-a "$PWT"
mkdir -p "$PWT/src" "$PWT/deploy"
printf 'export const retries = 3\nconst awsKey = "%s"\n' "$AWS" > "$PWT/src/client.js"
printf 'ENV_FILE_MARKER=1\n' > "$PWT/deploy/.env"
printf 'PEM_FILE_MARKER\n' > "$PWT/deploy/server.pem"
git -C "$PWT" add -A; git -C "$PWT" commit -qm 'client and deploy files'
URL='https://github.com/acme/alpha/pull/8'
export FM_HOME="$H" FM_GUARD_GRACE=999999
register() {  # [env...] [-- extra args]
  fm_write_meta "$H/state/task-a.meta" "window=fm-task-a" "kind=ship" "worktree=$PWT" "project=$PROJ"
  rm -f "$H/state/task-a.pr-poll"; reset_log
  env FM_STATE_OVERRIDE="$H/state" FAKE_PR_JSON='{"title":"Add client","body":"Adds the client."}' "$@" 2>"$T/err"
}
show() { printf '$ %s\n%s\n(rc=%s, requests=%s)\n' "$1" "$2" "$3" "$(requests)"; }

printf 'alpha\n' > "$H/config/jev-code-projects"
out=$(register "$ROOT/bin/fm-pr-check.sh" task-a "$URL"); rc=$?
show "fm-pr-check.sh task-a $URL   [alpha listed

... [2165 bytes truncated] ...

eck.sh task-a $URL --no-risk   [what fm-pr-merge.sh runs]" "$out" "$rc"
expect "the merge-time registration makes no request and prints no risk line" \
  bash -c "[ $rc -eq 0 ] && [ '$(requests)' = 0 ] && ! printf '%s' \"\$1\" | grep -q risk" _ "$out"

###########################################################################
say "C. failed check sort: real bin/fm-pr-state.sh over a fixture forge"
HEADSHA=c2eac54c17a1ddc2633ad51b83e21e5fe888142e
cat > "$BIN/gh-forge" <<SH
#!/usr/bin/env bash
set -o pipefail
serve() {
  case "\$*" in
    "pr view "*" --json state,mergedAt,isDraft,headRefOid,author,mergeable,reviewDecision --jq "*)
      echo '{"state":"OPEN","mergedAt":null,"isDraft":false,"headRefOid":"$HEADSHA","author":{"login":"a","is_bot":false},"mergeable":"MERGEABLE","reviewDecision":"APPROVED"}' ;;
    "api /repos/o/r/pulls/7/reviews"*) echo '[]' ;;
    "pr checks "*) echo '[{"name":"CI Status","state":"FAILURE","bucket":"fail","workflow":"ci"},{"name":"slow","state":"IN_PROGRESS","bucket":"pending","workflow":"ci"}]' ;;
    "api -X GET /repos/o/r/commits/$HEADSHA/check-runs "*)
      echo '{"check_runs":[{"id":41,"status":"completed","conclusion":"failure","details_url":"https://github.com/o/r/actions/runs/5/job/41","app":{"slug":"github-actions"}}]}' ;;
    "api -X GET /repos/o/r/actions/runs/5/jobs "*) echo '{"jobs":[]}' ;;
    "run view --job 41 --log-failed -R o/r")
      printf 'build\tpush\t2026-10-06T10:00:00.0000000Z error: push failed with GH_TOKEN=%s\nbuild\ttest\t2026-10-06T10:00:01.0000000Z assert failed in the parser\n' "$GHP" ;;
    "pr view "*" --json baseRefName --jq .baseRefName") echo '{"baseRefName":"main"}' ;;
    "api -X GET /repos/o/r/commits/main/check-runs "*) echo '{"check_runs":[]}' ;;
    *) echo "unexpected gh call: \$*" >&2; exit 91 ;;
  esac
}
prog=; prev=
for arg in "\$@"; do [ "\$prev" != --jq ] || prog=\$arg; prev=\$arg; done
if [ -n "\$prog" ]; then serve "\$@" | jq -r "\$prog"; else serve "\$@"; fi
SH
chmod +x "$BIN/gh-forge"
state() {  # [env...] -- args
  reset_log
  env DRIVE_GH="$BIN/gh-forge" DRIVE_CHOICE=code_bug "$@" 2>"$T/err"
}
PRURL=https://github.com/o/r/pull/7
printf 'o/r\n' > "$H/config/jev-code-projects"
plain=$(state "$ROOT/bin/fm-pr-state.sh" "$PRURL"); rc=$?
show "fm-pr-state.sh $PRURL   [o/r listed, key present, option NOT given]" "$plain" "$rc"
expect "without --sort-failed-checks nothing is sent and no label is printed" \
  bash -c "[ '$(requests)' = 0 ] && ! printf '%s' \"\$1\" | grep -q 'FAILED CHECK SORT'" _ "$plain"
expect "without the option no check-run or job-log read is made" bash -c "! grep -Eq 'check-runs|run view' '$LOG/gh-calls'"

out=$(state "$ROOT/bin/fm-pr-state.sh" --sort-failed-checks "$PRURL"); rc=$?
show "fm-pr-state.sh --sort-failed-checks $PRURL   [o/r listed]" "$out" "$rc"
printf 'state sent to Jev:\n'; jq .state "$LOG/body" 2>/dev/null
expect "the failed check gains one label and the blocker lines are unchanged" \
  eq "$out" "$(printf '%s\nFAILED CHECK SORT: CI Status: code bug (jev, confidence 0.9)' "$plain")"
expect "the token in the job log is not in the request" body_lacks "$GHP"
expect "the failure line beside it is sent" body_has 'assert failed in the parser'

for entry in r x/r O/R 'o/r-web' '# o/r'; do
  printf '%s\n' "$entry" > "$H/config/jev-code-projects"
  out=$(state "$ROOT/bin/fm-pr-state.sh" --sort-failed-checks "$PRURL"); rc=$?
  printf '$ list entry [%s] -> %s (requests=%s)\n' "$entry" "$(printf '%s' "$out" | grep 'FAILED CHECK SORT')" "$(requests)"
  expect "list entry '$entry' does not allow o/r: unknown, nothing sent" \
    bash -c "[ '$(requests)' = 0 ] && printf '%s' \"\$1\" | grep -qx 'FAILED CHECK SORT: CI Status: unknown'" _ "$out"
done
printf 'o/r\n' > "$H/config/jev-code-projects"
out=$(state DRIVE_CURL_FAIL=7 "$ROOT/bin/fm-pr-state.sh" --sort-failed-checks "$PRURL"); rc=$?
show "fm-pr-state.sh --sort-failed-checks $PRURL   [Jev unreachable]" "$out" "$rc"
expect "with Jev down the report is intact, exit 0, label unknown" \
  eq "$rc:$out" "0:$(printf '%s\nFAILED CHECK SORT: CI Status: unknown' "$plain")"
mv "$H/.env" "$H/.env.off"
out=$(state "$ROOT/bin/fm-pr-state.sh" --sort-failed-checks "$PRURL"); rc=$?
mv "$H/.env.off" "$H/.env"
expect "without a key the option prints exactly the plain report" eq "$out" "$plain"

###########################################################################
say "D. the one caller: adversarial inputs to the credential filter"
ask() {  # <state-json> -> the state as sent
  reset_log
  printf '%s' "$1" > "$T/state.json"
  ( . "$ROOT/bin/fm-jev-lib.sh"; fm_jev_key_load "$H" && fm_jev_choice q 'pick' "$T/state.json" <(printf '{"yes":"y","no":"n"}') ) >/dev/null 2>&1
  jq -c .state "$LOG/body" 2>/dev/null
}
BODY1=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7
BODY2=VJTUt9Us8cKjMzEfYyjiWA4R4ypbHjGmqkQH3uRwPz1JhG0VbXw9
tail_only=$(jq -cn --arg b1 "$BODY1" --arg b2 "$BODY2" '{block: "\($b1)\n\($b2)\n-----END RSA PRIVATE KEY-----\nplain line after"}')
sent=$(ask "$tail_only"); printf 'key tail with no BEGIN line   -> sent: %s\n' "$sent"
expect "a key tail cut off from its BEGIN line sends no body line and no END marker" \
  bash -c "! grep -qF '$BODY1' '$LOG/body' && ! grep -qF '$BODY2' '$LOG/body' && ! grep -q 'END RSA' '$LOG/body'"
expect "the ordinary line after the block is still sent" body_has 'plain line after'
whole=$(jq -cn --arg b1 "$BODY1" '{a: {deep: ["before\n-----BEGIN OPENSSH PRIVATE KEY-----\n\($b1)\n-----END OPENSSH PRIVATE KEY-----\nafter"]}}')
sent=$(ask "$whole"); printf 'whole block, nested in the state -> sent: %s\n' "$sent"
expect "a whole private key block nested deep in the state is replaced by one placeholder" \
  eq "$sent" '{"a":{"deep":["before\n[line withheld: looks like a credential]\nafter"]}}'
open=$(jq -cn --arg b1 "$BODY1" '{t: "x\n-----BEGIN PRIVATE KEY-----\n\($b1)\nstill key"}')
sent=$(ask "$open"); printf 'block with no END line          -> sent: %s\n' "$sent"
expect "a block with no END line is withheld to the end of the string" eq "$sent" '{"t":"x\n[line withheld: looks like a credential]"}'
mixed=$(jq -cn --arg g "$GHP" --arg a "$AWS" '{lines: ["ok line", "export GH=\($g)", "id \($a) here", "password = \"hunter2hunter2\"", "xoxb-1234567890-abcdefghij", "the token type is bearer"]}')
sent=$(ask "$mixed"); printf 'one string per credential kind  -> sent: %s\n' "$sent"
expect "GitHub, AWS, Slack and quoted-password lines are each withheld; ordinary lines pass" \
  eq "$sent" '{"lines":["ok line","[line withheld: looks like a credential]","[line withheld: looks like a credential]","[line withheld: looks like a credential]","[line withheld: looks like a credential]","the token type is bearer"]}'

###########################################################################
say "E. house-rules check: an embedded private key spanning two 80-line blocks"
HR="$T/hr"; fm_git_init_commit "$HR"; git -C "$HR" checkout -q -b task
mkdir -p "$HR/src"
{
  for i in $(seq 1 59); do printf 'value_%s = %s\n' "$i" "$i"; done
  printf 'KEY = """-----BEGIN RSA PRIVATE KEY-----\n'
  for i in $(seq 1 49); do printf 'KEYBODYLINE%sMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEA\n' "$i"; done
  printf -- '-----END RSA PRIVATE KEY-----"""\n'
  for i in $(seq 1 10); do printf 'after_%s = %s\n' "$i" "$i"; done
} > "$HR/src/fixtures.py"
git -C "$HR" add -A; git -C "$HR" commit -qm 'fixtures'
printf 'hr\n' > "$H/config/jev-code-projects"
reset_log
out=$(cd "$HR" && "$ROOT/bin/fm-house-rules-check.sh" hr 2>&1); rc=$?
printf '$ fm-house-rules-check.sh hr   -> rc=%s requests=%s\n%s\n' "$rc" "$(requests)" "$out"
printf 'key lines in any request body: %s\n' "$(cat "$LOG"/body.* 2>/dev/null | grep -c 'KEYBODYLINE\|PRIVATE KEY')"
expect "the change was actually sent in more than one block" bash -c "[ '$(requests)' -ge 2 ]"
expect "ordinary lines of both blocks are sent" bash -c "grep -q 'value_1 = 1' '$LOG'/body.* && grep -q 'after_10 = 10' '$LOG'/body.*"
expect "no key body line, BEGIN line or END line is in any request" bash -c "! cat '$LOG'/body.* | grep -q 'KEYBODYLINE\|PRIVATE KEY'"
expect "the check exits 0 whatever Jev says" eq "$rc" 0

printf '\n%s check(s) failed\n' "$FAILS"
exit "$FAILS"
Evidence: What reached Jev when credentials were planted
commit message -> "Parse trailing commas\n\nReproduced with the staging token:\n[line withheld: looks like a credential]\nNothing else changed." files: [notes.md, src/parser.js]
risk diff_start -> +export const retries = 3 / [line withheld: looks like a credential] (.env and .pem content absent)
failed-check log_tail -> "[line withheld: looks like a credential]\nassert failed in the parser"
key tail with no BEGIN line -> {"block":"[line withheld: looks like a credential]\nplain line after"}
house-rules, key split across two blocks -> 4 requests, 0 key lines in any body

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ bin/fm-pr-risk-lib.sh:181 - Intent ruling: "a line that looks like a credential is never sent". Three paths in the combined change send text to typesafe.ai with no credential-line filter. (1) bin/fm-pr-risk-lib.sh:181 sends the first 30000 bytes of the pull request diff as diff_start for a listed project; nothing drops secret-shaped files (.env, *.pem) or lines matching a credential format, so a PR that adds AWS_KEY=AKIA... in an ordinary file has that line sent at registration. (2) bin/fm-check-sort-lib.sh:123 sends up to 4000 characters of the failed job's log as log_tail with no filter. (3) bin/fm-commit-check.sh:215 sends the commit message; the credential stop scans only added lines of staged files, so a token pasted into the message is sent. The commit check's FM_CC_CREDENTIALS patterns exist but are applied only to staged added lines. Policy decision needed: does the ruling cover the risk-level diff excerpt, the failed-check log tail and the commit message, or only staged content in the commit check? If it covers them, the smallest remedy is to drop lines matching the existing credential patterns before each of those three payloads is built.
  • ⚠️ bin/fm-house-rules-check.sh:78 - Commit ed0692e ("gate the house-rules check on the shared Jev project list") is not one of the four items the intent names and is not in the commit lists of PRs 45 or 49 (47 and 48 were not checked). It changes an already-landed feature's opt-in in both directions: a home that opted a project in through projects in config/house-rules.json silently loses the check (that key is now ignored), and any project listed in config/jev-code-projects for another check (for example the review finding sort) now has its changed source lines sent by the house-rules check with no separate opt-in. The project is still explicitly named, so the "explicitly named" ruling holds, but no intent requirement needs this component; recommend removing the commit from the batch unless the author confirms it. Side effect of the same change: the gate reads $FM_HOME/config/jev-code-projects while the rules are still read from ${FM_CONFIG_OVERRIDE:-$FM_HOME/config}/house-rules.json, so with FM_CONFIG_OVERRIDE set the two halves read different directories.

🔧 Fix applied.
2 issues (1 warning, 1 info) still open:

  • ⚠️ bin/fm-jev-lib.sh:165 - The fix round's withhold filter recognises a private key block only from its BEGIN line, and tracks the open block per string. A caller that cuts text before handing it over can deliver the tail of a block without its BEGIN line, and then the key body lines and the -----END ... PRIVATE KEY----- line are sent unchanged (line 166-168: no BEGIN, and no pattern matches a base64 body line or the END marker). Concrete path: bin/fm-house-rules-check.sh cuts each hunk into 80-line blocks (house_rules_blocks, count &gt;= max), one request per block. A listed project adds a file not named like a secret (e.g. tests/fixtures.py) holding an embedded PEM key at added lines 60-110: block 1 has BEGIN and is withheld to its end, block 2 starts with ~30 key body lines plus the END line and is sent whole. The same happens for a -U3 hunk whose leading context lines are the end of an existing key, and for bin/fm-check-sort-lib.sh's tail -c 4000 when it cuts off the BEGIN line. This contradicts the library's own contract ("a private key block replaced whole") and docs/configuration.md. Smallest fix, inside the one filter: when an END line is met with no block open, replace everything already kept from that string, and the END line, with the single placeholder. Add one case to the library-boundary test in tests/fm-jev-wake-triage.test.sh (a string starting with body lines and an END line).
  • ℹ️ bin/fm-watch.sh:1753 - Side effect of filtering at the one caller, noted as a tradeoff: when a worker's newest paused: status line matches a credential pattern or the generic secret literal (e.g. paused: deploy needs API_KEY=&#34;abcd1234efgh&#34; rotated, who owns it?), routine-wake triage now asks Jev over [line withheld: looks like a credential] in place of that line, and a routine answer at or above the floor still absorbs the reminder; the fix round's own test (tests/fm-jev-wake-triage.test.sh, "a status line holding a credential is withheld") asserts that absorb. The harm is bounded: the status line itself was already delivered unasked, only the later recheck reminders are absorbed, JEV_TRIAGE_MAX_STREAK caps it at 6 in a row, and the question tells the model to choose needs_firstmate when the wait is unclear. Making code refuse to absorb when a line was withheld would need a new output from the library, which is beyond what the fix was asked to do.

🔧 Fix applied.
1 info still open:

  • ℹ️ bin/fm-jev-lib.sh:169 - Residual limit of the round-2 fix, noted as a tradeoff: the filter recognises a private key block by its BEGIN or END line, so a string holding only body lines (neither marker) is sent as is. That needs a caller's cut to land on both sides of one key: a key body over 80 lines covering a whole middle block in bin/fm-house-rules-check.sh (an ordinary RSA 4096 key is about 51 lines, so it spans at most two blocks and both are now withheld), or a key printed unterminated at the very end of a failed job's log with its BEGIN line beyond tail -c 4000 in bin/fm-check-sort-lib.sh. I could not construct a sequence that intended usage actually performs, and base64 body lines cannot be told from ordinary text without a new heuristic, so no change is recommended.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 14 of 17 scenarios driven live against the product
Scenario Result Live Evidence
A worker on a listed project commits with a GitHub token pasted into the commit message: the commit is made, one request is sent, and it holds the message with that line replaced by the placeholder pl… ✅ pass live jev-batch-drive-transcript.txt section A (state sent to Jev shown); TypeSafe endpoint replaced by a local recorder
A worker stages a line holding a GitHub token: the commit is refused by pattern alone, naming file:line and kind but not the value, and nothing is sent ✅ pass live jev-batch-drive-transcript.txt section A: rc=1, 'src/conf.py:1: GitHub token', requests=0, HEAD unchanged
Commit check stays off for an unlisted project, for an owner/repo spelling of a bare entry, for another repository under the same exported hooks setting, and as soon as the project is delisted ✅ pass live jev-batch-drive-transcript.txt section A: --install exits 1 with no output and no hooks directory; commits elsewhere go through silently with requests=0
Jev answering yes to all three commit questions, or timing out, never stops a commit ✅ pass live jev-batch-drive-transcript.txt section A: rc=0 with three 'advisory, the commit goes through' lines; rc=0 and silence on a timeout
Registering a pull request for a listed project prints one risk line beside the unchanged registration, and the diff excerpt sent leaves out an AWS key line and the contents of .env and .pem files ✅ pass live jev-batch-drive-transcript.txt section B (diff_start sent to Jev shown); pr= recorded and merge poll armed
A firstmate project is not listed by an owner/repo entry, a similar name or a commented name: the registration still succeeds, nothing is sent and the forge description is not read ✅ pass live jev-batch-drive-transcript.txt section B: list 'acme/alpha, alpha-web, # alpha' gives requests=0 and 'project not listed'
The risk level only advises: a worst-case Jev answer or a Jev timeout still records the pull request and arms the merge poll ✅ pass live jev-batch-drive-transcript.txt section B: 'risk: high ...' and 'risk: not rated ... (Jev error)' both with rc=0 and the poll armed
The merge-time registration (--no-risk, what fm-pr-merge.sh runs) makes no Jev request and prints no risk line ✅ pass live jev-batch-drive-transcript.txt section B: requests=0, output is the arming line only; fm-pr-merge.sh itself covered by tests/fm-pr-merge.test.sh test_merge_registration_never_rates_risk
fm-pr-state.sh without --sort-failed-checks, with a key and the repository listed, sends nothing, reads no check runs or job log, and prints no label ✅ pass live jev-batch-drive-transcript.txt section C: requests=0, only the two REQUIRED CHECK lines
fm-pr-state.sh --sort-failed-checks on a listed owner/repo adds one label under unchanged blocker lines, and a token in the job log is withheld from the request ✅ pass live jev-batch-drive-transcript.txt section C (state sent to Jev shown: log_tail starts with the placeholder)
For the repository-keyed sort, a bare name, another owner, different case, a longer name and a commented entry do not allow o/r: label is unknown and nothing is sent ✅ pass live jev-batch-drive-transcript.txt section C: five list entries, each 'unknown' with requests=0
With Jev unreachable or no key, the failed-check report keeps its lines and exit status ✅ pass live jev-batch-drive-transcript.txt section C: rc=0 with 'unknown' when Jev is down; output identical to the plain report with no key
The one Jev caller withholds a private key tail that arrives without its BEGIN line, a whole nested key block, a block with no END line, and GitHub, AWS, Slack and quoted-password lines, while ordinar… ✅ pass live jev-batch-drive-transcript.txt section D (each sent state shown)
House-rules check on a listed project whose change embeds a private key spanning two 80-line blocks sends no key body, BEGIN or END line in any request ✅ pass live jev-batch-drive-transcript.txt section E: 4 requests over 2 blocks, 0 key lines in any body, rc=0
The watcher offers the pause recheck of an exited worker with an open pull request to triage, absorbs it on a routine answer, delivers on any other answer or error, and withholds a credential in a sta… ⏸️ untested no The prior payload did not establish a live result: this was exercised only through the existing suite bash tests/fm-jev-wake-triage.test.sh (which passed) on a fake terminal backend with a fake curl…
The finished check sends only the worker's closing message, never a shell command the worker ran ⏸️ untested no The prior payload did not establish a live result: this was exercised only through the existing suite bash tests/fm-finished-check.test.sh (which passed), not by a separate manual drive of the finis…
The real TypeSafe model rates 'waiting for the merge word' from an exited worker as routine, and answers the commit-message and failed-check questions sensibly ⏸️ untested no No TYPESAFE_API_KEY is available in this environment (not in the environment, no firstmate home configured). To test, provide a key in the environment or a home's .env and re-run the callers against h…
  • bash tests/fm-commit-check.test.sh
  • bash tests/fm-pr-risk.test.sh
  • bash tests/fm-pr-state.test.sh
  • bash tests/fm-jev-wake-triage.test.sh
  • bash tests/fm-house-rules-check.test.sh
  • bash tests/fm-finding-sort.test.sh
  • bash tests/fm-pr-merge.test.sh
  • bash tests/fm-dispatch-resolve.test.sh
  • bash tests/fm-finished-check.test.sh
  • bash ~/.no-mistakes/evidence/01M473YB6E12KYY47NVN9RMKJ7/jev-batch-drive.sh run from the worktree root: section A bin/fm-commit-check.sh --install plus real git commit runs under the exported GIT_CONFIG_PARAMETERS
  • same script, section B: bin/fm-pr-check.sh task-a &lt;url&gt; with the project listed, unlisted, Jev answering yes to everything, Jev timing out, and --no-risk
  • same script, section C: bin/fm-pr-state.sh [--sort-failed-checks] &lt;url&gt; with list entries o/r, r, x/r, O/R, o/r-web, # o/r, with Jev unreachable, and with no key
  • same script, section D: fm_jev_choice from bin/fm-jev-lib.sh with a key tail missing its BEGIN line, a nested whole key block, a block with no END line, and one line per credential kind
  • same script, section E: bin/fm-house-rules-check.sh hr over a file with a private key spanning two 80-line blocks
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…to routine-wake triage

The pause recheck is reached for an ordinary crew only once its agent is
confidently dead, and the triage refused every agent that was not alive, so the
recheck it was wired to could never be offered. A worker whose agent has exited
beside an open pull request is now offered, the model is told which, and the
question reads a wait for the merge word of that pull request as the already
reported merge whoever gives it.
…onment, opt-in

The sort runs only when its caller passes --sort-failed-checks, sends a failure
log only for a repository listed as owner/repo, and never changes a blocker line
or an exit status.
…v, opt-in

The hook reaches a worker only on a project listed in config/jev-code-projects,
acts only on a commit to the task's own worktree, sends the message and the
staged file names and no file content, and stops a commit only for an added
line in a recognised credential format.
The house-rules check kept its own opt-in list in config/house-rules.json while
every other check that sends a project's code asks fm_jev_code_allowed. It now
asks the same function, so config/jev-code-projects is the one place a project
is named, config/house-rules.json holds only optional rules, and one
docs/configuration.md section owns the list's format and its owner/repo rule.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant