feat(rch): converge recovery contract (age-h433.54)

This commit is contained in:
boshu
2026-07-12 06:12:34 -04:00
parent 28e6cd543d
commit c57eaffbe6
6 changed files with 294 additions and 4 deletions
+2 -2
View File
@@ -819,8 +819,8 @@
{
"name": "rch",
"source_skill": "skills/rch",
"source_hash": "8acdb18dfca92a0f74b825a55919652a48ba019a41a959fcaf1dc4239d4443ef",
"generated_hash": "66a9faf8688f99543648d52f6de00a2b28bd9e45d034e86c22e71a13b9a71b12"
"source_hash": "39e5812214beeb04475dbe7ebf3ae1fdeed8ec47448bad248b06490cd012e075",
"generated_hash": "ee381f73f5548f2d8c9238e0410e25a256841c9c0b5d33250212e6f73ec0637b"
},
{
"name": "reality-check",
+2 -2
View File
@@ -2,6 +2,6 @@
"generator": "codex-sync",
"source_skill": "skills/rch",
"layout": "modular",
"source_hash": "8acdb18dfca92a0f74b825a55919652a48ba019a41a959fcaf1dc4239d4443ef",
"generated_hash": "66a9faf8688f99543648d52f6de00a2b28bd9e45d034e86c22e71a13b9a71b12"
"source_hash": "39e5812214beeb04475dbe7ebf3ae1fdeed8ec47448bad248b06490cd012e075",
"generated_hash": "ee381f73f5548f2d8c9238e0410e25a256841c9c0b5d33250212e6f73ec0637b"
}
+34
View File
@@ -8,6 +8,21 @@ description: Use when offloading slow builds to remote
This skill is the operational layer agents use when something about that pipeline isn't working — and the much more common case where it *thinks* it's working but is silently falling back to local execution. The skill is built around a single principle: **self-resolve before asking the human.** Every recovery path here is one the agent can run on its own.
## Constraints
- **Observe before mutation.** Capture the `[RCH]` summary, `rch doctor --json`, and the first failing triage stage before changing daemon, hook, worker, or disk state because an unobserved repair destroys the evidence needed to prove causality.
- **Fail-open is not success.** Treat every `[RCH] local (<reason>)` result as unresolved until the named reason is repaired or deliberately accepted because a green local build does not prove remote offload worked.
- **Bound autonomous repair.** Apply only the documented idempotent or precondition-guarded fixes in this skill, and preview destructive cleanup because remote workers may contain active builds or another agent's state.
- **Consult the pawl before raising the andon.** WARN, FAIL, or REFUTED contract results repair and rerun automatically because ordinary rejection is diagnostic evidence; only a breaker may enter HOLD or consume the single helper lane.
## Breaker State Machine
- **Ordinary rejection — `WARN|FAIL|REFUTED -> AUTO-REDO`:** repair the named defect, recapture evidence, and rerun the failed check; plain rejection never enters HOLD and never consumes the helper lane.
- **Breaker — `BREAKER -> HOLD -> ONE-HELPER`:** when recovery cannot proceed safely with available permissions or observability, freeze mutations and route exactly one bounded helper consultation with the evidence packet.
- **Recovered — `HELPER-UNSTUCK -> AUTO-REDO`:** leave HOLD, apply the bounded recovery, and re-earn the deterministic check plus pawl verdict before declaring the pipeline healthy.
- **Helper escalation — `HELPER-ESCALATE -> HUMAN`:** stop automation and send the helper-provided concrete escalation packet to the operator.
- **Direct human lane — `REFUSAL-LANE|EXPLICIT-JUDGMENT|EXHAUSTED-BUDGET -> HUMAN`:** skip the helper and route directly to the operator; these are the only direct-human states.
**Don't re-learn the command surface here.** `rch --help`, `rch doctor`, and the machine surfaces (`--json`, `--schema`, `--help-json`, `--capabilities` — see [MACHINE_INTROSPECTION.md](references/MACHINE_INTROSPECTION.md)) self-describe every subcommand, flag, and env var. Env-var knobs and config precedence: [CONFIGURATION.md](references/CONFIGURATION.md). This skill carries the triage doctrine and recovery playbook routing.
Tested against rch v1.0.18; concepts apply to v1.0.16+.
@@ -59,6 +74,8 @@ ssh ubuntu@<host> 'du -sh /tmp/rch-* /tmp/rch_target_* 2>/dev/null | sort -h'
Always check both `/` and `/tmp` on the worker before deciding what to fix. End-to-end verify: `rch self-test --all`; comprehensive checks: `rch doctor` (`--fix --dry-run` previews auto-fixes).
**Checkpoint:** before the first mutation, record the failing stage, exact command, exit code, and relevant `[RCH]` summary in the run evidence; after repair, rerun that same probe so the before/after claim is falsifiable.
---
## Quick Fixes
@@ -102,6 +119,23 @@ When in genuine doubt, capture the escalation packet (Playbook end of [RECOVERY_
---
## Output Specification
- **Path:** `.agents/evidence/remote-compilation/<run-id>/evidence.json` in the active repository, with raw command output stored beside it when needed.
- **Filename convention:** the machine handoff is always `evidence.json`; `<run-id>` is a filesystem-safe timestamp or task identifier unique to the recovery attempt.
- **Serialization/schema format:** JSON object `rch-evidence.v1` with nonempty `run_id`, `summary_line`, and `next_action`; enum `status` (`healthy|recovered|breaker`); enum `stage` (`availability|config|hook|classification|remote-compile|worker-pressure|complete`); nullable string `worker`; and a nonempty `commands` array of `{command:string,exit_code:number}` objects.
- **Validator command:** set `OUT=".agents/evidence/remote-compilation/<run-id>/evidence.json"`, then run `jq -e '. as $in | .schema_version=="rch-evidence.v1" and (.run_id|type=="string" and length>0) and (["healthy","recovered","breaker"]|index($in.status))!=null and (["availability","config","hook","classification","remote-compile","worker-pressure","complete"]|index($in.stage))!=null and ($in.summary_line|type=="string" and length>0) and (($in.worker==null) or ($in.worker|type=="string")) and ($in.next_action|type=="string" and length>0) and ($in.commands|type=="array" and length>0) and all($in.commands[]; (.command|type=="string" and length>0) and (.exit_code|type=="number"))' "$OUT"`.
- **Downstream handoff:** a `healthy|recovered` packet returns the verified worker and probe to the build lane; a `breaker` packet enters HOLD and accompanies the single helper, and only the explicit human states above reach the operator.
## Quality Checklist
- The evidence names the first failing stage and preserves the exact pre-repair command, exit code, and `[RCH]` summary instead of inferring success from build completion.
- Every mutation is documented, scoped to the diagnosed failure, and either idempotent or guarded by the playbook's safety precondition.
- The same probe is rerun after repair, and `rch self-test --all` or an isolated `rch exec` demonstrates remote execution rather than local fallback.
- Ordinary rejection remains in AUTO-REDO; HOLD has exactly one helper, and operator escalation is limited to the declared human states.
---
## Reference Index
Everything below ships in the skill. Read whichever is relevant.
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SKILL="$SKILL_DIR/SKILL.md"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# Backticks and jq fragments are exact Markdown markers, never shell execution.
# shellcheck disable=SC2016
MARKERS=(
'## Constraints'
'- **Observe before mutation.** Capture the `[RCH]` summary, `rch doctor --json`, and the first failing triage stage before changing daemon, hook, worker, or disk state because an unobserved repair destroys the evidence needed to prove causality.'
'- **Fail-open is not success.** Treat every `[RCH] local (<reason>)` result as unresolved until the named reason is repaired or deliberately accepted because a green local build does not prove remote offload worked.'
'- **Consult the pawl before raising the andon.** WARN, FAIL, or REFUTED contract results repair and rerun automatically because ordinary rejection is diagnostic evidence; only a breaker may enter HOLD or consume the single helper lane.'
'## Breaker State Machine'
'- **Ordinary rejection — `WARN|FAIL|REFUTED -> AUTO-REDO`:** repair the named defect, recapture evidence, and rerun the failed check; plain rejection never enters HOLD and never consumes the helper lane.'
'- **Breaker — `BREAKER -> HOLD -> ONE-HELPER`:** when recovery cannot proceed safely with available permissions or observability, freeze mutations and route exactly one bounded helper consultation with the evidence packet.'
'- **Recovered — `HELPER-UNSTUCK -> AUTO-REDO`:** leave HOLD, apply the bounded recovery, and re-earn the deterministic check plus pawl verdict before declaring the pipeline healthy.'
'- **Helper escalation — `HELPER-ESCALATE -> HUMAN`:** stop automation and send the helper-provided concrete escalation packet to the operator.'
'- **Direct human lane — `REFUSAL-LANE|EXPLICIT-JUDGMENT|EXHAUSTED-BUDGET -> HUMAN`:** skip the helper and route directly to the operator; these are the only direct-human states.'
'**Checkpoint:** before the first mutation, record the failing stage, exact command, exit code, and relevant `[RCH]` summary in the run evidence; after repair, rerun that same probe so the before/after claim is falsifiable.'
'## Output Specification'
'- **Path:** `.agents/evidence/remote-compilation/<run-id>/evidence.json` in the active repository, with raw command output stored beside it when needed.'
'- **Filename convention:** the machine handoff is always `evidence.json`; `<run-id>` is a filesystem-safe timestamp or task identifier unique to the recovery attempt.'
'- **Serialization/schema format:** JSON object `rch-evidence.v1` with nonempty `run_id`, `summary_line`, and `next_action`; enum `status` (`healthy|recovered|breaker`); enum `stage` (`availability|config|hook|classification|remote-compile|worker-pressure|complete`); nullable string `worker`; and a nonempty `commands` array of `{command:string,exit_code:number}` objects.'
'- **Downstream handoff:** a `healthy|recovered` packet returns the verified worker and probe to the build lane; a `breaker` packet enters HOLD and accompanies the single helper, and only the explicit human states above reach the operator.'
'## Quality Checklist'
'- Ordinary rejection remains in AUTO-REDO; HOLD has exactly one helper, and operator escalation is limited to the declared human states.'
)
# `$in` is an exact jq/Markdown marker, not a shell variable.
# shellcheck disable=SC2016
SUBSTRINGS=(
'- **Validator command:**'
'. as $in | .schema_version=="rch-evidence.v1"'
)
validate_contract() {
local file="$1" marker
[[ -s "$file" ]] || return 1
for marker in "${MARKERS[@]}"; do
grep -Fqx -- "$marker" "$file" || return 1
done
for marker in "${SUBSTRINGS[@]}"; do
grep -Fq -- "$marker" "$file" || return 1
done
}
validate_evidence() {
jq -e '
. as $in
| .schema_version=="rch-evidence.v1"
and (.run_id|type=="string" and length>0)
and (["healthy","recovered","breaker"]|index($in.status))!=null
and (["availability","config","hook","classification","remote-compile","worker-pressure","complete"]|index($in.stage))!=null
and ($in.summary_line|type=="string" and length>0)
and (($in.worker==null) or ($in.worker|type=="string"))
and ($in.next_action|type=="string" and length>0)
and ($in.commands|type=="array" and length>0)
and all($in.commands[]; (.command|type=="string" and length>0) and (.exit_code|type=="number"))
' "$1" >/dev/null
}
delete_one_marker_fixture() {
local marker variant="$TMP/missing-marker.md"
for marker in "${MARKERS[@]}"; do
grep -Fvx -- "$marker" "$SKILL" >"$variant"
if validate_contract "$variant"; then
echo "rch contract validator accepted a missing marker: $marker" >&2
return 1
fi
done
for marker in "${SUBSTRINGS[@]}"; do
grep -Fv -- "$marker" "$SKILL" >"$variant"
if validate_contract "$variant"; then
echo "rch contract validator accepted a missing substring: $marker" >&2
return 1
fi
done
}
evidence_fixtures() {
local valid="$TMP/valid.json" invalid="$TMP/invalid.json" field
jq -n '{schema_version:"rch-evidence.v1",run_id:"age-test",status:"recovered",stage:"complete",summary_line:"[RCH] remote worker-a (1.2s)",worker:"worker-a",next_action:"resume build",commands:[{command:"rch self-test --all",exit_code:0}]}' >"$valid"
validate_evidence "$valid"
for field in schema_version run_id status stage summary_line next_action commands; do
jq --arg field "$field" 'del(.[$field])' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted missing field: $field" >&2
return 1
fi
done
jq '.status="unknown"' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted an unknown status" >&2
return 1
fi
jq '.commands[0].exit_code="zero"' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted a nonnumeric exit code" >&2
return 1
fi
}
[[ -f "$SKILL" ]] || { echo "FAIL: missing SKILL.md" >&2; exit 1; }
head -1 "$SKILL" | grep -q '^---$' || { echo "FAIL: missing frontmatter" >&2; exit 1; }
validate_contract "$SKILL"
delete_one_marker_fixture
evidence_fixtures
echo "OK: rch"
+34
View File
@@ -15,6 +15,21 @@ practices:
This skill is the operational layer agents use when something about that pipeline isn't working — and the much more common case where it *thinks* it's working but is silently falling back to local execution. The skill is built around a single principle: **self-resolve before asking the human.** Every recovery path here is one the agent can run on its own.
## Constraints
- **Observe before mutation.** Capture the `[RCH]` summary, `rch doctor --json`, and the first failing triage stage before changing daemon, hook, worker, or disk state because an unobserved repair destroys the evidence needed to prove causality.
- **Fail-open is not success.** Treat every `[RCH] local (<reason>)` result as unresolved until the named reason is repaired or deliberately accepted because a green local build does not prove remote offload worked.
- **Bound autonomous repair.** Apply only the documented idempotent or precondition-guarded fixes in this skill, and preview destructive cleanup because remote workers may contain active builds or another agent's state.
- **Consult the pawl before raising the andon.** WARN, FAIL, or REFUTED contract results repair and rerun automatically because ordinary rejection is diagnostic evidence; only a breaker may enter HOLD or consume the single helper lane.
## Breaker State Machine
- **Ordinary rejection — `WARN|FAIL|REFUTED -> AUTO-REDO`:** repair the named defect, recapture evidence, and rerun the failed check; plain rejection never enters HOLD and never consumes the helper lane.
- **Breaker — `BREAKER -> HOLD -> ONE-HELPER`:** when recovery cannot proceed safely with available permissions or observability, freeze mutations and route exactly one bounded helper consultation with the evidence packet.
- **Recovered — `HELPER-UNSTUCK -> AUTO-REDO`:** leave HOLD, apply the bounded recovery, and re-earn the deterministic check plus pawl verdict before declaring the pipeline healthy.
- **Helper escalation — `HELPER-ESCALATE -> HUMAN`:** stop automation and send the helper-provided concrete escalation packet to the operator.
- **Direct human lane — `REFUSAL-LANE|EXPLICIT-JUDGMENT|EXHAUSTED-BUDGET -> HUMAN`:** skip the helper and route directly to the operator; these are the only direct-human states.
**Don't re-learn the command surface here.** `rch --help`, `rch doctor`, and the machine surfaces (`--json`, `--schema`, `--help-json`, `--capabilities` — see [MACHINE_INTROSPECTION.md](references/MACHINE_INTROSPECTION.md)) self-describe every subcommand, flag, and env var. Env-var knobs and config precedence: [CONFIGURATION.md](references/CONFIGURATION.md). This skill carries the triage doctrine and recovery playbook routing.
Tested against rch v1.0.18; concepts apply to v1.0.16+.
@@ -66,6 +81,8 @@ ssh ubuntu@<host> 'du -sh /tmp/rch-* /tmp/rch_target_* 2>/dev/null | sort -h'
Always check both `/` and `/tmp` on the worker before deciding what to fix. End-to-end verify: `rch self-test --all`; comprehensive checks: `rch doctor` (`--fix --dry-run` previews auto-fixes).
**Checkpoint:** before the first mutation, record the failing stage, exact command, exit code, and relevant `[RCH]` summary in the run evidence; after repair, rerun that same probe so the before/after claim is falsifiable.
---
## Quick Fixes
@@ -109,6 +126,23 @@ When in genuine doubt, capture the escalation packet (Playbook end of [RECOVERY_
---
## Output Specification
- **Path:** `.agents/evidence/remote-compilation/<run-id>/evidence.json` in the active repository, with raw command output stored beside it when needed.
- **Filename convention:** the machine handoff is always `evidence.json`; `<run-id>` is a filesystem-safe timestamp or task identifier unique to the recovery attempt.
- **Serialization/schema format:** JSON object `rch-evidence.v1` with nonempty `run_id`, `summary_line`, and `next_action`; enum `status` (`healthy|recovered|breaker`); enum `stage` (`availability|config|hook|classification|remote-compile|worker-pressure|complete`); nullable string `worker`; and a nonempty `commands` array of `{command:string,exit_code:number}` objects.
- **Validator command:** set `OUT=".agents/evidence/remote-compilation/<run-id>/evidence.json"`, then run `jq -e '. as $in | .schema_version=="rch-evidence.v1" and (.run_id|type=="string" and length>0) and (["healthy","recovered","breaker"]|index($in.status))!=null and (["availability","config","hook","classification","remote-compile","worker-pressure","complete"]|index($in.stage))!=null and ($in.summary_line|type=="string" and length>0) and (($in.worker==null) or ($in.worker|type=="string")) and ($in.next_action|type=="string" and length>0) and ($in.commands|type=="array" and length>0) and all($in.commands[]; (.command|type=="string" and length>0) and (.exit_code|type=="number"))' "$OUT"`.
- **Downstream handoff:** a `healthy|recovered` packet returns the verified worker and probe to the build lane; a `breaker` packet enters HOLD and accompanies the single helper, and only the explicit human states above reach the operator.
## Quality Checklist
- The evidence names the first failing stage and preserves the exact pre-repair command, exit code, and `[RCH]` summary instead of inferring success from build completion.
- Every mutation is documented, scoped to the diagnosed failure, and either idempotent or guarded by the playbook's safety precondition.
- The same probe is rerun after repair, and `rch self-test --all` or an isolated `rch exec` demonstrates remote execution rather than local fallback.
- Ordinary rejection remains in AUTO-REDO; HOLD has exactly one helper, and operator escalation is limited to the declared human states.
---
## Reference Index
Everything below ships in the skill. Read whichever is relevant.
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SKILL="$SKILL_DIR/SKILL.md"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# Backticks and jq fragments are exact Markdown markers, never shell execution.
# shellcheck disable=SC2016
MARKERS=(
'## Constraints'
'- **Observe before mutation.** Capture the `[RCH]` summary, `rch doctor --json`, and the first failing triage stage before changing daemon, hook, worker, or disk state because an unobserved repair destroys the evidence needed to prove causality.'
'- **Fail-open is not success.** Treat every `[RCH] local (<reason>)` result as unresolved until the named reason is repaired or deliberately accepted because a green local build does not prove remote offload worked.'
'- **Consult the pawl before raising the andon.** WARN, FAIL, or REFUTED contract results repair and rerun automatically because ordinary rejection is diagnostic evidence; only a breaker may enter HOLD or consume the single helper lane.'
'## Breaker State Machine'
'- **Ordinary rejection — `WARN|FAIL|REFUTED -> AUTO-REDO`:** repair the named defect, recapture evidence, and rerun the failed check; plain rejection never enters HOLD and never consumes the helper lane.'
'- **Breaker — `BREAKER -> HOLD -> ONE-HELPER`:** when recovery cannot proceed safely with available permissions or observability, freeze mutations and route exactly one bounded helper consultation with the evidence packet.'
'- **Recovered — `HELPER-UNSTUCK -> AUTO-REDO`:** leave HOLD, apply the bounded recovery, and re-earn the deterministic check plus pawl verdict before declaring the pipeline healthy.'
'- **Helper escalation — `HELPER-ESCALATE -> HUMAN`:** stop automation and send the helper-provided concrete escalation packet to the operator.'
'- **Direct human lane — `REFUSAL-LANE|EXPLICIT-JUDGMENT|EXHAUSTED-BUDGET -> HUMAN`:** skip the helper and route directly to the operator; these are the only direct-human states.'
'**Checkpoint:** before the first mutation, record the failing stage, exact command, exit code, and relevant `[RCH]` summary in the run evidence; after repair, rerun that same probe so the before/after claim is falsifiable.'
'## Output Specification'
'- **Path:** `.agents/evidence/remote-compilation/<run-id>/evidence.json` in the active repository, with raw command output stored beside it when needed.'
'- **Filename convention:** the machine handoff is always `evidence.json`; `<run-id>` is a filesystem-safe timestamp or task identifier unique to the recovery attempt.'
'- **Serialization/schema format:** JSON object `rch-evidence.v1` with nonempty `run_id`, `summary_line`, and `next_action`; enum `status` (`healthy|recovered|breaker`); enum `stage` (`availability|config|hook|classification|remote-compile|worker-pressure|complete`); nullable string `worker`; and a nonempty `commands` array of `{command:string,exit_code:number}` objects.'
'- **Downstream handoff:** a `healthy|recovered` packet returns the verified worker and probe to the build lane; a `breaker` packet enters HOLD and accompanies the single helper, and only the explicit human states above reach the operator.'
'## Quality Checklist'
'- Ordinary rejection remains in AUTO-REDO; HOLD has exactly one helper, and operator escalation is limited to the declared human states.'
)
# `$in` is an exact jq/Markdown marker, not a shell variable.
# shellcheck disable=SC2016
SUBSTRINGS=(
'- **Validator command:**'
'. as $in | .schema_version=="rch-evidence.v1"'
)
validate_contract() {
local file="$1" marker
[[ -s "$file" ]] || return 1
for marker in "${MARKERS[@]}"; do
grep -Fqx -- "$marker" "$file" || return 1
done
for marker in "${SUBSTRINGS[@]}"; do
grep -Fq -- "$marker" "$file" || return 1
done
}
validate_evidence() {
jq -e '
. as $in
| .schema_version=="rch-evidence.v1"
and (.run_id|type=="string" and length>0)
and (["healthy","recovered","breaker"]|index($in.status))!=null
and (["availability","config","hook","classification","remote-compile","worker-pressure","complete"]|index($in.stage))!=null
and ($in.summary_line|type=="string" and length>0)
and (($in.worker==null) or ($in.worker|type=="string"))
and ($in.next_action|type=="string" and length>0)
and ($in.commands|type=="array" and length>0)
and all($in.commands[]; (.command|type=="string" and length>0) and (.exit_code|type=="number"))
' "$1" >/dev/null
}
delete_one_marker_fixture() {
local marker variant="$TMP/missing-marker.md"
for marker in "${MARKERS[@]}"; do
grep -Fvx -- "$marker" "$SKILL" >"$variant"
if validate_contract "$variant"; then
echo "rch contract validator accepted a missing marker: $marker" >&2
return 1
fi
done
for marker in "${SUBSTRINGS[@]}"; do
grep -Fv -- "$marker" "$SKILL" >"$variant"
if validate_contract "$variant"; then
echo "rch contract validator accepted a missing substring: $marker" >&2
return 1
fi
done
}
evidence_fixtures() {
local valid="$TMP/valid.json" invalid="$TMP/invalid.json" field
jq -n '{schema_version:"rch-evidence.v1",run_id:"age-test",status:"recovered",stage:"complete",summary_line:"[RCH] remote worker-a (1.2s)",worker:"worker-a",next_action:"resume build",commands:[{command:"rch self-test --all",exit_code:0}]}' >"$valid"
validate_evidence "$valid"
for field in schema_version run_id status stage summary_line next_action commands; do
jq --arg field "$field" 'del(.[$field])' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted missing field: $field" >&2
return 1
fi
done
jq '.status="unknown"' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted an unknown status" >&2
return 1
fi
jq '.commands[0].exit_code="zero"' "$valid" >"$invalid"
if validate_evidence "$invalid"; then
echo "rch evidence validator accepted a nonnumeric exit code" >&2
return 1
fi
}
[[ -f "$SKILL" ]] || { echo "FAIL: missing SKILL.md" >&2; exit 1; }
head -1 "$SKILL" | grep -q '^---$' || { echo "FAIL: missing frontmatter" >&2; exit 1; }
validate_contract "$SKILL"
delete_one_marker_fixture
evidence_fixtures
echo "OK: rch"