← Files APODICTIC Development EditorARCHIVED FILE
scripts/validate.d/commands-b.sh
168 KB · Nov 9, 2026 · 00:53 UTC
# shellcheck shell=bash
# Sourced by ../validate.sh; not runnable on its own.
case "$COMMAND" in
# ----------------------------------------------------------------------
# audit-tier-criterion <pass_dependencies_file> [<audits_root_dir>]
#
# Mechanical check that audit tier assignments in pass-dependencies.md
# §4a/§4b match the §4c Audit Tier Promotion Criteria documented in
# the same file (Phase 6 Wave 2 added the criteria).
#
# Three criteria from §4c (per the canonical home):
# 1. The audit produces named hard gates or audit-internal Must-Fix
# floors (severity signals strong enough to gate synthesis).
# 2. The audit catches a class of issue undetectable by passes
# alone (the audit's absence creates a blind spot, not just
# lower-resolution coverage).
# 3. Disclosure is non-equivalent to running the audit (blind-spot
# disclosure cannot reasonably substitute for the audit's
# output).
#
# The validator scans §4a + §4b for tier assignments per audit and,
# for each audit at Auto-run / Auto-recommend before synthesis /
# Pre-DE Prerequisite / Hard Prerequisite tier, looks for hard-gate
# / Must-Fix-floor language in the audit's reference file. Audits
# at high tiers without named hard gates / Must-Fix floors are
# surfaced as candidates for tier review.
#
# IMPORTANT — capability ceiling. This validator can only verify
# criterion 1 mechanically (named hard gates / Must-Fix floors are
# detectable by reference-file pattern matching). Criteria 2 and 3
# require model judgment about the manuscript / fixture corpus and
# cannot be verified by bash. The validator surfaces criterion-1
# gaps; criteria 2 and 3 remain in the §4a/§4b verification
# subsection prose.
#
# Override marker: <!-- override: audit-tier-criterion-<audit-slug>
# — <rationale> --> placed in pass-dependencies.md body. One marker
# per audit; rationale must name which criterion is overridden and
# why.
#
# Self-test: pass --self-test as the only argument to run built-in
# cases.
# ----------------------------------------------------------------------
audit-tier-criterion)
if [ $# -lt 1 ]; then echo "Usage: $0 audit-tier-criterion <pass_dependencies_file> [<audits_root_dir>] | --self-test"; exit 2; fi
# Primary path: real parser in scripts/config_checks.py (Inc.5). Degrades to bash below.
CFG_DIR=$(cd "$(dirname "$0")" && pwd)
CFG_HELPER="$CFG_DIR/config_checks.py"
if [ "$1" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CFG_HELPER" ]; then python3 "$CFG_HELPER" --self-test audit-tier-criterion; exit $?; fi
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
mkdir -p "$TMPDIR/audits"
# Positive case: pass-dependencies references audits at high
# tiers that ALL document hard gates / Must-Fix floors in their
# reference files.
cat > "$TMPDIR/pos_pd.md" <<'EOF'
## §4a. Router-triggered audits
| Trigger | Audit | Tier | Reference |
|---|---|---|---|
| Erotic content flagged at intake | Erotic Content | Auto-run (bundled with workflow) | `audits/erotic-content.md` |
| Representation or reception sensitivity disclosed at intake | Reception Risk | Auto-recommend before synthesis | `audits/reception-risk.md` |
## §4b. Finding-triggered audits
| Layer | Trigger | Audit | Tier |
|---|---|---|---|
| 1 (Reader Experience) | Pacing stalls | Compression | Auto-recommend before synthesis |
EOF
cat > "$TMPDIR/audits/erotic-content.md" <<'EOF'
# Erotic Content Audit
## Hard Gates
- EC-1 hard gate: explicit non-consensual content without aftercare framing.
## Must-Fix floor
Any §Hard Gate firing produces audit-internal Must-Fix floor.
EOF
cat > "$TMPDIR/audits/reception-risk.md" <<'EOF'
# Reception Risk Audit
## §7 Severity Hard Gates
Five hard gates: extractable hate, minor exploitation, etc.
Must-Fix floor when any hard gate fires.
EOF
cat > "$TMPDIR/audits/compression-audit.md" <<'EOF'
# Compression Audit
## §7 Hard Gates
Compression hard gate fires on systematic narrative summary.
Must-Fix floor: any §7 hard gate triggers audit-internal Must-Fix.
EOF
# Negative case: an audit at Auto-recommend before synthesis tier
# whose reference file documents only Recommend/Note-class output
# (no hard gates, no Must-Fix floor).
cat > "$TMPDIR/neg_pd.md" <<'EOF'
## §4a. Router-triggered audits
| Trigger | Audit | Tier | Reference |
|---|---|---|---|
| Some trigger | Soft Audit | Auto-recommend before synthesis | `audits/soft-audit.md` |
EOF
cat > "$TMPDIR/audits/soft-audit.md" <<'EOF'
# Soft Audit
## Output
Produces only Note-class observations. Surfaces patterns for editorial review. Severity outputs: Recommend / Note / Suggestion.
EOF
# Override case: same as neg_pd but with override marker present.
cat > "$TMPDIR/over_pd.md" <<'EOF'
## §4a. Router-triggered audits
| Trigger | Audit | Tier | Reference |
|---|---|---|---|
| Some trigger | Soft Audit | Auto-recommend before synthesis | `audits/soft-audit.md` |
<!-- override: audit-tier-criterion-soft-audit — Promoted on cross-fixture material findings (criterion 2); criterion 1 deliberately waived per Phase 7 Wave 2 plan. -->
EOF
# Edge case: audit at Recommend tier (low tier — no criterion check
# applies). Should pass regardless of reference-file content.
cat > "$TMPDIR/edge_pd.md" <<'EOF'
## §4b. Finding-triggered audits
| Layer | Trigger | Audit | Tier |
|---|---|---|---|
| 9 (Thematic Coherence) | Some pattern | Some Recommend Audit | Recommend |
EOF
# Auto-run definitional case (v1.8.4 canonical-failure analogue):
# mirrors the Memoir / Narrative-NF pattern surfaced when running
# against canonical pass-dependencies.md. An Auto-run (definitional)
# audit at high tier whose reference file documents named gates as
# a §Hard Gates section header form (header line "## Hard Gates" or
# bold-paragraph form) plus per-flag (Hard Gate) parenthetical
# markers — the form Memoir / Series Continuity / Narrative NF /
# Consent Complexity / AI-Prose now use. Should PASS.
cat > "$TMPDIR/autorun_pd.md" <<'EOF'
## §4a. Router-triggered audits
| Trigger | Audit | Tier | Reference |
|---|---|---|---|
| Memoir-shape disclosed at intake | Definitional Memoir Audit | Auto-run (bundled) | `audits/definitional-memoir.md` |
EOF
cat > "$TMPDIR/audits/definitional-memoir.md" <<'EOF'
# Definitional Memoir Audit
## Diagnostic Flags
### Must-Fix Floor — Hard Gates
The two flags below are audit-internal hard gates carrying an audit-internal Must-Fix floor that propagates to synthesis.
**"Memory Fraud"** (Hard Gate) — invented scenes presented as factual.
**"Living Person Harm"** (Hard Gate) — identifiable person damaged without consent.
EOF
# Finding-triggered §4b high-tier case (v1.8.4): mirrors the
# canonical Consent Complexity / AI-Prose / Series Continuity §4b
# rows where the audit appears in §4b at Auto-recommend before
# synthesis tier. Validator must extract audit name from §4b's
# different column ordering (| Pass | Trigger | Audit | Policy |
# vs. §4a's | Trigger | Audit | Tier | Reference |). Note: the §4b
# column-3 cell holds the audit name and the canonical reference
# path lives in the §4a row for the same audit. Self-test asserts
# the validator does not error when §4b rows lack a backtick
# reference path (which is the canonical pattern).
cat > "$TMPDIR/findingtrig_pd.md" <<'EOF'
## §4b. Finding-triggered audits
| Pass | Finding pattern | Audit(s) | Policy |
|------|----------------|----------|--------|
| 1 (Reader Experience) | Uniform fluency | AI-Prose Calibration | Auto-recommend before synthesis (if not already loaded) |
EOF
# Note: §4b row's column 5 is empty (no backtick reference path
# cell). Validator's REF_PATH extraction returns empty; the row
# is correctly skipped without erroring. This is canonical
# behavior — §4a is the source of truth for reference paths.
RESULTS=0
"$0" audit-tier-criterion "$TMPDIR/pos_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && echo " pos: OK (high-tier audits document hard gates / Must-Fix floors)" || { echo " pos: FAIL (expected OK)"; RESULTS=1; }
"$0" audit-tier-criterion "$TMPDIR/neg_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && { echo " neg: FAIL (expected ERROR — high-tier audit lacks criterion-1 hard-gate language)"; RESULTS=1; } || echo " neg: OK (caught — soft audit at Auto-recommend before synthesis tier)"
"$0" audit-tier-criterion "$TMPDIR/over_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && echo " over: OK (override marker downgrades ERROR→WARN)" || { echo " over: FAIL (expected OK after override)"; RESULTS=1; }
"$0" audit-tier-criterion "$TMPDIR/edge_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && echo " edge: OK (Recommend-tier audit not subject to criterion-1 check)" || { echo " edge: FAIL (expected OK — Recommend tier exempt)"; RESULTS=1; }
"$0" audit-tier-criterion "$TMPDIR/autorun_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && echo " autorun: OK (v1.8.4: Auto-run definitional audit with §Hard Gates section header + per-flag (Hard Gate) markers)" || { echo " autorun: FAIL (expected OK — Auto-run definitional with hard-gate section-header form)"; RESULTS=1; }
"$0" audit-tier-criterion "$TMPDIR/findingtrig_pd.md" "$TMPDIR/audits" >/dev/null 2>&1 && echo " findingtrig: OK (v1.8.4: §4b finding-triggered row without ref-path cell skipped without error)" || { echo " findingtrig: FAIL (expected OK — §4b without ref path should not error)"; RESULTS=1; }
[ "$RESULTS" -eq 0 ] && { echo "Self-test: PASS"; exit 0; } || { echo "Self-test: FAIL"; exit 1; }
fi
# Real-file invocation: delegate to the parser when python3 is present.
if command -v python3 >/dev/null 2>&1 && [ -f "$CFG_HELPER" ]; then
python3 "$CFG_HELPER" audit-tier-criterion "$@"; exit $?
fi
# Degraded path (no python3): bash regex implementation.
if [ ! -f "$1" ]; then echo "Error: File not found: $1" >&2; exit 2; fi
PD_FILE="$1"
AUDIT_ROOT="${2:-}"
# Default audit root: try the conventional layout if not provided.
if [ -z "$AUDIT_ROOT" ]; then
PD_DIR=$(dirname "$PD_FILE")
# Common layout: pass-dependencies.md is in core-editor/references;
# audits live across sibling skill reference directories.
if [ -d "$PD_DIR/../.." ]; then
AUDIT_ROOT="$PD_DIR/../.."
else
AUDIT_ROOT="$PD_DIR"
fi
fi
ERRORS=0
WARNS=0
# Tiers that require the criterion-1 (hard-gate / Must-Fix floor)
# check. Recommend / Auto-recommend tiers are exempt.
HIGH_TIER_PATTERN="(Hard Prerequisite|Pre-DE Prerequisite|Auto-run|Auto-recommend before synthesis)"
# Extract pipe-table rows that mention a high-tier assignment.
# Each row format (in §4a / §4b): | <trigger> | <audit name> | <tier> | <reference> |
# We scan all pipe rows and try to extract audit name + tier + reference.
HIGH_TIER_ROWS=$(grep -E "^\|" "$PD_FILE" 2>/dev/null | grep -E "$HIGH_TIER_PATTERN" || true)
if [ -z "$HIGH_TIER_ROWS" ]; then
echo "OK: No high-tier audit assignments detected in pipe-table rows of ${PD_FILE}."
exit 0
fi
# For each high-tier row, extract audit name and reference path.
while IFS= read -r row; do
[ -z "$row" ] && continue
# Parse pipe-separated cells; trim surrounding whitespace.
AUDIT_NAME=$(echo "$row" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $3); print $3}')
REF_CELL=$(echo "$row" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $5); print $5}')
# Reference path is in backticks like `craft/foo.md` or `audits/foo.md`.
REF_PATH=$(echo "$REF_CELL" | grep -oE '`[^`]+\.md`' | head -1 | tr -d '`' || true)
[ -z "$AUDIT_NAME" ] && continue
[ -z "$REF_PATH" ] && continue
# Compute slug for override marker matching: lowercase audit name,
# spaces and slashes to hyphens, strip non-alphanumerics.
AUDIT_SLUG=$(echo "$AUDIT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//; s/-$//')
# Per-audit override check: marker in the body of pass-dependencies.
OV_AUDIT=0
if _has_override "audit-tier-criterion-${AUDIT_SLUG}" < "$PD_FILE"; then
OV_AUDIT=1
fi
# Locate the reference file. Try AUDIT_ROOT/REF_PATH; fall back to
# walking AUDIT_ROOT for any file matching the basename.
REF_FILE=""
if [ -f "$AUDIT_ROOT/$REF_PATH" ]; then
REF_FILE="$AUDIT_ROOT/$REF_PATH"
else
BASENAME=$(basename "$REF_PATH")
FOUND=$(find "$AUDIT_ROOT" -name "$BASENAME" -type f 2>/dev/null | head -1 || true)
[ -n "$FOUND" ] && REF_FILE="$FOUND"
fi
if [ -z "$REF_FILE" ]; then
echo "WARN: '${AUDIT_NAME}' — reference file '${REF_PATH}' not found under '${AUDIT_ROOT}'; cannot verify criterion 1."
WARNS=$((WARNS + 1))
continue
fi
# Criterion 1: reference file must mention hard gates OR Must-Fix
# floor language. Pattern: "hard gate", "Hard Gate", "Must-Fix
# floor", "Must-Fix-floor".
if grep -iE "(hard[ -]?gate|must-?fix[ -]?floor)" "$REF_FILE" > /dev/null 2>&1; then
: # criterion-1 satisfied
else
if [ "$OV_AUDIT" -eq 1 ]; then
echo "WARN: '${AUDIT_NAME}' — reference file '${REF_PATH}' does not document hard gates / Must-Fix floor (criterion 1 unmet); audit-tier-criterion-${AUDIT_SLUG} override marker present."
WARNS=$((WARNS + 1))
else
echo "ERROR: '${AUDIT_NAME}' — reference file '${REF_PATH}' does not document hard gates / Must-Fix floor (criterion 1 unmet for high-tier assignment). Add hard-gate / Must-Fix-floor language to the audit reference, demote the tier, or add <!-- override: audit-tier-criterion-${AUDIT_SLUG} — <rationale> --> in pass-dependencies body."
ERRORS=$((ERRORS + 1))
fi
fi
done <<< "$HIGH_TIER_ROWS"
if [ "$ERRORS" -gt 0 ]; then
echo ""
echo "FAILED: ${ERRORS} audit-tier-criterion failure(s); ${WARNS} warning(s). Capability ceiling: criterion 1 (hard gates / Must-Fix floor) is mechanically verified; criteria 2 (undetectable-by-passes) and 3 (disclosure-non-equivalence) require model judgment and remain in the §4a/§4b verification subsection prose. Canonical home: core-editor/references/pass-dependencies.md §4c Audit Tier Promotion Criteria."
exit 1
else
echo "OK: All high-tier audit assignments satisfy criterion 1 (named hard gates / Must-Fix floor in reference file) or carry override markers. ${WARNS} warning(s) surfaced. Capability ceiling: criteria 2 + 3 remain prose-verified."
exit 0
fi
;;
# ----------------------------------------------------------------------
# pass-header <pass_artifact_file> [<pass_dependencies_file>]
#
# A Core DE pass artifact must carry a §3-sourced header:
# > **Macro block:** <block> · **Writer question:** <question>
# · **Legacy pass id:** Pass <N>
# The three values are READ from pass-dependencies.md §3 (the single source
# of truth for the 8 blocks, the pass↔block map, and the User Question),
# never authored. H1 header present (header-less legacy artifact = WARN, not
# ERROR); H2 block ∈ 8 AND block matches §3's pass→block map for the Legacy
# pass id AND Writer question matches §3's User Question; H3 all three fields
# non-empty. Concern-driven runs declare the pass's OWN canonical §3 block
# (the map is by pass, not by run).
#
# Primary path: scripts/config_checks.py. Degrades to bash when python3 is absent.
# ----------------------------------------------------------------------
pass-header)
# Delegates to scripts/config_checks.py (the SSoT parser for §3 — block ∈ the 8,
# block↔pass map, block→User Question). Per the fleet NEW-VALIDATOR convention,
# it degrades to an advisory WARN without python3 — NOT a bash reimplementation
# of the §3 parse (that duplicate drifts from the parser and, under set -euo
# pipefail, mis-fires a hard failure on the header-absent WARN path). A header
# comparison against §3 needs the real parser; there is no faithful bash stand-in.
PH_DIR=$(cd "$(dirname "$0")" && pwd)
PH_HELPER="$PH_DIR/config_checks.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$PH_HELPER" ]; then python3 "$PH_HELPER" --self-test pass-header; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; pass-header is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$PH_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 pass-header <pass_artifact_file> [<pass_dependencies_file>] | --self-test"; exit 2; fi
python3 "$PH_HELPER" pass-header "$@"; exit $?
fi
echo "WARN: python3 unavailable — pass-header skipped; check inline that the header's block/question/pass id agree with pass-dependencies.md §3 (block ∈ the 8, the block↔pass map is by pass not by run, and the Writer question is §3's User Question — never authored). See references/example-pass-artifact-header.md."
exit 0
;;
# ----------------------------------------------------------------------
# argument-recon-prerequisite <run_folder> [<editorial_letter_file>]
#
# Mechanical check that argument-shaped runs satisfy the Field
# Reconnaissance prerequisite per pass-dependencies.md §4a (Hard
# Prerequisite or Auto-recommend before synthesis tier) and v1.7.9
# Hard Prerequisite tier wiring.
#
# Behavior: scan the run folder for argument-engine artifacts
# (Argument_State.md, Red_Team_Memo.md, Argument_Evidence.md, or
# editorial-letter mentions of Dialectical Clarity / Argument Red
# Team / Argument Evidence Deep-Dive / argument-engine pass output).
# If argument-engine artifacts are present, verify that EITHER:
# (a) Field_Reconnaissance_Report.md exists in the run folder, OR
# (b) the editorial letter records the canonical blind-spot
# disclosure per run-synthesis.md §Step 3 (Phase 6 Wave 3 /
# CR-4): "literature-counterevidence not surveyed" naming what
# is unsurveyed and what the absence implies for synthesis
# confidence.
#
# If neither (a) nor (b) holds, the validator fails — Hard
# Prerequisite policy forbids silent omission.
#
# Run folders without argument-engine artifacts (fiction runs;
# narrative-NF runs; non-argument-shaped runs) are exempt and the
# validator returns OK.
#
# Override marker: <!-- override: argument-recon-prerequisite —
# <rationale> --> in the editorial letter body (e.g., "argument-
# engine artifacts present pre-date Phase 6 Wave 3 prerequisite
# policy; back-fill blind-spot disclosure scheduled for next
# revision round").
#
# Self-test: pass --self-test as the only argument to run built-in
# cases.
# ----------------------------------------------------------------------
argument-recon-prerequisite)
if [ $# -lt 1 ]; then echo "Usage: $0 argument-recon-prerequisite <run_folder> [<editorial_letter_file>] | --self-test"; exit 2; fi
# Primary path: real parser in scripts/config_checks.py (Inc.5). Degrades to bash below.
CFG_DIR=$(cd "$(dirname "$0")" && pwd)
CFG_HELPER="$CFG_DIR/config_checks.py"
if [ "$1" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CFG_HELPER" ]; then python3 "$CFG_HELPER" --self-test argument-recon-prerequisite; exit $?; fi
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# Positive case 1: argument-engine artifacts present + Field
# Reconnaissance report present. Should pass.
mkdir -p "$TMPDIR/run_pos1"
touch "$TMPDIR/run_pos1/Argument_State.md"
touch "$TMPDIR/run_pos1/Field_Reconnaissance_Report.md"
cat > "$TMPDIR/run_pos1/Editorial_Letter.md" <<'EOF'
# Editorial Letter
## §1 What Needs Work
Must-Fix: warrant gap on §3 claim.
EOF
# Positive case 2: argument-engine artifacts present + no Field
# Recon, but editorial letter records canonical blind-spot
# disclosure. Should pass.
mkdir -p "$TMPDIR/run_pos2"
touch "$TMPDIR/run_pos2/Red_Team_Memo.md"
cat > "$TMPDIR/run_pos2/Editorial_Letter.md" <<'EOF'
# Editorial Letter
## §3 Blind Spot / Absence Inventory
Field Reconnaissance was declined at intake. The synthesis layer records "literature-counterevidence not surveyed" as a confidence-limiting blind spot: competing studies, counter-citations, replication failures, and opposing scholarly positions in the literature were not surfaced. Dialectical Clarity, Argument Red Team, and Argument Evidence Deep-Dive operated against a manuscript-internal claim graph rather than a literature-aware one.
EOF
# Positive case 3: no argument-engine artifacts (fiction run).
# Should pass — validator exempt.
mkdir -p "$TMPDIR/run_pos3"
cat > "$TMPDIR/run_pos3/Editorial_Letter.md" <<'EOF'
# Editorial Letter
## §1 What Needs Work
Must-Fix: pacing collapse in Chapter 7.
EOF
# Negative case: argument-engine artifacts present + no Field
# Recon report + no blind-spot disclosure in editorial letter.
# Should fail.
mkdir -p "$TMPDIR/run_neg"
touch "$TMPDIR/run_neg/Argument_State.md"
touch "$TMPDIR/run_neg/Red_Team_Memo.md"
cat > "$TMPDIR/run_neg/Editorial_Letter.md" <<'EOF'
# Editorial Letter
## §1 What Needs Work
Must-Fix: warrant gap on §3 claim.
## §3 Absence Inventory
The pass artifacts are complete; no missing structural elements identified.
EOF
# Override case: same setup as neg, but with override marker in
# editorial letter body. Should pass with WARN.
mkdir -p "$TMPDIR/run_over"
touch "$TMPDIR/run_over/Argument_State.md"
cat > "$TMPDIR/run_over/Editorial_Letter.md" <<'EOF'
# Editorial Letter
## §1 What Needs Work
Must-Fix: warrant gap on §3 claim.
<!-- override: argument-recon-prerequisite — Argument-engine artifacts present pre-date Phase 6 Wave 3 prerequisite policy; back-fill blind-spot disclosure scheduled for next revision round. -->
EOF
RESULTS=0
"$0" argument-recon-prerequisite "$TMPDIR/run_pos1" >/dev/null 2>&1 && echo " pos1: OK (argument-engine + Field Recon report)" || { echo " pos1: FAIL (expected OK)"; RESULTS=1; }
"$0" argument-recon-prerequisite "$TMPDIR/run_pos2" >/dev/null 2>&1 && echo " pos2: OK (argument-engine + canonical blind-spot disclosure)" || { echo " pos2: FAIL (expected OK)"; RESULTS=1; }
"$0" argument-recon-prerequisite "$TMPDIR/run_pos3" >/dev/null 2>&1 && echo " pos3: OK (fiction run — no argument-engine artifacts; exempt)" || { echo " pos3: FAIL (expected OK)"; RESULTS=1; }
"$0" argument-recon-prerequisite "$TMPDIR/run_neg" >/dev/null 2>&1 && { echo " neg: FAIL (expected ERROR — argument-engine present, no Field Recon, no disclosure)"; RESULTS=1; } || echo " neg: OK (caught — silent omission of Hard Prerequisite)"
"$0" argument-recon-prerequisite "$TMPDIR/run_over" >/dev/null 2>&1 && echo " over: OK (override marker downgrades ERROR→WARN)" || { echo " over: FAIL (expected OK after override)"; RESULTS=1; }
[ "$RESULTS" -eq 0 ] && { echo "Self-test: PASS"; exit 0; } || { echo "Self-test: FAIL"; exit 1; }
fi
# Real-folder invocation: delegate to the parser when python3 is present.
if command -v python3 >/dev/null 2>&1 && [ -f "$CFG_HELPER" ]; then
python3 "$CFG_HELPER" argument-recon-prerequisite "$@"; exit $?
fi
# Degraded path (no python3): bash regex implementation.
if [ ! -d "$1" ]; then echo "Error: Run folder not found: $1" >&2; exit 2; fi
RUN_FOLDER="$1"
LETTER="${2:-}"
# Auto-detect editorial letter if not provided: look for
# *Editorial_Letter*.md or *editorial_letter*.md in run folder.
if [ -z "$LETTER" ]; then
LETTER=$(find "$RUN_FOLDER" -maxdepth 2 -type f \( -iname "*editorial_letter*.md" -o -iname "*_de*.md" \) 2>/dev/null | head -1 || true)
fi
# Detect argument-engine artifacts by filename pattern.
ARG_ARTIFACTS=$(find "$RUN_FOLDER" -maxdepth 3 -type f \( -iname "Argument_State*.md" -o -iname "Red_Team_Memo*.md" -o -iname "Argument_Evidence*.md" -o -iname "Argument_Red_Team*.md" -o -iname "Argument_Persuasion*.md" -o -iname "Adversarial_Evidence*.md" \) 2>/dev/null | head -5 || true)
# Also check editorial letter body for argument-engine pass mentions.
ARG_LETTER_MENTION=0
if [ -n "$LETTER" ] && [ -f "$LETTER" ]; then
if grep -iE "(Dialectical Clarity|Argument Red Team|Argument Evidence Deep-Dive|argument-engine|Argument_State|Claim Ladder)" "$LETTER" > /dev/null 2>&1; then
ARG_LETTER_MENTION=1
fi
fi
if [ -z "$ARG_ARTIFACTS" ] && [ "$ARG_LETTER_MENTION" -eq 0 ]; then
echo "OK: No argument-engine artifacts detected in '${RUN_FOLDER}'; Field Reconnaissance prerequisite does not apply (non-argument-shaped run)."
exit 0
fi
# Argument-engine present. Check (a): Field Recon report exists.
FIELD_RECON=$(find "$RUN_FOLDER" -maxdepth 3 -type f -iname "Field_Reconnaissance_Report*.md" 2>/dev/null | head -1 || true)
if [ -n "$FIELD_RECON" ]; then
echo "OK: Argument-engine artifacts detected; Field_Reconnaissance_Report.md present at '${FIELD_RECON}'."
exit 0
fi
# Check (b): canonical blind-spot disclosure in editorial letter.
DISCLOSURE_OK=0
if [ -n "$LETTER" ] && [ -f "$LETTER" ]; then
if grep -iE "literature[- ]counterevidence[- ]not[- ]surveyed" "$LETTER" > /dev/null 2>&1; then
DISCLOSURE_OK=1
fi
fi
# Override marker check (in editorial letter body, above appendices).
OV_ARP=0
if [ -n "$LETTER" ] && [ -f "$LETTER" ]; then
APPENDIX_LINE=$(grep -niE "^#{1,4}.*Appendix [A-C]" "$LETTER" 2>/dev/null | head -1 | cut -d: -f1 || true)
if [ -n "$APPENDIX_LINE" ]; then
BODY=$(sed -n "1,$((APPENDIX_LINE - 1))p" "$LETTER")
else
BODY=$(cat "$LETTER")
fi
if echo "$BODY" | _has_override "argument-recon-prerequisite"; then
OV_ARP=1
fi
fi
if [ "$DISCLOSURE_OK" -eq 1 ]; then
echo "OK: Argument-engine artifacts detected; canonical blind-spot disclosure ('literature-counterevidence not surveyed') present in editorial letter."
exit 0
fi
if [ "$OV_ARP" -eq 1 ]; then
echo "WARN: Argument-engine artifacts detected; no Field_Reconnaissance_Report.md and no canonical blind-spot disclosure found, but override marker present in editorial letter body. Phase 6 Wave 3 / CR-4 Hard Prerequisite policy: this run carries documented exception rationale."
exit 0
fi
echo "ERROR: Argument-engine artifacts detected in '${RUN_FOLDER}' (no Field_Reconnaissance_Report.md present), but the editorial letter does not record the canonical blind-spot disclosure ('literature-counterevidence not surveyed'). Per pass-dependencies.md §4a (Hard Prerequisite) + run-synthesis.md §Step 3 (Phase 6 Wave 3 / CR-4): silent omission is forbidden. Either (a) run Field Reconnaissance and produce Field_Reconnaissance_Report.md, (b) record the canonical blind-spot disclosure in the editorial letter naming what is unsurveyed and what the absence implies for synthesis confidence, or (c) place a body override marker <!-- override: argument-recon-prerequisite — <rationale> --> in the editorial letter."
exit 1
;;
structured-findings)
# Phase 3: validate embedded apodictic:* JSON blocks (in ledger/letter .md)
# and the Diagnostic_State.meta.json sidecar. Delegates JSON parsing to
# scripts/structured_findings.py (a real parser). On a host without python3,
# degrade to a presence check so the block — which stays human-readable —
# does not hard-block; full validation needs python3 (present on Cowork).
SF_DIR=$(cd "$(dirname "$0")" && pwd)
SF_HELPER="$SF_DIR/structured_findings.py"
if command -v python3 >/dev/null 2>&1 && [ -f "$SF_HELPER" ]; then
python3 "$SF_HELPER" "$@"
exit $?
fi
# Degraded path (no python3).
if [ "${1:-}" = "--self-test" ]; then
SF_TMP=$(mktemp -d); trap 'rm -rf "$SF_TMP"' EXIT
printf '<!-- apodictic:finding\n{"schema":"apodictic.finding.v1","severity":"Must-Fix"}\n-->\n' > "$SF_TMP/f.md"
if grep -q 'apodictic:finding' "$SF_TMP/f.md" && grep -q '"schema"' "$SF_TMP/f.md"; then
echo "Self-test: PASS (degraded — python3 unavailable; presence check only)"; exit 0
else
echo "Self-test: FAIL"; exit 1
fi
fi
if [ $# -lt 1 ]; then echo "Usage: $0 structured-findings <file> [<file>...] | --self-test"; exit 2; fi
SF_WARN=0
for f in "$@"; do
if [ ! -f "$f" ]; then echo "Error: File not found: $f" >&2; exit 2; fi
if grep -q 'apodictic:' "$f" 2>/dev/null; then SF_WARN=1; fi
done
if [ "$SF_WARN" -eq 1 ]; then
echo "WARN: python3 unavailable — presence check only; full JSON validation skipped (blocks remain human-readable). Install python3 for full structured-findings validation."
else
echo "structured-findings: PASS (degraded presence check; no structured blocks found)"
fi
exit 0
;;
softness-check)
# Phase 4 (Harden Honesty): compare the delivered letter against the
# Triage-locked findings (Deficit Lock). Delegates to honesty_check.py;
# degrades to advisory (WARN, exit 0) without python3 — the Deficit Lock
# prose rule still applies.
HC_DIR=$(cd "$(dirname "$0")" && pwd)
HC_HELPER="$HC_DIR/honesty_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$HC_HELPER" ]; then python3 "$HC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; softness-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$HC_HELPER" ]; then
if [ $# -lt 2 ]; then echo "Usage: $0 softness-check <editorial_letter> <findings_ledger> | --self-test"; exit 2; fi
python3 "$HC_HELPER" softness-check "$@"
exit $?
fi
echo "WARN: python3 unavailable — softness-check (delivered-vs-locked severity) skipped; the Deficit Lock prose rule still applies. Install python3 for the mechanical gate."
exit 0
;;
deficit-lock)
# Phase 4: verify the Triage Deficit Lock was recorded structurally in the
# ledger. Delegates to honesty_check.py; degrades to advisory without python3.
HC_DIR=$(cd "$(dirname "$0")" && pwd)
HC_HELPER="$HC_DIR/honesty_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$HC_HELPER" ]; then python3 "$HC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$HC_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 deficit-lock <findings_ledger> | --self-test"; exit 2; fi
python3 "$HC_HELPER" deficit-lock "$@"
exit $?
fi
echo "WARN: python3 unavailable — deficit-lock (structured-lock presence) skipped."
exit 0
;;
artifacts-schema)
# Shared structured-artifact parser/validator (scripts/apodictic_artifacts.py) —
# the source-of-truth schema engine behind structured-findings / softness-check /
# deficit-lock. Only --self-test is meaningful here; it gates the shared module
# in --self-test-all. Delegates to the module; degrades without python3.
AS_DIR=$(cd "$(dirname "$0")" && pwd)
AS_HELPER="$AS_DIR/apodictic_artifacts.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AS_HELPER" ]; then python3 "$AS_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable)"; exit 0
fi
echo "Usage: $0 artifacts-schema --self-test"; exit 2
;;
gate)
# Runner-Governed Execution (increments 1-5): run the execution-gate engine for a
# phase against a run folder — checks the manifest's required artifacts + mechanical
# validators, prints the attested checklist, and records the decision as an append-only
# event in execution.gate_events[]. Subcommands (passed through to run_gate.py):
# gate <phase> <run_folder> [--strict-warnings] mechanical run (-> mechanical-passed
# for a gate with attested items)
# gate --attest <phase> <run_folder> re-run checks + record clearing pass
# gate --skip/--defer <phase> <run_folder> --reason ... record an exception
# Degrades without python3 (model performs the manifest's checks inline and hand-authors
# the gate_events[] entry — see docs/runner-governed-execution.md §Degradation).
GT_DIR=$(cd "$(dirname "$0")" && pwd)
GT_HELPER="$GT_DIR/run_gate.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$GT_HELPER" ]; then python3 "$GT_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$GT_HELPER" ]; then
if [ $# -lt 2 ]; then echo "Usage: $0 gate <phase> <run_folder> [--strict-warnings] | gate --attest <phase> <run_folder> | gate --skip/--defer <phase> <run_folder> --reason ..."; exit 2; fi
python3 "$GT_HELPER" "$@"
exit $?
fi
echo "WARN: python3 unavailable — gate engine skipped; perform the phase's manifest checks inline and append the result as an event in the sidecar (execution.gate_events)."
exit 0
;;
gate-state)
# Runner-Governed Execution (increment 5): gate-state validator — validate a sidecar's
# execution.gate_events[] log (structural + the semantic invariants the stdlib subset
# checker cannot express: attestation coverage, migration-prefix integrity, finding_deltas
# clearing-only, pointer==fold) and assert pointer==fold. --strict is nonzero while any
# open exception (non-clearing latest event) remains. Delegates to scripts/run_gate.py
# --check-state; degrades to advisory without python3.
GS_DIR=$(cd "$(dirname "$0")" && pwd)
GS_HELPER="$GS_DIR/run_gate.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$GS_HELPER" ]; then python3 "$GS_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; gate-state is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$GS_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 gate-state <Diagnostic_State.meta.json> [--strict]"; exit 2; fi
python3 "$GS_HELPER" --check-state "$@"
exit $?
fi
echo "WARN: python3 unavailable — gate-state skipped; the gate_events[] contract is documented in docs/runner-governed-execution.md. Install python3 for the mechanical check."
exit 0
;;
finding-trace)
# Finding Lifecycle IDs cross-artifact trace (docs/finding-lifecycle-ids.md): referential
# integrity + sidecar lifecycle coherence by Finding Lifecycle ID — E1 dangling letter
# reference, E2 phantom sidecar finding_states key, E3 invalid state, E4 dangling revision
# reference, E5 phantom completion (an in-scope report mentions a `revised` finding but carries
# no `<!-- resolved: ID -->` marker for it), E6 dangling retcon source (a Retcon Plan retcon_item
# `source` finding-ref that is not in the ledger — Retcon Planning F3); W1 lifecycle coverage,
# W2 revision-plan follow-through, W3 completion follow-through (all advisory; ERROR under --strict).
# Completion keys on the explicit resolved marker, not a bare mention. Complements softness-check
# (severity fidelity) and structured-findings (intra-ledger ID hygiene) — raises only classes neither owns.
# Takes a run folder (globs ledger/letter/revisions/retcon-plans, walks up for the sidecar) or explicit files.
# Delegates to scripts/finding_trace.py; degrades to an advisory WARN without python3.
FT_DIR=$(cd "$(dirname "$0")" && pwd)
FT_HELPER="$FT_DIR/finding_trace.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$FT_HELPER" ]; then python3 "$FT_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; finding-trace is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$FT_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 finding-trace <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$FT_HELPER" finding-trace "$@"; exit $?
fi
echo "WARN: python3 unavailable — finding-trace skipped; perform the cross-artifact ID trace inline (every cited F-... ID resolves to a ledger finding; finding_states keys are ledger IDs). See docs/finding-lifecycle-ids.md."
exit 0
;;
feedback-triage)
# Feedback Triage workflow integrity (docs/feedback-triage.md): structural checks over the
# apodictic.feedback_item.v1 blocks in a Feedback Triage artifact — E1 invalid item, E2
# duplicate id, E3 dangling conflict reference, E4 self conflict, E5 dangling maps_to (a
# finding-id ref not in the paired ledger; Increment 2), W1 unresolved conflict (both sides
# still actionable), W2 acting now on an unvalidated claim, W4 unmapped validated (a `validated`
# item carrying no maps_to; Increment 2). W1/W2/W4 advisory; ERROR under --strict. Owns conflict
# referential integrity, the "contradiction kept live" coherence gap, and feedback->ledger
# maps_to referential integrity. Takes a run folder (globs *_Feedback_Triage_*.md plus an
# optional *_Findings_Ledger_*.md for the maps_to cross-check) or explicit files (triage +
# optional ledger); without a ledger the maps_to check is skipped. Delegates to
# scripts/feedback_triage.py; degrades to an advisory WARN without python3.
FBT_DIR=$(cd "$(dirname "$0")" && pwd)
FBT_HELPER="$FBT_DIR/feedback_triage.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$FBT_HELPER" ]; then python3 "$FBT_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; feedback-triage is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$FBT_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 feedback-triage <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$FBT_HELPER" feedback-triage "$@"; exit $?
fi
echo "WARN: python3 unavailable — feedback-triage skipped; check inline that every conflicts_with id resolves to a real feedback item and no contradiction is left actionable on both sides. See docs/feedback-triage.md."
exit 0
;;
retcon-plan)
# Retcon Planning coaching track (docs/retcon-planning.md): structural checks over the
# apodictic.retcon_item.v1 blocks in a Retcon Plan — R1 invalid item, R2 duplicate id,
# R3 evidential retcon of locked canon (fair-play violation; the signature gate), R4 dangling
# target_id; W1 unaccounted blast radius on a locked/costly item, W2 firewall drift (invented
# prose where a class belongs). The Door-B Selection step (F1) also checks apodictic.retcon_reading.v1
# blocks — R5 invalid reading (schema + 1-5 score rubric), R6 duplicate reading id, R7 dangling
# implied_target; W3 missing coincidence_note (over-fitting guard; the signature F1 check), W4
# more than 3 candidate readings (top-1-3 shortlist). W1-W4 advisory, ERROR under --strict. Takes
# a run folder (globs *_Retcon_Plan_*.md) or explicit files. Delegates to scripts/retcon_plan.py;
# degrades to an advisory WARN without python3.
RCP_DIR=$(cd "$(dirname "$0")" && pwd)
RCP_HELPER="$RCP_DIR/retcon_plan.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RCP_HELPER" ]; then python3 "$RCP_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; retcon-plan is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RCP_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 retcon-plan <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$RCP_HELPER" retcon-plan "$@"; exit $?
fi
echo "WARN: python3 unavailable — retcon-plan skipped; check inline that no evidential retcon touches locked canon, every target_id/implied_target is declared, intervention classes aren't invented prose, and each candidate reading is scored 1-5 with a coincidence_note. See docs/retcon-planning.md."
exit 0
;;
state-card-diff)
# Retcon Planning State Card cross-revision diff (docs/retcon-planning.md, F2): the State Card
# promoted to a standalone rolling artifact (apodictic.state_card.v1), diff'd across revision
# rounds (Pass-10-class pattern, modeled on timeline-diff). One file = single-card validate
# (S1 invalid card / id-prefix, S2 duplicate SE-NN id). Two files = <prior> <current> cross-round
# diff adding S3 round-backwards, S4 promise->contradiction (the signature transition; override
# <!-- override: state-card-transition SE-NN — … -->), W1 dropped promise, W2 controlling-idea
# shift, W3 same-round edit. W1-W3 advisory, ERROR under --strict. Delegates to
# scripts/state_card_diff.py; degrades to an advisory WARN without python3.
SCD_DIR=$(cd "$(dirname "$0")" && pwd)
SCD_HELPER="$SCD_DIR/state_card_diff.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SCD_HELPER" ]; then python3 "$SCD_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; state-card-diff is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SCD_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 state-card-diff <current> | <prior> <current> [--strict] | --self-test"; exit 2; fi
python3 "$SCD_HELPER" state-card-diff "$@"; exit $?
fi
echo "WARN: python3 unavailable — state-card-diff skipped; check inline that every tracked element carries a unique SE-NN id, no active promise has become a forbidden contradiction across rounds, and a shifted controlling idea is intentional. See docs/retcon-planning.md."
exit 0
;;
revision-arc)
# Multi-Session Revision Arc Planning coaching track (docs/multi-session-arc-planning.md): structural
# checks over the single apodictic.revision_arc.v1 block in a Revision Arc artifact — the phased
# multi-week strategy (Phase 1 root causes -> Phase 2 consequences -> Phase 3 polish) that sequences
# the Findings Ledger into per-session Loop Dispatch. HONEST POSTURE (the Retcon pattern: the coach
# infers, the validator gates the PLAN): the Root-Cause mapping is NOT machine-readable, so this gates
# the arc's self-consistency + provenance + firewall ONLY — NOT a true causal graph; the coach's
# dependency reasoning is TRUSTED, not gated. A1 invalid arc (schema + nested phase shape: no empty
# phases, finding_ref pattern, root_cause subset), A2 provenance closure (every finding_ref resolves to
# a real Ledger finding), A3 SELF-CONSISTENCY ONLY (each finding in exactly one phase; a Must-Fix
# finding the arc labels a structural root cause is not parked in the last/polish phase — NOT a causal-
# structure check), A4 non-empty phase rationale; W1 firewall drift (a rationale that prescribes
# execution; reuses the retcon-plan heuristics — advisory/best-effort), W2 orphan (a Must-Fix Ledger
# finding absent from the arc). W1-W2 advisory, ERROR under --strict. Takes a run folder (globs
# *_Revision_Arc_*.md + the paired *_Findings_Ledger_*.md) or explicit files (arc [ledger]). Delegates
# to scripts/revision_arc.py; degrades to an advisory WARN without python3.
RVA_DIR=$(cd "$(dirname "$0")" && pwd)
RVA_HELPER="$RVA_DIR/revision_arc.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RVA_HELPER" ]; then python3 "$RVA_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; revision-arc is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RVA_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 revision-arc <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$RVA_HELPER" revision-arc "$@"; exit $?
fi
echo "WARN: python3 unavailable — revision-arc skipped; check inline that every finding_ref resolves to a ledger finding, each finding sits in exactly one phase, no Must-Fix structural root cause is parked in the polish phase, every phase has a sequencing (not execution-prescribing) rationale, and no Must-Fix finding is left out of the arc. See docs/multi-session-arc-planning.md."
exit 0
;;
regression-diff)
# Draft-over-Draft Structural Regression Testing (docs/draft-regression-testing.md): the cross-round
# Findings-Ledger diff — did this revision resolve what it claimed, and did it break anything that was
# working? Finding IDs are per-run (renumbered each round), so cross-round identity is a DETERMINISTIC
# heuristic match (same origin code + equal chapter token + >=1 shared mechanism token; greedy stable
# one-to-one) and every regression signal is a CANDIDATE for editor judgment. R1 round-linkage (ERROR:
# both ledgers parse, non-empty, distinct rounds); W1 recurrence-candidate (a resolved/'revised' prior
# finding matched in round N), W2 new-in-quiet-chapter (a current finding in a chapter quiet on the prior
# record; override <!-- override: regression-cleared <runlabel>:<chapter> — … -->), W3 unexplained-drop.
# W1-W3 advisory, ERROR under --strict. Prints to stdout (the diff-validator precedent — persists no
# file); the Regression Report is orchestrator-written at round-close. Delegates to
# scripts/regression_diff.py; degrades to an advisory WARN without python3.
RGD_DIR=$(cd "$(dirname "$0")" && pwd)
RGD_HELPER="$RGD_DIR/regression_diff.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RGD_HELPER" ]; then python3 "$RGD_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; regression-diff is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RGD_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 regression-diff <prior_run_folder> <this_run_folder> [--strict] | <run_folder> | --self-test"; exit 2; fi
python3 "$RGD_HELPER" regression-diff "$@"; exit $?
fi
echo "WARN: python3 unavailable — regression-diff skipped; check inline whether any finding the prior round marked resolved recurs (same origin/chapter/mechanism), and whether a chapter quiet on the prior record now carries findings. See docs/draft-regression-testing.md."
exit 0
;;
reanchor)
# Annotated-Manuscript round-trip re-anchoring (docs/annotated-manuscript-reanchoring.md): carry
# draft N's margin annotations onto a REVISED draft (N+1). Re-resolves each anchor against N+1's
# snapshot by PURE TEXT SEARCH (the A6 identity reused; the quote offset is recomputed against N+1,
# never carried forward) and classifies held / moved / vanished / ambiguous / not-re-anchorable.
# RA1 re-anchor integrity (ERROR: the re-anchored manifest passes the structural A-gate — A1+A2+A3+
# A4-multiset+A6 — against N+1; ledger arms inert, there is no re-diagnosed N+1 ledger); RA2 comment
# fidelity (ERROR: comments carried byte-identical, never re-authored); RA3 partition completeness
# (ERROR: every draft-N annotation in exactly one class). W1 candidate-resolved (a vanished anchor),
# W2 re-anchor refused (ambiguous / line-range) — advisory, ERROR under --strict. Prints to stdout
# (the diff-validator precedent). Delegates to scripts/reanchor.py; degrades to advisory WARN
# without python3.
RAN_DIR=$(cd "$(dirname "$0")" && pwd)
RAN_HELPER="$RAN_DIR/reanchor.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RAN_HELPER" ]; then python3 "$RAN_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; reanchor is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RAN_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 reanchor <prior_run_folder> <new_snapshot> [--strict] | --self-test"; exit 2; fi
python3 "$RAN_HELPER" reanchor "$@"; exit $?
fi
echo "WARN: python3 unavailable — reanchor skipped; check inline that each draft-N margin note's anchored text still occurs verbatim+unique in the revised draft (held/moved), is gone (candidate-resolved), or is duplicated/line-range (refused). See docs/annotated-manuscript-reanchoring.md."
exit 0
;;
roundtrip-disposition)
# Round-trip disposition record gate (the `/start` round-trip resume round-close;
# docs/annotated-manuscript-reanchoring.md Increment 3): validate the operator disposition record
# `[Project]_Roundtrip_Disposition_[runlabel].md` in <this_run_folder> against a LIVE recompute of
# the anchor classes (reanchor's RA3 partition) and the crossref classes (regression_diff) — the
# model proposes, the operator disposes, and this gate proves the record never asserts evidence
# the recompute doesn't support. RT1 recompute alignment (ERROR: every row's finding_id is in the
# prior manifest's partition, its anchor=/regression= classes equal the recomputed ones, the
# `compares:` header names the actual runlabels); RT2 confirmation record present (ERROR: any
# decided row requires the file-level disposition-confirmed token — this proves the RECORD of
# confirmation exists and is consistent, never that a human confirmed; the human layer is rev-a4
# at `gate --attest`); RT3 confirmed-writes-only (ERROR: a resolved marker in the folder's
# Revision Report(s) for an annotated finding without a decision=confirm-resolved row is the
# vanished-anchor auto-close this gate exists to prevent). RT4 partition coverage (WARN; ERROR
# --strict: every finding id in the recomputed partition has a disposition row — a record that
# exists is a round-close record, so a missing row is a finding silently omitted from round-close
# review, reported by id). W1 unadjudicated/staged — advisory,
# ERROR under --strict. Absent record -> PASS no-op (safe anywhere). Delegates to
# scripts/reanchor.py disposition; degrades to advisory WARN without python3 (coherent: no
# disposition record can have been produced on such a host).
RTD_DIR=$(cd "$(dirname "$0")" && pwd)
RTD_HELPER="$RTD_DIR/reanchor.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RTD_HELPER" ]; then python3 "$RTD_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; roundtrip-disposition is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RTD_HELPER" ]; then
if [ $# -lt 3 ]; then echo "Usage: $0 roundtrip-disposition <prior_run_folder> <new_snapshot> <this_run_folder> [--strict] | --self-test"; exit 2; fi
python3 "$RTD_HELPER" disposition "$@"; exit $?
fi
echo "WARN: python3 unavailable — roundtrip-disposition skipped; check inline that every disposition row's classes match a fresh reanchor/crossref run, that decided rows carry the disposition-confirmed token, that every resolved marker in the Revision Report has a confirm-resolved disposition row, and that every prior annotated finding has a disposition row (none omitted). See docs/annotated-manuscript-reanchoring.md."
exit 0
;;
disposition-check)
# Engine-level finding-disposition integrity (docs/finding-dispositions.md): the declined/
# deferred OVERLAY on the finding lifecycle — execution.finding_dispositions records mirroring
# the pinned `<!-- declined: F-… — reason -->` / `<!-- deferred: F-… until: trigger — reason -->`
# markers (grammar SSoT: apodictic_artifacts.parse_disposition_markers). DP0 record shape
# (schema + trigger-iff-deferred + non-empty reason); DP1 declined/deferred-Must-Fix caveat —
# an active set-aside Must-Fix must be NAMED on the readiness assessment's pinned caveat
# line(s) or the verdict is absorbing it (the /ready workflow runs this BEFORE delivering a
# verdict; a DP1 ERROR blocks it); DP2 no-laundering — same-run resolved+declined
# contradiction, phantom map key, ledger<->calibration severity mismatch, triage_summary
# decrement below the ledger tally, and the bidirectional marker/sidecar sync (WARN; ERROR
# under --strict; governed marker-lag exempt). Artifact-side audit; the log-side twin for
# governed projects lives in gate-state (run_gate.py --check-state disposition_deltas checks).
# Takes a run folder, the project sidecar (+ optional readiness assessment), or explicit
# files. Delegates to scripts/disposition_check.py; degrades to an advisory WARN without
# python3 (the finding-trace posture — never a false failure).
DPC_DIR=$(cd "$(dirname "$0")" && pwd)
DPC_HELPER="$DPC_DIR/disposition_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$DPC_HELPER" ]; then python3 "$DPC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; disposition-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$DPC_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 disposition-check <run_folder|sidecar> [readiness_assessment] [--strict] | --self-test"; exit 2; fi
python3 "$DPC_HELPER" disposition-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — disposition-check skipped; verify inline that every finding_dispositions record is schema-shaped (trigger iff deferred, non-empty reason), that every ACTIVE declined/deferred Must-Fix is named on the readiness assessment's '**Declined/Deferred Must-Fixes:**' caveat line(s), and that markers and sidecar records agree. See docs/finding-dispositions.md."
exit 0
;;
synthesis-coverage)
# Synthesis coverage disclosure gate (docs/synthesis-regrounding.md, M1): a run that wrote a
# full editorial letter (*_Core_DE_Synthesis_* / *_Full_DE_Synthesis_*) must disclose what the
# synthesis step could actually see. The disclosure is computed FROM the artifact-read manifest
# ([Project]_Synthesis_Read_Manifest_[runlabel].md, written BEFORE the letter — run-synthesis.md
# §Processing Protocol step 9b), and the manifest's denominator is enumerated FROM DISK
# (run-folder globs) — never from the letter's own prose. V1 presence (manifest, exact name — a
# *_Manifest_Draft_* lookalike does not count; the Appendix C `### Synthesis Coverage`
# subsection; the `<!-- coverage: ok|degraded -->` title-block marker; the sidecar
# synthesis_coverage object). V2 completeness (disk<->manifest row bijection — the manifest can
# neither shrink nor pad the denominator). V3 reconciliation (the note table + sidecar tallies
# are exact projections of the manifest; coverage == the letter marker; one marker, one place).
# V4 provenance/mode agreement (sequential/hybrid/swarm => dispatch-derived, `declared` FAILS —
# the cheap lie is blocked; single-agent => declared + the pinned not-platform-verified sentence
# verbatim). V5 degrade disclosure (ok|degraded RECOMPUTED from the D1-D4 truth table — absent
# artifact rows, uncovered synthesis-bound findings, >60% single-agent context utilization,
# zero excerpts under a Must-Fix — masking fails louder than degrading; normal multi-agent
# outline-mediated coverage is NOT degraded). No override markers exist for any check —
# disclosure is not overridable; the only escape is fixing the manifest. LAUNCH POSTURE
# (operator call folded 2026-07-01, spec §Open questions #1): V2/V3/V4 fiction-checks are
# BLOCKING day one (exit 1); V1/V5 — and so the overall gate — are ADVISORY-FIRST for one
# release (print WARN at exit 0, the escalation-check posture; the run_spot_check gate records
# pass-with-warn); --strict promotes V1/V5 to ERROR. Nothing in the manifest or sidecar ever
# drives a filesystem read/write — row ids and paths are compared as strings against this
# validator's own enumeration. Delegates to scripts/synthesis_coverage.py; degrades to an
# advisory WARN without python3.
SCV_DIR=$(cd "$(dirname "$0")" && pwd)
SCV_HELPER="$SCV_DIR/synthesis_coverage.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SCV_HELPER" ]; then python3 "$SCV_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; synthesis-coverage is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SCV_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 synthesis-coverage <run_folder> [--strict] | --self-test"; exit 2; fi
python3 "$SCV_HELPER" synthesis-coverage "$@"; exit $?
fi
echo "WARN: python3 unavailable — synthesis-coverage skipped; verify inline that the Synthesis Read Manifest exists and covers every on-disk pass artifact / Findings Ledger / Contract / audit file with a closed-enum status, that the letter's Appendix C 'Synthesis Coverage' note and the sidecar synthesis_coverage object are exact projections of the manifest, that provenance matches the execution mode (multi-agent => dispatch-derived), and that the <!-- coverage: ok|degraded --> title-block marker matches the recomputed D1-D4 degrade state. See docs/synthesis-regrounding.md."
exit 0
;;
specificity-floor)
# Pre-Letter Re-Grounding fidelity gate (docs/synthesis-regrounding.md, M2): the Pre-Letter
# Re-Grounding step (run-synthesis.md §Processing Protocol, after the step-9b Synthesis
# Coverage Manifest, before the Step 10 pre-output gate) re-reads the consolidated Findings
# Ledger verbatim and restores the counts / names / quote anchors that context-salience decay
# smears into vague prose ("nine belief failures" -> "several belief failures"). This validator
# is the checkable half: it holds the delivered letter to the specificity the ledger locked.
# COUNT FLOOR (blocking): for each finding cited in the letter (its `<!-- finding: F-... -->`
# prose block), if the finding's ledger entry (Notable-Finding sentence + structured block)
# carries a count (integer 2-99; number-words two..ninety-nine NORMALIZE to digit strings,
# case-insensitively, so a ledger "nine" and a letter "9" are the same token in both
# directions) and the delivered window uses a vague quantifier (VAGUE_QUANTIFIERS, pinned in
# specificity_floor.py) with NONE of the ledger's counts, FAIL. Evidence-locator numbers
# (Ch 12, sc./scenes 30-31, a spelled-out "Chapter Nine") are stripped, never counted; an
# all-malformed ledger (finding blocks present, ZERO parse) REFUSES with a named error
# (malformed-ledger refusal) instead of a vacuous pass.
# ANCHOR FLOOR (blocking): each delivered Must-Fix window must carry an evidence
# reference matching the finding's locked evidence_refs, so a restored number rides a
# ledger-matching anchor (bounds count-hallucination). Both overridable per-ID with
# `<!-- override: specificity-floor F-... — <why> -->` + an Appendix B entry (non-countable
# findings). The `<!-- regrounding: done -->` marker presence is an ADVISORY WARN (--strict
# promotes). The reverse-direction letter-ID-must-exist-in-ledger check is NOT here — it is
# finding-trace E1's (spec §M2.3, single ownership); the smuggled-finding gate is
# `validate.sh finding-trace`. Firewall: re-grounding may only ADD specificity — this validator
# reads severity only to CLASSIFY (synthesis-bound / Must-Fix), never emits or rewrites it.
# Delegates to scripts/specificity_floor.py; degrades to an advisory WARN without python3.
SPF_DIR=$(cd "$(dirname "$0")" && pwd)
SPF_HELPER="$SPF_DIR/specificity_floor.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SPF_HELPER" ]; then python3 "$SPF_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; specificity-floor is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SPF_HELPER" ]; then
if [ $# -lt 2 ]; then echo "Usage: $0 specificity-floor <editorial_letter> <findings_ledger> [--strict] | --self-test"; exit 2; fi
python3 "$SPF_HELPER" specificity-floor "$@"; exit $?
fi
echo "WARN: python3 unavailable — specificity-floor skipped; verify inline that every delivered finding's count survived from the ledger (a ledger 'nine belief failures' must not read 'several belief failures' in the letter without an ID-scoped <!-- override: specificity-floor F-... --> + Appendix B), and that each Must-Fix window carries an evidence reference matching the finding's locked evidence_refs. The smuggled-finding check is finding-trace's, not this one. See docs/synthesis-regrounding.md."
exit 0
;;
refutation-coverage)
# Finding disconfirmation V1 (docs/finding-disconfirmation.md §8): no HIGH without
# survived refutation. Every synthesis-bound ledger finding at confidence HIGH needs an
# apodictic.refutation.v1 record with attempted:true + outcome survived — or a letter-BODY
# cap-bound disclosure marker <!-- refutation: not-attempted-budget F-… -->, honored ONLY
# when the RECOMPUTED budget actually binds (eligible recomputed from the ledger > the
# spec cap 15) under a bound:true budget for an unprocessed id (a marker on a processed
# finding, under an unbound budget, or under a bound:true the recompute does not
# corroborate, is an ERROR — the marker is a disclosure, not an exemption). The budget
# block is the pass's self-report: eligible/bound/cap recompute against the ledger and
# the §5 spec constant, mismatch = blocking ERROR (recompute, don't trust — Codex P1,
# PR #161).
# Every Must-Fix needs a record with attempted:true (the cap of 15 can never bind on
# Must-Fix: their ceiling is 10 — a missing Must-Fix record is a silent skip). Every
# record id must resolve to a locked ledger finding (dangling = ERROR). Findings carrying
# a declined/deferred disposition marker are exempt (the author already ruled — spec §5).
# Runs at Step 10 alongside deficit-lock/softness-check and in the run_spot_check gate.
# Delegates to scripts/refutation_check.py; degrades to advisory (WARN, exit 0) without
# python3 — the Step 6b prose contract still applies.
RFC_DIR=$(cd "$(dirname "$0")" && pwd)
RFC_HELPER="$RFC_DIR/refutation_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RFC_HELPER" ]; then python3 "$RFC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; refutation-coverage is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RFC_HELPER" ]; then
if [ $# -lt 3 ]; then echo "Usage: $0 refutation-coverage <editorial_letter> <findings_ledger> <refutation_record> | --self-test"; exit 2; fi
python3 "$RFC_HELPER" refutation-coverage "$@"; exit $?
fi
echo "WARN: python3 unavailable — refutation-coverage skipped; verify inline that every synthesis-bound HIGH finding has a survived apodictic.refutation.v1 record (or a cap-bound disclosure marker honored only when the budget RECOMPUTED from the ledger actually binds — bound:true is a claim to verify, never an input), every Must-Fix has an attempted record, and every record id resolves to a locked ledger finding. See docs/finding-disconfirmation.md."
exit 0
;;
refutation-evidence)
# Finding disconfirmation V2 (docs/finding-disconfirmation.md §8): attempts without
# quote-anchored counter-evidence don't count. counter_evidence_quotes /
# alternative_explanations at minItems 1; each quote VERBATIM in the intake manuscript
# snapshot (as-is bytes) and single-line — verbatim-presence only, count >= 1 (A6's
# uniqueness/offsets deliberately dropped: a counter-evidence quote is evidence, not an
# anchor); a fabricated quote is an ERROR (the anti-rubber-stamp tooth). Quote > 25 words
# -> WARN; ungrounded alternative_explanations (no quote mark / Ch.-p. locator / contract
# ref) -> WARN. The snapshot is REQUIRED (missing = ERROR) under --require-snapshot or
# when the record's folder holds a *_Core_DE_Synthesis_*/*_Full_DE_Synthesis_* letter
# (the annotated-manuscript offer's run-shape detection — pass the flag from Step 10 on
# core-de/full-de runs); elsewhere missing = WARN and every demotion in the record is
# VOID (weakened/refuted may not be transcribed; the letter must disclose). Each record's
# snapshot_path/snapshot_sha256 must match the on-disk snapshot (as-is bytes, hashed) —
# mismatch = ERROR. snapshot_path is model-written and READ-ONLY: resolved beside the
# record, contained to that folder (realpath); absolute/escaping paths refused by name.
# Budget arithmetic: processed == min(eligible, cap), bound == (eligible > processed),
# cap == the §5 spec constant 15, and processed == the count of schema-valid refutation
# blocks in the record — all blocking ERRORs (the processed/count cross-check was
# promoted from WARN per the Codex P1 on PR #161: the budget block is a self-report).
# Delegates to scripts/refutation_check.py; degrades to advisory WARN without python3.
RFE_DIR=$(cd "$(dirname "$0")" && pwd)
RFE_HELPER="$RFE_DIR/refutation_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RFE_HELPER" ]; then python3 "$RFE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; refutation-evidence is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RFE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 refutation-evidence <refutation_record> [<manuscript_snapshot>] [--require-snapshot] | --self-test"; exit 2; fi
python3 "$RFE_HELPER" refutation-evidence "$@"; exit $?
fi
echo "WARN: python3 unavailable — refutation-evidence skipped; verify inline that every counter-evidence quote occurs verbatim (single-line) in the intake snapshot, snapshot_sha256 matches the on-disk bytes, the budget arithmetic holds (cap is the spec constant 15; processed must equal the actual refutation-block count), and no demotion rides unverified quotes. See docs/finding-disconfirmation.md."
exit 0
;;
refutation-write-scope)
# Finding disconfirmation V3 (docs/finding-disconfirmation.md §8): the pass may write
# only refutation.* + confidence — any severity write fails the run. A severity key at
# any depth, or a canonical severity token used as a field value, inside a
# refutation/budget block is an ERROR (the schema omits the property; this arm enforces
# it against the subset checker's unknown-key tolerance). For every processed id: ledger
# confidence == record confidence_after, per the outcome caps (survived = unchanged and
# never confidence-raising; weakened capped at MEDIUM; refuted = LOW/UNCERTAIN); the
# equality assertion is skipped for records V2 voided for a missing snapshot (re-derived
# from the record's snapshot_path beside the record). A pre-delivery fail-the-run gate,
# not write prevention — the deficit-lock/softness-check posture; the letter-side
# severity floor stays owned by deficit-lock + softness-check (no duplication).
# Delegates to scripts/refutation_check.py; degrades to advisory WARN without python3.
RFW_DIR=$(cd "$(dirname "$0")" && pwd)
RFW_HELPER="$RFW_DIR/refutation_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RFW_HELPER" ]; then python3 "$RFW_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; refutation-write-scope is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RFW_HELPER" ]; then
if [ $# -lt 2 ]; then echo "Usage: $0 refutation-write-scope <findings_ledger> <refutation_record> | --self-test"; exit 2; fi
python3 "$RFW_HELPER" refutation-write-scope "$@"; exit $?
fi
echo "WARN: python3 unavailable — refutation-write-scope skipped; verify inline that no refutation block carries a severity channel and that each processed finding's ledger confidence equals the record's confidence_after per the outcome caps table. See docs/finding-disconfirmation.md."
exit 0
;;
obsidian-export)
# Annotated-Manuscript Obsidian export (docs/annotated-manuscript-export.md): project the gated
# annotation manifest + snapshot into Obsidian-NATIVE Markdown (no plugin) — each finding becomes a
# footnote [^<finding_id>] at its anchor locus whose definition carries the VERBATIM manifest comment
# (Obsidian renders footnotes natively; CriticMarkup needs a plugin). A pure projection: the reverse
# transform (strip the manifest-keyed [^id] refs + the trailing [^id]: block) reproduces the snapshot
# byte-for-byte. O1 round-trip (ERROR, two-sided precondition), O2 footnote resolution (ERROR:
# ref<->definition bijection == manifest id set), O3 comment fidelity (ERROR: definition == verbatim
# comment). `obsidian <run_folder>` writes obsidian/<copy>; `obsidian-export <run_folder>` validates.
# Delegates to scripts/annotation_export.py; degrades to an advisory WARN without python3.
OBE_DIR=$(cd "$(dirname "$0")" && pwd)
OBE_HELPER="$OBE_DIR/annotation_export.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$OBE_HELPER" ]; then python3 "$OBE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; obsidian-export is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$OBE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 obsidian-export <run_folder> | (generate) $0 ... via annotation_export.py obsidian <run_folder>"; exit 2; fi
python3 "$OBE_HELPER" obsidian-export "$@"; exit $?
fi
echo "WARN: python3 unavailable — obsidian-export skipped; check inline that each finding renders as a [^<finding_id>] footnote whose definition is the verbatim manifest comment, and that stripping the refs + definition block reproduces the snapshot. See docs/annotated-manuscript-export.md."
exit 0
;;
html-export)
# Annotated-Manuscript read-only HTML export (docs/annotated-manuscript-export.md, Increment 3):
# project the gated manifest + snapshot into a self-contained .html (faithful <pre>, escaped, with
# <sup> footnote-style markers at each anchor + a findings section with bidirectional anchor links;
# embedded CSS, no network). A pure projection: the reverse transform (strip the manifest-keyed
# <sup id="ref-…"> markers + the exact 3-entity HTML-unescape) reproduces the snapshot byte-for-byte.
# H1 round-trip (ERROR), H2 anchor resolution (ERROR: <sup>↔<li> bijection == manifest id set),
# H3 comment fidelity (ERROR: <li> == escaped verbatim comment + exact back-ref). `html <run_folder>`
# writes html/<copy>.html; `html-export <run_folder>` validates the on-disk artifact. Delegates to
# scripts/annotation_export.py; degrades to an advisory WARN without python3.
HXE_DIR=$(cd "$(dirname "$0")" && pwd)
HXE_HELPER="$HXE_DIR/annotation_export.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$HXE_HELPER" ]; then python3 "$HXE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; html-export is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$HXE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 html-export <run_folder> | (generate) via annotation_export.py html <run_folder>"; exit 2; fi
python3 "$HXE_HELPER" html-export "$@"; exit $?
fi
echo "WARN: python3 unavailable — html-export skipped; check inline that the <pre> is the escaped snapshot with manifest-keyed <sup> markers, the findings <li> are verbatim comments, and stripping the markers + unescaping reproduces the snapshot. See docs/annotated-manuscript-export.md."
exit 0
;;
docx-export)
# Annotated-Manuscript DOCX export (docs/annotated-manuscript-export.md, Increment 4): project the
# gated manifest + snapshot into a .docx (OOXML zip) where each finding's manuscript span is wrapped
# as an anchored Word comment (commentRangeStart/End + commentReference + comments.xml) — so Google
# Docs imports it as a native ANCHORED comment. A pure projection (verbatim snapshot text +
# verbatim comments, fixed OOXML boilerplate); the zip is byte-deterministic (ZIP_STORED, pinned
# ZipInfo). D1 artifact integrity (ERROR: on-disk == fresh build byte-for-byte — the authoritative
# lock for a binary), D2 text round-trip (ERROR: document.xml <w:t> -> snapshot), D3 comment
# resolution + fidelity (ERROR: range/reference <-> comment bijection == manifest set; each comment
# verbatim). `docx <run_folder>` writes docx/<copy>.docx; `docx-export <run_folder>` validates.
# Delegates to scripts/annotation_export.py; degrades to an advisory WARN without python3.
DXE_DIR=$(cd "$(dirname "$0")" && pwd)
DXE_HELPER="$DXE_DIR/annotation_export.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$DXE_HELPER" ]; then python3 "$DXE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; docx-export is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$DXE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 docx-export <run_folder> | (generate) via annotation_export.py docx <run_folder>"; exit 2; fi
python3 "$DXE_HELPER" docx-export "$@"; exit $?
fi
echo "WARN: python3 unavailable — docx-export skipped; check inline that document.xml is the snapshot text in <w:p>/<w:t> with each finding's span wrapped commentRangeStart/End + commentReference, and comments.xml carries the verbatim comments. See docs/annotated-manuscript-export.md."
exit 0
;;
pdf-export)
# Annotated-Manuscript PDF export (docs/annotated-manuscript-export.md, Increment 5): project the
# gated manifest + snapshot into a self-contained .pdf hand-written from raw PDF objects (header,
# body, xref, trailer — NO external libs). The snapshot prose renders as base-14 Helvetica text; each
# finding drops an inline [<finding_id>] marker at its anchor locus (the HTML <sup> precedent) and its
# verbatim comment lands in a trailing Findings section. A pure projection; the PDF is byte-
# deterministic (no /Info, no dates, no /ID, no compression, octal-escaped ASCII text). P1 artifact
# integrity (ERROR: on-disk == fresh build byte-for-byte — the authoritative lock for a binary), P2
# text round-trip (ERROR: the manuscript text runs, markers stripped, -> snapshot), P3 marker
# resolution + comment fidelity (ERROR: markers <-> comments bijection == manifest set; each comment
# verbatim). `pdf <run_folder>` writes pdf/<copy>.pdf; `pdf-export <run_folder>` validates.
# Delegates to scripts/annotation_export.py; degrades to an advisory WARN without python3.
PXE_DIR=$(cd "$(dirname "$0")" && pwd)
PXE_HELPER="$PXE_DIR/annotation_export.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$PXE_HELPER" ]; then python3 "$PXE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; pdf-export is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$PXE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 pdf-export <run_folder> | (generate) via annotation_export.py pdf <run_folder>"; exit 2; fi
python3 "$PXE_HELPER" pdf-export "$@"; exit $?
fi
echo "WARN: python3 unavailable — pdf-export skipped; check inline that the PDF text runs are the snapshot lines with a manifest-keyed [finding_id] marker at each locus, the Findings section carries the verbatim comments, and stripping the markers reproduces the snapshot. See docs/annotated-manuscript-export.md."
exit 0
;;
legal-risk)
# Legal Risk Register workflow (docs/legal-risk-register.md): structural checks over the
# apodictic.legal_risk.v1 blocks in a register — L1 invalid item, L2 duplicate id, L3 missing
# not-a-lawyer disclaimer (the signature gate; the register must never read as legal advice);
# W1 legal-advice drift (a legal CONCLUSION where a flag belongs — the module firewall; override
# <!-- override: legal-advice-drift LR-NN — … -->), W2 a review-now item not routed to legal
# counsel. W1/W2 advisory, ERROR under --strict. The register FLAGS areas for legal review; it
# does not give legal advice. Takes a run folder (globs *_Legal_Risk_Register_*.md) or explicit
# files. Delegates to scripts/legal_risk.py; degrades to an advisory WARN without python3.
LRK_DIR=$(cd "$(dirname "$0")" && pwd)
LRK_HELPER="$LRK_DIR/legal_risk.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$LRK_HELPER" ]; then python3 "$LRK_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; legal-risk is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$LRK_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 legal-risk <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$LRK_HELPER" legal-risk "$@"; exit $?
fi
echo "WARN: python3 unavailable — legal-risk skipped; check inline that the register carries a not-a-lawyer disclaimer, every item flags (not adjudicates) the exposure, and each review-now item routes to counsel. See docs/legal-risk-register.md."
exit 0
;;
promise-contract)
# Promise-Contract Fidelity workflow (docs/promise-contract-audit.md): does the pitch keep the
# promise the book makes? Structural checks over the apodictic.pitch_copy.v1 input + the F-PCF
# apodictic.finding.v1 blocks — P1 two-sided gap (every F-PCF-NN finding cites >=1 copy: ref AND
# >=1 contract:/ms: ref, the namespaced convention), P2 pitch copy persisted & typed (a valid
# apodictic.pitch_copy.v1 input exists, every doc declares a copy_type), P3 reveal-leak form gate
# (a PCF2 finding's copy: ref must not point at a synopsis); W1 drafted-copy leak (a multi-sentence
# quoted block in the report that is not a verbatim substring of the persisted pitch copy — the
# Firewall; override <!-- override: drafted-copy PCF-NN — … -->), W2 market-prediction drift (a
# finding matching the prohibited sales-prediction phrase set — the #14 boundary; override
# <!-- override: market-prediction PCF-NN — … -->). W1/W2 advisory, ERROR under --strict. The
# module FLAGS the pitch↔book gap and a class of repair; it never drafts the copy (diagnose, don't
# write — Shelf & Positioning owns the rewrite). Takes a run folder (globs *_Pitch_Copy_*.md, plus
# the folder's findings) or explicit files. Delegates to scripts/promise_contract.py; degrades to
# an advisory WARN without python3.
PCF_DIR=$(cd "$(dirname "$0")" && pwd)
PCF_HELPER="$PCF_DIR/promise_contract.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$PCF_HELPER" ]; then python3 "$PCF_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; promise-contract is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$PCF_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 promise-contract <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$PCF_HELPER" promise-contract "$@"; exit $?
fi
echo "WARN: python3 unavailable — promise-contract skipped; check inline that every F-PCF finding cites a two-sided namespaced ref (copy: + contract:/ms:), the pitch copy is persisted and typed, no PCF2 is raised against a synopsis, and the report quotes the author's own copy verbatim (never drafts a replacement). See docs/promise-contract-audit.md."
exit 0
;;
continuity-bible)
# Auto-Derived Continuity Bible (docs/continuity-bible.md): the narrative half of a style sheet —
# consolidate the canonical facts the manuscript commits to (identity/physical facts, named
# objects, place details) and surface the contradictions. Structural checks over the
# apodictic.canon_fact.v1 blocks — C1 schema (bad category enum, malformed CF-NN id, missing
# field, unquoted-numeric value, empty loci, duplicate id), C2 locus presence & shape (a coarse
# chapter/§/¶/line/page token; a precondition, NOT a firewall proof — locus resolution is deferred
# to the shared snapshot layer), C3 contradiction integrity (a `## Contradiction Ledger` row must
# pair >=2 real canon_facts that share entity+attribute but assert DIFFERENT values); C4 chronology
# consume (a chronology fact that does not consolidate to a real Timeline scene id re-derives a
# temporal fact the Timeline owns; override <!-- override: bible-rederive CF-NN — … -->), W1
# coverage (a Timeline POV with no Cast entry, or a Timeline setting with no Places entry). C4/W1
# advisory, ERROR under --strict. The module EXTRACTS the stated and SURFACES contradictions; it
# never infers an unstated fact or resolves a conflict (the Firewall). Pass the project-root
# Timeline.md as a second file so C4 can resolve scene ids and W1 can check coverage; without it C4
# cannot confirm scene ids and W1 is skipped. Takes a run folder (globs *_Continuity_Bible_*.md) or
# explicit files. Delegates to scripts/continuity_bible.py; degrades to an advisory WARN without
# python3.
CBL_DIR=$(cd "$(dirname "$0")" && pwd)
CBL_HELPER="$CBL_DIR/continuity_bible.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CBL_HELPER" ]; then python3 "$CBL_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; continuity-bible is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$CBL_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 continuity-bible <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$CBL_HELPER" continuity-bible "$@"; exit $?
fi
echo "WARN: python3 unavailable — continuity-bible skipped; check inline that every canon_fact carries a well-shaped locus, the Contradiction Ledger pairs real conflicting facts, chronology facts consolidate to a Timeline scene id, and the Bible records stated facts only (never inferring or resolving canon). See docs/continuity-bible.md."
exit 0
;;
setup-payoff)
# Setup–Payoff Ledger (docs/setup-payoff-ledger.md): the referential-completeness half of a style
# sheet — the mechanical home for ConStory-Bench's "Abandoned Plot Elements" row (introduced
# expectations never resolved). Records the author-marked Foreshadow → Trigger → Payoff triples
# (Codified Foreshadowing-Payoff Text Generation, Yun et al., arXiv:2601.07033) and CHECKS every
# foreshadow resolves. Structural checks over apodictic.setup_payoff.v1 (foreshadow) +
# apodictic.payoff.v1 (resolving payoff) blocks — SP1 schema (SP-NN / PO-NN ids, required fields,
# state enum, duplicate id, bad JSON), SP2 referential integrity (a non-empty payoff_ref must
# id-match an existing payoff block; forward-only, N:1 allowed; a phantom ref FAILs), SP3 open
# rationale (an `open` state must carry a non-empty open_rationale), SP4 derived state (the declared
# `state` must match the mechanically-derived §D4 value over the resolved refs — the register never
# overrides the derivation; NO model in the gate), X1 firewall (the Ledger carries no editorial
# Must/Should/Could-Fix token and no apodictic:finding block — a fact register, not a defect list).
# The model marks the triple; the validator DERIVES the state and checks completeness — it never
# decides whether a passage "counts" as payoff (the deferred SETEC-consumer job). An `abandoned` row
# is a SURFACED fact the editorial letter cites in prose, not a validation failure. Takes a run
# folder (globs *_Setup_Payoff_Ledger_*.md) or explicit files. Delegates to
# scripts/setup_payoff_checks.py; degrades to an advisory WARN without python3.
SPL_DIR=$(cd "$(dirname "$0")" && pwd)
SPL_HELPER="$SPL_DIR/setup_payoff_checks.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SPL_HELPER" ]; then python3 "$SPL_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; setup-payoff is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SPL_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 setup-payoff <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$SPL_HELPER" setup-payoff "$@"; exit $?
fi
echo "WARN: python3 unavailable — setup-payoff skipped; check inline that every setup_payoff foreshadow carries a state, a non-empty payoff_ref resolves to a real payoff block, an `open` state carries a rationale, the declared state matches the refs, and the ledger carries no editorial severity token or finding block. See docs/setup-payoff-ledger.md."
exit 0
;;
intake-interview)
# Uncertainty-Resolution Intake Interview (docs/uncertainty-intake-interview.md): at the
# after-Pass-0/1 checkpoint, asks the author to resolve a specific structural ambiguity the
# framework DETECTED but cannot settle from the text — and only that. Structural checks over the
# apodictic.intake_query.v1 blocks — I1 schema (bad kind/confidence enum, malformed IQ-NN id,
# missing current_inference/question, duplicate id), I2 no-contract-duplication (a question that
# re-asks a contract element owned by the intake / Shelf — advisory, ERROR --strict; override
# <!-- override: intake-dup IQ-NN — … -->), I3 grounded ambiguity (one of a resolving
# ambiguity_ref (a real finding id in the Ledger) or a non-empty source_note; a dangling ref is an
# error — a query grounded in neither is manufactured), I4 calibrate-not-suppress (treat_as_intended
# may direct HOW a feature is assessed but never pre-empt a verdict — ERROR, the Deficit-Lock
# guard), W1 coverage (a Pass-0/1 LOW/UNCERTAIN finding or an Unresolved-Questions bullet with no
# query — advisory). Pass the Findings Ledger as a second file so I3 resolves ids and W1 checks
# coverage. Takes a run folder (globs *_Intake_Interview_*.md) or explicit files. Delegates to
# scripts/intake_interview.py; degrades to an advisory WARN without python3.
IIV_DIR=$(cd "$(dirname "$0")" && pwd)
IIV_HELPER="$IIV_DIR/intake_interview.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$IIV_HELPER" ]; then python3 "$IIV_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; intake-interview is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$IIV_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 intake-interview <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$IIV_HELPER" intake-interview "$@"; exit $?
fi
echo "WARN: python3 unavailable — intake-interview skipped; check inline that every intake_query disambiguates a detected ambiguity (never re-asks the contract), is grounded in a real finding id or a source_note, and that treat_as_intended calibrates assessment without suppressing a finding. See docs/uncertainty-intake-interview.md."
exit 0
;;
author-fingerprint)
# Cross-Manuscript Author Voice/Craft Fingerprint (docs/author-voice-fingerprint.md): the
# persistent cross-work memory of a writer's voice, collected under an operator-designated
# author-root. It does NO new stylometry — it consumes the single-voice AI-prose machinery
# (voice_profile / voice_distance + personal-baseline z-scores) and persists/diagnoses. Structural
# checks over the apodictic.voice_fingerprint.v1 blocks — F1 schema (bad source enum, malformed
# VF-… id, missing field, empty/non-scalar metrics, duplicate id), F2 provenance (each fingerprint
# cites a centroid_ref naming a consumed audit output), F3 same-register comparison (a drift/range
# claim referencing >=2 fingerprints must share a register — the AI-prose domain-shift guard); F4
# descriptive-not-prescriptive (no Must/Should/Could token, no "fix your voice" directive — the
# module observes movement, it never prescribes or grades; advisory, ERROR --strict; override
# <!-- override: fingerprint-frame VF-… — … -->), W1 insufficient data (no register has >=2
# fingerprints — seed-only), W2 local-only hygiene (missing local-only marker or an external URL —
# advisory WARN ONLY, never gate-blocking; the binding privacy guarantee is the module's runtime
# no-external-call rule). Takes an author-root (globs Author_Voice_Profile*.md) or explicit files.
# Delegates to scripts/author_fingerprint.py; degrades to an advisory WARN without python3.
AVF_DIR=$(cd "$(dirname "$0")" && pwd)
AVF_HELPER="$AVF_DIR/author_fingerprint.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AVF_HELPER" ]; then python3 "$AVF_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; author-fingerprint is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AVF_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 author-fingerprint <author_root|files...> [--strict] | --self-test"; exit 2; fi
python3 "$AVF_HELPER" author-fingerprint "$@"; exit $?
fi
echo "WARN: python3 unavailable — author-fingerprint skipped; check inline that every voice_fingerprint cites a source + centroid_ref, drift is compared only within a register, the profile is descriptive (no Must/Should/Could, no 'fix your voice'), and it carries a local-only marker. See docs/author-voice-fingerprint.md."
exit 0
;;
content-advisory)
# Content-Advisory / Sensitivity-Surface Derivation (docs/content-advisory.md): derives a
# reader/marketing-facing advisory — where the manuscript depicts intense material, at what
# intensity, on- or off-page — generated ONLY under the opt-in marker. Structural checks over the
# apodictic.content_note.v1 blocks — A1 schema (bad category/intensity/depiction enum, malformed
# CN-NN id, missing field, empty loci, category 'other' with empty label, duplicate id), A2 locus
# presence & shape (a coarse chapter/§/¶/line/page token; resolution deferred to the snapshot
# layer), A3 no editorial-severity leak (no Must/Should/Could token in the prose or a label, no
# apodictic:finding block — content notes are advisories, not findings, the Legal-Risk
# orthogonal-severity discipline); W1 prescriptive drift (a "should cut/soften …" construction,
# NOT a bare descriptive adjective — advisory, ERROR --strict; override
# <!-- override: advisory-eval CN-NN — … -->), W2 opt-in marker present. The module DESCRIBES the
# depicted; it never judges or prescribes. Takes a run folder (globs *_Content_Advisory_*.md) or
# explicit files; if no advisory artifact is resolved it no-ops with exit 2. Delegates to
# scripts/content_advisory.py; degrades to an advisory WARN without python3.
CAD_DIR=$(cd "$(dirname "$0")" && pwd)
CAD_HELPER="$CAD_DIR/content_advisory.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CAD_HELPER" ]; then python3 "$CAD_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; content-advisory is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$CAD_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 content-advisory <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$CAD_HELPER" content-advisory "$@"; exit $?
fi
echo "WARN: python3 unavailable — content-advisory skipped; check inline that every content_note carries a well-shaped locus, the advisory is descriptive (no Must/Should/Could, no 'should cut/soften'), carries no apodictic:finding block, and has the opt-in marker. See docs/content-advisory.md."
exit 0
;;
coaching-history)
# Coaching History & Pattern Recognition (docs/coaching-history.md; ROADMAP §Coaching Deepening):
# APODICTIC's ONE ethically-sensitive surface. Over multiple revision cycles the coach can surface
# a cross-session PROCESS pattern — the same finding deferred across an unbroken run of sessions, or
# a revision-arc phase left open across consecutive sessions — as a rolling, OPT-IN, local-only
# artifact of DESCRIPTIVE observations, each MECHANICALLY derived from the recorded finding-
# disposition / finding_states records (a count, never a vibe) and carrying NO editorial severity
# (no Must/Should/Could token, no apodictic:finding block). Structural checks over the
# apodictic.coaching_observation.v1 blocks in the ONE *_Coaching_History_*.md — H1 schema + unique
# CH-NN + per-pattern count floor (deferral-recurrence >=3, phase-incompletion >=2), H2 provenance/
# anti-fabrication (every `<F-id> deferred @ session <n>` resolves to a recorded deferred
# disposition; len(evidence)>=count; the cited sessions are ACTUALLY CONSECUTIVE), H3 descriptive-
# not-judgmental (reuses author_fingerprint._PRESCRIPTIVE_RE + a trait-blame lexicon; WARN, ERROR
# --strict; per-id override <!-- override: coaching-observation CH-NN — … -->), H4 no-severity-leak,
# H7 tentative-framing (no trait VERDICT / no bare-scoreboard rendering / carries an invitation —
# the transference-health floor; WARN, ERROR --strict), W1 local-only. Plus the TWO Fable ethics
# gates: H5 single-home / no coach-only shadow (scan project root + runs/* + the sidecar; a
# projected observation block / schema-id / evidence-grammar string outside the one artifact, a
# second artifact, or sidecar coaching material beyond `coaching_history_seq` = non-overridable
# ERROR; a bare CH-NN token elsewhere = WARN + override), and H6 deletion-honored (under the
# <!-- coaching-history: deleted --> tombstone, RECOMPUTE deletion from artifacts — the full H5 scan
# empty + no surviving artifact + no seq; residue = ERROR, NO override; opted-in+deleted = ERROR).
# The `delete <project_root>` subcommand removes every artifact (root + runs/*), drops the seq, and
# flips the Diagnostic_State.md consent marker to the tombstone (deletion revokes consent). OPT-IN:
# produced only under `<!-- coaching-history: opted-in -->` (home: Diagnostic_State.md); no marker →
# no-op (exit 2). Takes the PROJECT ROOT (H5/H6 scan scope), not just the artifact. Delegates to
# scripts/coaching_history.py; degrades to an advisory WARN without python3.
CHH_DIR=$(cd "$(dirname "$0")" && pwd)
CHH_HELPER="$CHH_DIR/coaching_history.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CHH_HELPER" ]; then python3 "$CHH_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; coaching-history is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$CHH_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 coaching-history <project_root|files...> [--strict] | coaching-history delete <project_root> | --self-test"; exit 2; fi
if [ "${1:-}" = "delete" ]; then python3 "$CHH_HELPER" delete "$2"; exit $?; fi
python3 "$CHH_HELPER" coaching-history "$@"; exit $?
fi
echo "WARN: python3 unavailable — coaching-history skipped; check inline that observations live in exactly ONE opted-in *_Coaching_History_*.md (no projection into session plans / the sidecar), every observation resolves to recorded consecutive deferrals, none carries a Must/Should/Could token or a trait verdict, and a tombstoned project has no surviving artifact/seq. See docs/coaching-history.md."
exit 0
;;
style-explanation)
# Interpretable Stylometric Explanation (docs/interpretable-stylometric-explanation.md): the
# DESCRIPTIVE labelling layer ON TOP of the Author Voice Fingerprint (#9). #9 measures how
# distinctive a voice is and persists it as scalar z-scores; this overlay attaches a
# natural-language gloss to a handful of the salient MEASURED features, each bound by provenance
# (feature_ref) to the exact SETEC voice_profile feature it describes. It does NO new stylometry
# and offers NO advice — it NAMES a measured feature, it never prescribes a voice change and never
# fabricates a style claim. Structural checks over the apodictic.style_label.v1 blocks — X1 schema
# (bad feature_family/frame/direction/magnitude enum, malformed SL-NN id, missing field, broken
# JSON, duplicate id), X2 provenance/anti-fabrication (every label cites a non-empty feature_ref
# into a consumed measurement — an un-sourced label is fabricated), X3 no-severity-leak (no
# Must/Should/Could token in the prose or a label, no apodictic:finding block — a style label is
# not a defect), X4 descriptive-not-prescriptive (no prescriptive voice-directive and no
# comparison-to-emulate construction in a label or the prose — the signature firewall gate;
# advisory, ERROR --strict; per-id override <!-- override: style-frame SL-NN — … -->, prose-level
# override the bare <!-- override: style-frame — … -->), X5 same-register cluster (a
# describes-cluster label referencing >=2 labels must share a register — the AI-prose domain-shift
# guard); W1 seed/coverage (a single glossed feature is a thin overlay — advisory, ERROR --strict;
# no blocks no-op), X6 local-only hygiene (missing local-only marker or an external URL — advisory
# WARN ONLY, never gate-blocking; the binding privacy guarantee is the module's runtime
# no-external-call rule). The schema itself is built so a "write more like X" directive is
# unrepresentable (no target/goal/recommendation/rewrite field; closed descriptive enums). Takes an
# author-root (globs Author_Style_Explanation*.md / Author_Voice_Profile*.md) or explicit files; if
# no style-explanation artifact is resolved it no-ops with exit 2. The label-generating
# embedding/scoring model is a deferred M2 lazy-import + skipif seam — this validator never calls a
# model. Delegates to scripts/style_explanation.py; degrades to an advisory WARN without python3.
SEX_DIR=$(cd "$(dirname "$0")" && pwd)
SEX_HELPER="$SEX_DIR/style_explanation.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SEX_HELPER" ]; then python3 "$SEX_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; style-explanation is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SEX_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 style-explanation <author_root|files...> [--strict] | --self-test"; exit 2; fi
python3 "$SEX_HELPER" style-explanation "$@"; exit $?
fi
echo "WARN: python3 unavailable — style-explanation skipped; check inline that every style_label cites a non-empty feature_ref, the overlay is descriptive (no Must/Should/Could, no 'vary your voice', no 'write more like X'), carries no apodictic:finding block, a describes-cluster stays within one register, and the profile has a local-only marker. See docs/interpretable-stylometric-explanation.md."
exit 0
;;
persona-divergence)
# Reader-Persona Simulation (docs/reader-persona-simulation.md): runs the reader-experience lens
# through several declared reading DISPOSITIONS and surfaces where the predicted experience
# DIVERGES. Structural checks over the apodictic.persona.v1 + apodictic.divergence.v1 blocks — D1
# schema (bad disposition/target/experience enum, malformed P-NN/D-NN id, missing
# anchor/magnitude/experiences, a nested experiences value not in engaged|neutral|friction|
# disengage or naming an undeclared persona, duplicate id), D2 grounded prediction (a divergence
# anchor must resolve to a real finding id in the Ledger or a real Timeline scene id — the
# signature firewall gate; an ungrounded prediction is fabricated), D3 target-severity anchoring
# (exactly one persona target:true, and no divergence asserted_severity below the anchored
# finding's locked Ledger severity — segmentation may not downgrade the verdict); D4 no fabricated
# testimony (a first-person reader-reaction quote presented as data — the #17 boundary; advisory,
# ERROR --strict; override <!-- override: persona-quote D-NN — … -->), D5 disposition-not-character
# (a persona block with any key outside the closed disposition set — ERROR, NON-overridable, the
# real guarantee against #17), W1 coverage (>=2 personas with a varying disposition axis). Pass the
# Findings Ledger (and optionally the Timeline) as additional files so D2/D3 resolve. Takes a run
# folder (globs *_Persona_Divergence_Map_*.md) or explicit files. Delegates to
# scripts/persona_divergence.py; degrades to an advisory WARN without python3.
PDV_DIR=$(cd "$(dirname "$0")" && pwd)
PDV_HELPER="$PDV_DIR/persona_divergence.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$PDV_HELPER" ]; then python3 "$PDV_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; persona-divergence is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$PDV_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 persona-divergence <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$PDV_HELPER" persona-divergence "$@"; exit $?
fi
echo "WARN: python3 unavailable — persona-divergence skipped; check inline that every persona is a closed-key disposition (no character keys), every divergence anchors a real finding/locus, exactly one persona is target:true with no asserted severity below the locked verdict, and no fabricated reader quotes appear. See docs/reader-persona-simulation.md."
exit 0
;;
world-bible)
# Standalone Worldbuilding Bible (docs/worldbuilding-bible.md): a pre-draft consistency check over
# the author's OWN hand-authored worldbuilding bible — distinct from the manuscript-facing SFF
# audits. Structural checks over the apodictic.world_fact.v1 blocks — W1 schema (bad category/
# polarity enum, malformed WF-NN id, missing field, unquoted-numeric value, empty loci, broken
# JSON) PLUS bespoke closed-key checking (the subset engine admits unknown keys, so a misspelled
# field would otherwise pass — caught here or the closed-set guarantee is hollow), WD duplicate id,
# and the three deterministic, stdlib-only, CONSERVATIVE contradiction arms: WB-R1 closed-set rule
# consistency (same subject + normalized value, can vs cannot / requires vs cannot), WB-C1 cost
# contradiction (two different stated costs for one subject) + WB-C2 free-then-costed (advisory,
# ERROR --strict), WB-G1 distance contradiction (one edge, two parsed distances WITHIN a
# commensurable unit class — spatial mile/league/km vs temporal travel-time day/hour are SEPARATE
# axes that never collide-check against each other), WB-G2 chronology (a CYCLE in the happens-before
# graph, or the same event at two Day anchors). Plus a WF firewall prose scan (a resolution/
# invention verb leaking into the bible's prose; advisory, ERROR --strict). Each pair is overridable
# per-pair: <!-- override: world-rule|world-cost|world-geo WF-NN/WF-MM — … --> (and
# <!-- override: world-firewall — … --> for WF). The tool EXTRACTS the stated and SURFACES the
# contradictions the bible has committed to; it never invents world content or resolves a conflict
# (the Firewall). Takes a run folder (globs *_Worldbuilding_Bible_*.md) or explicit files.
# Delegates to scripts/world_bible.py; degrades to an advisory WARN without python3.
WBL_DIR=$(cd "$(dirname "$0")" && pwd)
WBL_HELPER="$WBL_DIR/world_bible.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$WBL_HELPER" ]; then python3 "$WBL_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; world-bible is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$WBL_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 world-bible <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$WBL_HELPER" world-bible "$@"; exit $?
fi
echo "WARN: python3 unavailable — world-bible skipped; check inline that every world_fact carries a well-shaped locus, no rule both can and cannot the same thing, no power is priced two ways or free-then-costed, no edge has two distances on one axis, the happens-before order has no cycle, and the bible records stated facts only (never inventing or resolving world content). See docs/worldbuilding-bible.md."
exit 0
;;
registry-check)
# Project registry integrity (Project Addressability, Increment 2; docs/project-addressability.md):
# structural checks over a workspace-relative .apodictic/registry.json (apodictic.project_registry.v1
# + apodictic.project_entry.v1) — R1 invalid entry (envelope/per-entry schema, bad id/mode/JSON),
# R2 missing root (no dir = ERROR; no sidecar = WARN), R3 drift between a denormalized field and the
# canonical sidecar (WARN; ERROR --strict; sidecar always wins — the registry is a rebuildable cache),
# R4 duplicate id. Takes a registry file or a workspace dir containing .apodictic/registry.json.
# Delegates to scripts/registry_check.py; degrades to an advisory WARN without python3.
RGC_DIR=$(cd "$(dirname "$0")" && pwd)
RGC_HELPER="$RGC_DIR/registry_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RGC_HELPER" ]; then python3 "$RGC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; registry-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RGC_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 registry-check <registry.json | workspace_dir> [--strict] | --self-test"; exit 2; fi
python3 "$RGC_HELPER" registry-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — registry-check skipped; check inline that .apodictic/registry.json is valid apodictic.project_registry.v1, ids are unique, every root resolves, and denormalized mode/next_action match each project's sidecar (sidecar wins). See docs/project-addressability.md."
exit 0
;;
schema-coverage)
# Harness Contracts v2 (docs/harness-contracts-v2.md): the schema-coverage gate — prove disk
# reality matches the declarative schemas/_coverage.json binding table, so every apodictic.*.schema.json
# stays bound to a validator (C2 no-orphan, C3 no-phantom) and stays exercised against a real canonical
# file by --check-all (C4 binding-proven via grep of the BOUND script, C5 canonical-gate reachable),
# and the closed-key (additionalProperties:false) contract in each schema file agrees with the manifest
# (C1'). W1 (advisory; ERROR --strict) flags a dead non_artifact exclusion. --check-docs runs the
# advisory docs-no-re-list prose lint. Delegates to scripts/schema_coverage.py; degrades to advisory
# PASS without python3.
SCV_DIR=$(cd "$(dirname "$0")" && pwd)
SCV_HELPER="$SCV_DIR/schema_coverage.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SCV_HELPER" ]; then python3 "$SCV_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; schema-coverage is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SCV_HELPER" ]; then
python3 "$SCV_HELPER" schema-coverage "$@"; exit $?
fi
echo "WARN: python3 unavailable — schema-coverage skipped; check inline that every plugins/apodictic/schemas/apodictic.*.schema.json appears in schemas/_coverage.json bindings[] with a real validator + canonical file, and that each closed_keys:true schema carries additionalProperties:false. See docs/harness-contracts-v2.md."
exit 0
;;
lifecycle-node)
# State-driven dispatch (Project Addressability, Increment 3; docs/project-addressability.md):
# derive a bound project's lifecycle node from its Diagnostic_State.meta.json sidecar by a single
# first-match precedence (cold -> blocked_gate -> execution -> pre_writing -> submission ->
# revising -> diagnosed -> diagnosing). Total: every readable sidecar resolves to exactly one node,
# with `diagnosing` the catch-all default. A tested primitive for /start, /projects, and the
# Increment-4 loop dispatcher — no new stored state. `diagnosed` checks for a synthesis/editorial
# letter relative to the sidecar's project root (optional run_folder = extra search location);
# no letter -> diagnosing. Pure derivation (no FAIL verdict). Delegates to scripts/lifecycle_node.py.
LCN_DIR=$(cd "$(dirname "$0")" && pwd)
LCN_HELPER="$LCN_DIR/lifecycle_node.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$LCN_HELPER" ]; then python3 "$LCN_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; lifecycle-node is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$LCN_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 lifecycle-node <sidecar> [run_folder] | --self-test"; exit 2; fi
python3 "$LCN_HELPER" lifecycle-node "$@"; exit $?
fi
echo "WARN: python3 unavailable — lifecycle-node skipped; derive the node inline by the precedence in docs/project-addressability.md (cold -> blocked_gate -> execution -> pre_writing -> submission -> revising -> diagnosed -> diagnosing)."
exit 0
;;
argument-spine)
# Nonfiction Pre-Draft Pathway, Increments 1-3 + 5 (docs/nonfiction-pre-draft.md): structural checks
# over the apodictic.argument_spine.v1 block (the pre-draft argument plan that SEEDS the shared
# Argument_State.md), the apodictic.support_plan.v1 blocks (source/evidence map, §3), the
# apodictic.warrant_plan.v1 blocks (warrant pre-check, §4), and the apodictic.genre_profile.v1 block
# (the genre layer — holds a genre to its required-section skeleton). A1 invalid spine; A2 unseeded
# (spine must populate §1/§2 — signature); A3 thesis/C0 drift. Inc 2: A4 invalid support plan; A5
# dangling subclaim_id; A6 support unseeded (no §3 heading). Inc 3: A7 invalid warrant plan; A8
# dangling subclaim_id; A9 warrant unseeded (no §4 heading). Inc 5: B1 invalid genre profile; B2
# section unseeded (a declared genre section has no heading — signature); B3 genre/form mismatch
# (spine-present only; normalized); B4 duplicate genre profile. Advisory (ERROR --strict): W1
# anti-thesis echo (override argument-spine-antithesis), W2 bare assertion (a subclaim with no
# planned support), W3 implicit warrant for a HOSTILE audience (non-EXPLICIT / ABSENT-backed;
# override argument-spine-warrant), W4 thin genre skeleton (a canonical genre section omitted;
# override argument-spine-genre). Takes a run folder (globs Argument_State*.md) or explicit files.
# Delegates to scripts/argument_spine.py; degrades to an advisory WARN without python3.
AS_DIR=$(cd "$(dirname "$0")" && pwd)
AS_HELPER="$AS_DIR/argument_spine.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AS_HELPER" ]; then python3 "$AS_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-spine is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AS_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 argument-spine <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$AS_HELPER" argument-spine "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-spine skipped; check inline that the spine seeds Argument_State §1/§2, the C0 main claim carries the thesis, and the anti-thesis names a genuine opposing view. See docs/nonfiction-pre-draft.md."
exit 0
;;
scene-ethics)
# Scene-Ethics Plan (Nonfiction Pre-Draft, Increment 4; docs/nonfiction-pre-draft.md): structural
# checks over the apodictic.scene_ethics.v1 blocks — the writer's pre-draft ETHICAL plan for each
# identifiable real person depicted (consent_status, handling, fairness_check), distinct from the
# Legal Risk Register (legal exposure) and cross-referencing it via legal_ref. E1 invalid item,
# E2 duplicate id; W1 unresolved depiction (as-is + consent not-sought + no fairness rationale —
# the signature; override scene-ethics-unresolved EP-NN), W2 no legal cross-check (an as-is
# identifiable depiction with no legal_ref — check it against the Legal Risk Register; override
# scene-ethics-legalcheck EP-NN). W1/W2 advisory, ERROR under --strict. Takes a run folder
# (globs *_Scene_Ethics_Plan_*.md) or explicit files. Delegates to scripts/scene_ethics.py;
# degrades to an advisory WARN without python3.
SCE_DIR=$(cd "$(dirname "$0")" && pwd)
SCE_HELPER="$SCE_DIR/scene_ethics.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SCE_HELPER" ]; then python3 "$SCE_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; scene-ethics is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SCE_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 scene-ethics <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$SCE_HELPER" scene-ethics "$@"; exit $?
fi
echo "WARN: python3 unavailable — scene-ethics skipped; check inline that no identifiable person is depicted as-is without consent and without a fairness rationale, and that as-is depictions cross-check the Legal Risk Register. See docs/nonfiction-pre-draft.md."
exit 0
;;
reader-instrument)
# Beta-Reader Instrument (Workflows / revision-coach; docs/beta-reader-instrument.md): the upstream
# complement to feedback-triage. Structural checks over apodictic.reader_question.v1 blocks — reader
# questions seeded from the diagnosis's OPEN uncertainties (LOW/UNCERTAIN findings, Unresolved-Questions
# bullets, risk_if_fixed tradeoffs), read together with the Findings Ledger they target. B1 invalid
# item; B2 duplicate id; B3 provenance integrity (finding source -> a `targets` resolving to a real
# ledger finding; unresolved-question -> a `source_note` and no targets); B4 leading/invented content
# (firewall scan — override reader-instrument leading-question RQ-NN); B5 relitigating a LOCKED verdict
# (severity in {Must-Fix,Should-Fix} AND confidence in {HIGH,MEDIUM} — override how-to-fix RQ-NN); W1
# coverage. B4/B5/W1 advisory, ERROR under --strict. Takes a run folder (globs the instrument +
# *_Findings_Ledger_*.md) or explicit files. Delegates to scripts/reader_instrument.py; degrades to an
# advisory WARN without python3.
RDI_DIR=$(cd "$(dirname "$0")" && pwd)
RDI_HELPER="$RDI_DIR/reader_instrument.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RDI_HELPER" ]; then python3 "$RDI_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; reader-instrument is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RDI_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 reader-instrument <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$RDI_HELPER" reader-instrument "$@"; exit $?
fi
echo "WARN: python3 unavailable — reader-instrument skipped; check inline that each reader question is non-leading, content-neutral, sourced from a LOW/UNCERTAIN finding or an Unresolved Question (not a locked verdict), and carries an expected_signal. See docs/beta-reader-instrument.md."
exit 0
;;
manuscript-viz)
# Manuscript-Structure Visualizations (Horizon Tier 1; docs/manuscript-visualizations.md): a
# presentation layer that adds no analysis. Validates the apodictic.viz_manifest.v1 block (data
# copied verbatim from the Timeline Event-Ledger + apodictic.finding.v1 blocks) against its sources:
# E1 schema + no-visual-style allowlist, E2 provenance closure (scene_id -> Timeline row; finding id
# -> ledger; finding chapter == the conservative Chapter-N/Ch-N evidence_refs parse, else 'unplaced'),
# E3 every body Must-Fix appears, E4 byte-equal copy fidelity (no compute/embellish). W1 coverage
# advisory, ERROR under --strict. Charts 4-7 (Manuscript-Visualization Completion) add four OPTIONAL
# additive arrays; the M1 render-only chart is the NONFICTION CLAIM LADDER (claim_ladder[]) over the
# apodictic.argument_spine.v1 + apodictic.support_plan.v1 producers: X1 new-array schema + no scene
# axis (a scene_ids/scene_id/section key on a claim_ladder object is itself a failure), X5/X6
# claim-ladder provenance (claim_id is a declared spine subclaim via spine_subclaim_ids; label is the
# subclaim string minus its leading Cn token; support[] byte-copies support_plan.v1; an empty
# support[] only for a bare assertion), X7 no duplicate rung, X8 producer-present (no producer, no
# chart), W3 chart coverage. All four manifest arrays now have a producer: chart 5 (co_presence) over
# apodictic.scene_roster.v1 (X2 — byte-checks each co_presence[].scene_id against a roster entry + a
# Timeline row, every co_presence name against the scene's roster (canonical) names, the Timeline POV
# against the roster (cross-check), and the producer's anchor-non-empty); chart 6 (scene_functions)
# over apodictic.scene_function.v1 (X3 — manifest function == the producer's closed scene-turn
# Unit-Classification, non-empty anchor, provenance closure); and chart 4 (reveal_points) over
# apodictic.tension_point.v1 (X4 — manifest tension == the producer's closed 1-5 reader-intensity
# level, non-empty anchor, provenance closure — keyed directly on scene_id, no evidence_ref
# resolution). The one still-producer-gated chart is 7-fiction's beat-map (no manifest array here;
# awaits apodictic.story_spine.v1). --require-block makes a missing/
# invalid manifest a hard failure (the canonical-example gate uses it so it can't pass vacuously). The
# severity->encoding map (and the co-presence weight->chord-thickness band, the scene-function colour
# band, the tension level->y-position map) is hardcoded in the render-only SVG layer (charts 1-3 + the
# claim ladder + the co-presence network + the scene-function heatmap + the tension timeline),
# not the manifest, so a run cannot recolor a Must-Fix. Takes a run folder (globs the manifest +
# Timeline + Findings Ledger + Argument_State spine + Scene_Roster + Scene_Function + Tension producers)
# or explicit files. Delegates to
# scripts/viz_manifest.py; degrades to an advisory WARN without python3. (`viz_manifest.py render ...`
# emits the HTML.)
MVZ_DIR=$(cd "$(dirname "$0")" && pwd)
MVZ_HELPER="$MVZ_DIR/viz_manifest.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$MVZ_HELPER" ]; then python3 "$MVZ_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; manuscript-viz is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$MVZ_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 manuscript-viz <run_folder|files...> [--strict] [--require-block] | --self-test"; exit 2; fi
python3 "$MVZ_HELPER" manuscript-viz "$@"; exit $?
fi
echo "WARN: python3 unavailable — manuscript-viz skipped; check inline that the viz_manifest copies Timeline/finding values verbatim, carries no visual-style fields, places findings only by the Chapter-N evidence_refs parse (else 'unplaced'), includes every Must-Fix, and that any claim_ladder[] copies the argument_spine subclaims + support_plan coverage verbatim with no scene axis. See docs/manuscript-visualizations.md."
exit 0
;;
annotated-manuscript)
# Annotated-Manuscript Deliverable (Horizon -> Planned; docs/annotated-manuscript.md): the editorial
# letter's findings anchored in the margin of an immutable manuscript SNAPSHOT. Validates the
# apodictic.annotation.v1 manifest + the annotated copy: A1 schema + finding_id uniqueness, A2
# no-mutation (delete every {>> ... <<} CriticMarkup span == the bound snapshot, byte-for-byte; a
# two-sided sigil precondition before render), A3 anchor integrity (line-range/section/chapter must
# resolve to a unique heading; an honest `document` note is fine), A4 the rendered comment-span
# multiset equals the manifest comment multiset both directions — every body Must-Fix renders, and
# no un-manifested/authored span is present (reusing finding_trace's ledger inventory), A5 each
# comment is a verbatim, inline-CriticMarkup-safe projection of the finding's fields. A6 (Increment 2)
# quote integrity: a `quote` anchor's text (from a finding's optional verbatim evidence_quote) occurs
# in the snapshot verbatim and EXACTLY once, the offsets pin it, and it matches the finding — the
# fabricated/mis-placed-quote failure A3 cannot see. W1 coverage / Timeline-boundary drift
# advisory, ERROR under --strict (override `<!-- override: annotation-coverage F-... -->`). Comments
# only — never prose mutation (the Firewall). Takes a run folder (globs snapshot + manifest +
# annotated copy + Findings Ledger + Timeline) or explicit files. Delegates to
# scripts/annotation_manifest.py; degrades to an advisory WARN without python3.
# (`annotation_manifest.py build <run_folder>` generates the manifest + annotated copy from the
# snapshot + ledger + Timeline; `render <manifest> <snapshot>` re-renders the annotated copy.)
AM_DIR=$(cd "$(dirname "$0")" && pwd)
AM_HELPER="$AM_DIR/annotation_manifest.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AM_HELPER" ]; then python3 "$AM_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; annotated-manuscript is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AM_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 annotated-manuscript <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$AM_HELPER" annotated-manuscript "$@"; exit $?
fi
echo "WARN: python3 unavailable — annotated-manuscript skipped; check inline that the annotated copy mutates no prose (deleting every {>> ... <<} span reproduces the snapshot), each comment is a verbatim finding-field projection, and every Must-Fix appears as a margin comment span. See docs/annotated-manuscript.md."
exit 0
;;
reader-contract-outline)
# Reader-Contract Reverse Outline Deliverable (SPEC v3.1; docs/reader-contract-outline.md): the book
# scene by scene, mapped against the reader contract it implicitly makes. A byte-deterministic
# PROJECTION of four inputs (Pass 0 reverse outline, Contract, Findings Ledger, and the gated
# Contract Map) — the model's ONLY authored bytes anywhere in the deliverable are the Map's ids-only
# localization (apodictic.contract_map.v1, closed-key). Validates R1 spine projection round-trips to
# Pass 0 (ids+count+text verbatim), R2 two-sided contract-field fidelity (none dropped/invented,
# empty fields render their literal state), R3 the rendered Contract Map <-> Map-block bijection with
# every evidence line byte-matching the cited scene's Pass 0 "what the reader now knows" line, R4 no
# fabricated gap (each gap cell is `none logged` or the verbatim Ledger projection), R5 no
# untranslated framework shorthand in the deliverable (the letter's author-facing families), R6
# contract coverage (advisory WARN; ERROR --strict; the READER-PROMISE split-completeness question,
# override `<!-- override: reader-contract-coverage — ... -->` via the override_marker SSoT), and R7
# map integrity (Mode-11 untrusted input: closed-key schema, inputs.*_sha256 bound to the staged
# artifacts so a stale Map fails loudly, every clause_text a verbatim substring of its named Contract
# source_field, the clause denominator recomputed from the Contract — never trusted from the Map).
# Takes a run folder (globs Pass 0 + Contract + Ledger + Map + the rendered outline) or explicit
# files. Delegates to scripts/reader_contract_outline.py; degrades to an advisory WARN without
# python3. (`reader_contract_outline.py build <staging>` R7-gates the Map then projects the outline.)
RCO_DIR=$(cd "$(dirname "$0")" && pwd)
RCO_HELPER="$RCO_DIR/reader_contract_outline.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RCO_HELPER" ]; then python3 "$RCO_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; reader-contract-outline is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RCO_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 reader-contract-outline <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$RCO_HELPER" reader-contract-outline "$@"; exit $?
fi
echo "WARN: python3 unavailable — reader-contract-outline skipped; check inline that §Scene Spine round-trips to the Pass 0 reverse outline verbatim, §The Reader Contract projects every Contract schema field two-sided, each Contract-Map evidence line byte-matches the cited scene's Pass 0 'what the reader now knows' line, each gap cell is `none logged` or the verbatim Ledger entry, and the Map's inputs.*_sha256 bind to the staged Pass 0/Contract/Ledger. See docs/reader-contract-outline.md."
exit 0
;;
calibration-honesty)
# Cross-surface uncalibrated-band calibration-honesty guard (SPEC narrative-decision-residue Inc c;
# docs/calibration-honesty.md). Both decision-audit surfaces (narrative_decision_audit / StoryScope,
# argument_decision_audit / ArgScope) ship an `uncalibrated` verdict band with null thresholds; the
# discipline — render the band as PROVENANCE only, never as a calibrated verdict — was enforced
# entirely in prose. This arm mechanizes it: a WHOLE-LETTER, per-PARAGRAPH scan (D2 — region-scoping
# was a false foundation: the only per-audit sectioner is appendix-only, decision-audit findings land
# in the unscoped synthesis body, and the canonical fixture carries no decision-audit region) that
# flags a paragraph only when a CLAIM SHAPE (CS1 band-placement / CS2 calibrated-verdict / CS3
# threshold-claim / CS4 aggregate-as-verdict) matches AND no QUALIFIER (uncalibrated / provenance-only
# / advisory / not-a-verdict / …) is co-present in that paragraph — bare token presence never fires,
# which is what keeps the mandated boilerplate + vendored bundle labels safe. WARN by default, ERROR
# under --strict (D4 — the honest class: an OPEN natural-language co-presence check, content_advisory
# W1 / stance-consistency F4, failure direction toward NOT firing). D5 disjoint from severity-floor:
# the submission-readiness band vocabulary (Strong Fit / Highest Band / …) is excised from CS1, so a
# legal readiness verdict does not fire here. Per-paragraph override via the shared override_marker
# SSoT (`<!-- override: calibration-honesty — <why> -->`). Cross-surface + surface-agnostic (D6).
# Delegates to scripts/calibration_honesty.py; degrades to an advisory WARN without python3.
CH_DIR=$(cd "$(dirname "$0")" && pwd)
CH_HELPER="$CH_DIR/calibration_honesty.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$CH_HELPER" ]; then python3 "$CH_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; calibration-honesty is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$CH_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 calibration-honesty <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$CH_HELPER" calibration-honesty "$@"; exit $?
fi
echo "WARN: python3 unavailable — calibration-honesty skipped; check inline that no editorial-letter paragraph renders an uncalibrated SETEC decision-audit band as a verdict (a 'scores in the X band' / 'above the threshold' / 'aggregate score confirms …' claim) without an uncalibrated/provenance-only/advisory qualifier co-present in that paragraph. See docs/calibration-honesty.md."
exit 0
;;
crosslink)
# Letter <-> margin cross-links (Annotated-Manuscript Increment 3; docs/annotated-manuscript.md
# §Increment 3): the symmetric mirror of the annotated copy, pointed at the editorial letter. A
# crosslink render injects a CriticMarkup back-link span ({>>-> marked-up copy: <id> @ kind:value<<})
# after each letter `<!-- finding: F-... -->` marker whose finding has a manifest annotation, copying
# the anchor VERBATIM from the gated manifest. The letter is a "second snapshot": the same reverse
# transform + two-sided sigil precondition prove no letter mutation. Validates X1 forward link (each
# margin comment carries (See letter §id)), X2 reverse-link consistency (back-link anchor == manifest,
# no drift), X3 no dangling either way (no phantom back-link; no missing reverse link), X4 no letter
# mutation; W1 annotated-but-uncited is advisory (ERROR under --strict, override
# `<!-- override: crosslink-uncited F-... -->`). Takes a run folder (globs editorial letter + manifest
# + crosslinked letter) or explicit files. Delegates to scripts/crosslink.py; degrades to advisory
# WARN without python3. (`crosslink.py render <run_folder>` writes the crosslinked letter.)
XL_DIR=$(cd "$(dirname "$0")" && pwd)
XL_HELPER="$XL_DIR/crosslink.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$XL_HELPER" ]; then python3 "$XL_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; crosslink is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$XL_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 crosslink <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$XL_HELPER" crosslink "$@"; exit $?
fi
echo "WARN: python3 unavailable — crosslink skipped; check inline that each letter finding marker has a back-link to the manifest anchor (no drift), no phantom/missing back-links, and deleting every {>> ... <<} span reproduces the letter. See docs/annotated-manuscript.md."
exit 0
;;
escalation-check)
# Adaptive Mid-Run Mode Escalation detector (docs/adaptive-mode-escalation.md): a
# CONDITION-TRIGGERED checkpoint after Tier 1 that compares revealed complexity (POV count,
# nonlinear timeline, belief/orientation density, Tier-1 finding count from the ledger) against
# the preflight estimate and recommends escalating the execution mode before Tier 2. The
# symmetric case: when no trigger fires and every signal is in a 'clearly simple' band, it
# recommends DE-escalating an over-provisioned expensive mode (hybrid/swarm) down to sequential
# (conservatively — a missing/malformed signal blocks it). Advisory by default (a recommendation,
# never automatic); --strict exits 1 on either recommendation. Delegates to
# scripts/escalation_check.py; degrades to advisory WARN.
EC_DIR=$(cd "$(dirname "$0")" && pwd)
EC_HELPER="$EC_DIR/escalation_check.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$EC_HELPER" ]; then python3 "$EC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; escalation-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$EC_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 escalation-check <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$EC_HELPER" escalation-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — escalation-check skipped; evaluate the escalation triggers inline (pov_count>3, nonlinear timeline, belief>5/orientation>3, Tier-1 findings>20). See docs/adaptive-mode-escalation.md."
exit 0
;;
dispatch-record)
# Model-Capacity Exploitation M1 — dispatch observability validator (docs/model-capacity-
# dispatch-log.md). A run that dispatched delegated agents records WHICH model each dispatched
# step was issued to in the additive `dispatch_log` sidecar array (Diagnostic_State.meta.json) —
# the join key between model identity and every outcome signal already on disk. PROVENANCE
# HONESTY (binding): a `dispatch-derived` entry attests the dispatch INSTRUCTION — parent-
# requested, NOT platform-verified; no host API attests which model actually served the
# subagent (deliberately weaker than synthesis_coverage's manifest-reconciled V4). Checks:
# R1 presence (advisory-first; key-ABSENT = pre-adoption grandfather / silent PASS, present-
# but-[] = a post-adoption recording failure that fires). R2 coverage against the pass-artifact
# glob family + synthesis-letter globs, per the satisfaction map (pass<N>->Pass N; pass0+1->
# BOTH Pass0+Pass1; all-passes->every pass artifact AND exclusive; synthesis->letter; deferred
# ids audit:*/prerequisite:*/refutation recorded-but-not-reconciled); artifact-with-no-entry =
# FAIL under --strict / WARN otherwise, entry-with-no-artifact = WARN never FAIL. R3 tag
# vocabulary parsed from the output-structure.md model-tag table (SSoT-by-reference; literal
# `unknown` is a sanctioned PASS; a ZERO-row table parse EXITS 2 model-tag-table-unparseable,
# never a vacuous accept-everything). R4 shape (modes/provenance enums, strictly-increasing
# seq, malformed/dup = FAIL, unknown step kind = WARN; NO mode-based provenance FAIL — a
# no-shell host legitimately records all-`declared` under execution_mode: sequential). R5
# escalation cross-check from the log + last_session.execution_mode (at most one mode
# transition; final entry mode == last_session.execution_mode) — WARN-first. R1/R5 and the R2
# shrink direction are advisory-first for one release (--strict promotes). `--report
# <project_dir>` is a read-only cross-run scan (gates nothing) that surfaces the M2 demand
# signal — quality_risk_override records (the CR-6 detectability fold). Delegates to
# scripts/dispatch_record.py; degrades to an advisory WARN without python3.
DR_DIR=$(cd "$(dirname "$0")" && pwd)
DR_HELPER="$DR_DIR/dispatch_record.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$DR_HELPER" ]; then python3 "$DR_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; dispatch-record is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$DR_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 dispatch-record <run_folder> [--strict] | dispatch-record --report <project_dir> | --self-test"; exit 2; fi
python3 "$DR_HELPER" dispatch-record "$@"; exit $?
fi
echo "WARN: python3 unavailable — dispatch-record skipped; verify inline that any dispatch_log in Diagnostic_State.meta.json carries one {seq, step, model_tag, execution_mode, max_turns, provenance} entry per dispatched step (pass<N>/pass0+1/synthesis/all-passes), model_tag is in the output-structure.md table or `unknown`, seq strictly increases, and the final entry's execution_mode matches last_session.execution_mode. See docs/model-capacity-dispatch-log.md."
exit 0
;;
argument-groundtruth-check)
# Argument Benchmark ground-truth answer-key validator (docs/argument-benchmark-spec.md
# §Mechanical validator): GT1-GT8 presence; DC code-namespace resolution; GT2 locus<->code
# consistency; GT7 warrant verdict (the warrant-verdict enum introduced in GT schema v0.2.0; retired-label/token rejection);
# GT8 premise-plausibility flags (leading-token parse + flag-type/Firewall check); Check 6 the
# GT schema v0.3.0 Reliability ledger (per-anchor status + decision-use; gate requires a licensed status;
# provisional confirm/report-only; low-agreement report-only; exact GT1-GT8 coverage; the
# stale-heading cross-check that consumes the formerly-dead PROVISIONAL bool); Check 7 the
# matched-pair provenance fields (Matched-pair member / Paired-with — the leading-token member
# parse, the clean-side derivation-record + GT2 gates, and slug self-consistency; a deliberate
# stricter superset of fiction's pairing grammar, absent-both-fields = unpaired legacy no-op).
# This case also hosts the round-record conformance mode (--round-record <record.md> --fixtures-dir <dir>):
# every booked ENGINE-fault must cite an anchor whose ledger licenses it (gate: any; confirm:
# OVER-FIRE only; report: none). And the repair-diff acceptance gate
# (--repair-diff <broken/fixture.md> <clean/fixture.md> <clean/groundtruth.md>): the clean twin
# must be the broken fixture with insertions ONLY, mapping 1:1 to the clean key's enumerated
# repair loci. Delegates to scripts/argument_groundtruth.py; degrades to an advisory WARN without
# python3 (the GT contract is prose in the template + spec).
AGT_DIR=$(cd "$(dirname "$0")" && pwd)
AGT_HELPER="$AGT_DIR/argument_groundtruth.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AGT_HELPER" ]; then python3 "$AGT_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-groundtruth-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AGT_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 argument-groundtruth-check <groundtruth_file> | --round-record <record.md> --fixtures-dir <dir> | --repair-diff <broken/fixture.md> <clean/fixture.md> <clean/groundtruth.md> | --self-test"; exit 2; fi
python3 "$AGT_HELPER" argument-groundtruth-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-groundtruth-check skipped; the GT template + spec define the contract. Install python3 for the mechanical check."
exit 0
;;
argument-crosswalk-check)
# Argument-taxonomy layer-boundary crosswalk validator (R4A; docs/adr/0001-argument-layer-boundary.md):
# wellformedness gate over evals/argument-crosswalk/crosswalk.json — membership completeness against a
# DERIVED registry set (verdict/flag/FM-A owners imported from argument_groundtruth.py; the 46 DC codes
# + 8 scheme hints parsed structurally from dialectical-clarity.md — never a 4th hardcoded copy), family
# well-typing, the closed cardinality enum, the unmapped<->no-targets equivalence, a non-empty rationale
# on every unmapped row, a provenance locator on every populated target, a global non-injectivity floor,
# closed external-ref value-spaces, and the OB5/FM-A20 count tripwires. Certifies SHAPE only — mapping
# correctness is human/Codex-adjudicated (see the ADR firewall). Delegates to scripts/argument_crosswalk.py;
# degrades to an advisory WARN without python3 (the ADR + README define the contract in prose).
AXW_DIR=$(cd "$(dirname "$0")" && pwd)
AXW_HELPER="$AXW_DIR/argument_crosswalk.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AXW_HELPER" ]; then python3 "$AXW_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-crosswalk-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AXW_HELPER" ]; then
python3 "$AXW_HELPER" argument-crosswalk-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-crosswalk-check skipped; the ADR + README define the contract. Install python3 for the mechanical check."
exit 0
;;
argument-aif-export)
AIF_DIR=$(cd "$(dirname "$0")" && pwd)
AIF_HELPER="$AIF_DIR/argument_aif.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AIF_HELPER" ]; then python3 "$AIF_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-aif is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AIF_HELPER" ]; then
python3 "$AIF_HELPER" export "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-aif skipped; docs/argument-aif-export.md defines the contract."
exit 0
;;
argument-aif-check)
AIF_DIR=$(cd "$(dirname "$0")" && pwd)
AIF_HELPER="$AIF_DIR/argument_aif.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AIF_HELPER" ]; then python3 "$AIF_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-aif is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AIF_HELPER" ]; then
python3 "$AIF_HELPER" check "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-aif skipped; docs/argument-aif-export.md defines the contract."
exit 0
;;
argument-reconstruction)
AGR_DIR=$(cd "$(dirname "$0")" && pwd)
AGR_HELPER="$AGR_DIR/approval_graph.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AGR_HELPER" ]; then python3 "$AGR_HELPER" --self-test; exit $?; fi
echo "ERROR: argument-reconstruction requires python3 and approval_graph.py" >&2; exit 1
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AGR_HELPER" ]; then
python3 "$AGR_HELPER" "$@"; exit $?
fi
echo "ERROR: argument-reconstruction requires python3 and approval_graph.py" >&2
exit 1
;;
argument-agd)
# AGD Move Audit validator (R3A; craft/argument-agd-audit.md): validates an Argument_State
# §10.9 block — coverage manifest, typed M-records, the total family×challenge×result matrix,
# the neutrality firewall (candidates only on failed function; DISAPPEARING whitelist), the
# DERIVED 62-code candidate namespace (DC minus AT1-AT4 via argument_crosswalk's scoped parse,
# + FM-A1-20), the DISCOUNTING cross-ref contract, and (with --source) normalized-substring
# Source-anchor resolution. Delegates to scripts/argument_agd.py; degrades to an advisory WARN
# without python3 (the audit doc defines the contract in prose).
AGD_DIR=$(cd "$(dirname "$0")" && pwd)
AGD_HELPER="$AGD_DIR/argument_agd.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AGD_HELPER" ]; then python3 "$AGD_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; argument-agd is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AGD_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 argument-agd <argument_state.md> [--source <source.md>] [--scan <agd_move_scan.json>] [--strict] | --self-test"; exit 2; fi
python3 "$AGD_HELPER" argument-agd "$@"; exit $?
fi
echo "WARN: python3 unavailable — argument-agd skipped; craft/argument-agd-audit.md defines the contract. Install python3 for the mechanical check."
exit 0
;;
fiction-groundtruth-check)
# Fiction Benchmark ground-truth answer-key validator (docs/fiction-benchmark-spec.md
# §Mechanical validator): FGT1-FGT7 presence; the multi-lane tag discipline (gt_class:C never
# gates, gate requires deterministic/panel_confirmed, Lane-2 needs a band + alpha_metric);
# broken-member completeness (plant record, well-shaped locus, defect family, Paired-with sibling);
# the OPEN-namespace expected-surface shape + family consistency (F-P<n> open by shape/family,
# CF/SP disjoint cross-artifact ids recognized only for continuity/reveal, the adapted fiction-grammar
# decoy masks); FGT4 severity tokens (via severity_vocab, presence-delta regime); FGT7 classification.
# KEY-CONFORMANCE ONLY — never a semantic judge, never scores an engine run. Delegates to
# scripts/fiction_groundtruth.py; degrades to an advisory WARN without python3 (the GT contract is
# prose in the template + spec).
FGT_DIR=$(cd "$(dirname "$0")" && pwd)
FGT_HELPER="$FGT_DIR/fiction_groundtruth.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$FGT_HELPER" ]; then python3 "$FGT_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; fiction-groundtruth-check is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$FGT_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 fiction-groundtruth-check <groundtruth_file> | --self-test"; exit 2; fi
python3 "$FGT_HELPER" fiction-groundtruth-check "$@"; exit $?
fi
echo "WARN: python3 unavailable — fiction-groundtruth-check skipped; the GT template + spec define the contract. Install python3 for the mechanical check."
exit 0
;;
agreement-alpha)
# Krippendorff's alpha inter-rater reliability CLI (pure stdlib) — the mechanical half of the
# Argument/Fiction Benchmark agreement-as-license promotion workflow (docs/argument-benchmark-spec.md
# §GT schema; the psychometric frame: inter-rater agreement LICENSES a label, it never scores the
# engine). Computes nominal + ordinal Krippendorff's alpha over a tidy `rater,unit,value` CSV with
# a >=1000-resample UNIT-level bootstrap 95% CI (Hayes & Krippendorff 2007), a fixed default seed
# (--seed / --resamples overrides; --resamples may only RAISE the 1000 floor), and the D_e=0 ->
# alpha=UNDEFINED + WARN degenerate guard. Licensing is on the CI LOWER BOUND (the small-n
# false-promotion guard), applied by the M2 round, not this tool. No --check-all corpus block:
# panel ratings live outside git (Dropbox-only), so there is no in-repo ratings fixture to scan.
# Delegates to scripts/agreement_alpha.py; degrades to an advisory WARN without python3.
AA_DIR=$(cd "$(dirname "$0")" && pwd)
AA_HELPER="$AA_DIR/agreement_alpha.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$AA_HELPER" ]; then python3 "$AA_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; agreement-alpha is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$AA_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 agreement-alpha <ratings.csv> [--metric nominal|ordinal] [--seed <int>] [--resamples <int>=1000] | --self-test"; exit 2; fi
python3 "$AA_HELPER" agreement-alpha "$@"; exit $?
fi
echo "WARN: python3 unavailable — agreement-alpha skipped; install python3 for the mechanical Krippendorff's-alpha computation. Ratings contract: a rater,unit,value CSV; see docs/argument-benchmark-spec.md."
exit 0
;;
diagnostic-vocabulary)
# Diagnostic Vocabulary Mode teaching-aid contract (docs/diagnostic-vocabulary.md): when the
# Vocabulary Guide declares `<!-- mode: diagnostic-vocabulary -->` (operator:facilitator),
# enforce V1 Glossary present (>=3 entries), V2 entries defined, V3 >=3 entries grounded in the
# manuscript, V4 Discussion Prompts (>=3, all questions); W1 author-directed prescription leak is
# advisory (ERROR under --strict). A file WITHOUT the marker is a no-op pass, so this is safe over
# any file. Delegates to scripts/diagnostic_vocabulary.py; degrades to an advisory WARN without python3.
DV_DIR=$(cd "$(dirname "$0")" && pwd)
DV_HELPER="$DV_DIR/diagnostic_vocabulary.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$DV_HELPER" ]; then python3 "$DV_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; diagnostic-vocabulary is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$DV_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 diagnostic-vocabulary <vocab_guide|run_folder> [--strict] | --self-test"; exit 2; fi
python3 "$DV_HELPER" diagnostic-vocabulary "$@"; exit $?
fi
echo "WARN: python3 unavailable — diagnostic-vocabulary skipped; if the file declares diagnostic-vocabulary mode, verify inline that it carries a grounded Glossary (>=3 entries) and a Discussion Prompts section (>=3 questions). See docs/diagnostic-vocabulary.md."
exit 0
;;
editor-scaffolding)
# Editor Scaffolding operator-mode presentation contract (docs/editor-scaffolding.md): when
# the editorial letter declares `<!-- mode: editor-scaffolding -->` (operator:editor),
# enforce the editor-facing reframe — E1 Editor Brief addressee, E2 What-You-Might-Have-Missed
# blind-spot section, E3 Intervention Menu (prescription deferred to the human editor),
# E4 severity vocabulary preserved; W1 author-directed prescription leak is advisory (ERROR
# under --strict). A letter WITHOUT the marker is a no-op pass, so this is safe over any
# letter. DUAL-OUTPUT: `editor-scaffolding --dual <editor_letter> <author_letter>` validates
# one diagnosis emitted as BOTH letters — D1 editor side (E1-E4), D2 author register (no editor
# marker / no editor-only sections + a Revision Checklist anchor), D3 top-severity-band match.
# PER-PASS: `editor-scaffolding --per-pass <pass_artifact>` applies the same reframe to an
# individual PASS artifact (not the letter) — P1 Editor Note addressee, P2 What-You-Might-Have-
# Missed blind-spot, W1 prescription firewall; a pass has no checklist so E3/E4 have no per-pass
# analog. Delegates to scripts/editor_scaffolding.py; degrades to an advisory WARN without python3.
ES_DIR=$(cd "$(dirname "$0")" && pwd)
ES_HELPER="$ES_DIR/editor_scaffolding.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$ES_HELPER" ]; then python3 "$ES_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; editor-scaffolding is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$ES_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 editor-scaffolding <editorial_letter|run_folder> [--strict] | editor-scaffolding --dual <editor_letter> <author_letter> [--strict] | editor-scaffolding --per-pass <pass_artifact> [--strict] | --self-test"; exit 2; fi
python3 "$ES_HELPER" editor-scaffolding "$@"; exit $?
fi
echo "WARN: python3 unavailable — editor-scaffolding skipped; if the letter (or pass artifact) declares editor-scaffolding mode, verify inline that a letter carries an Editor Brief, a 'What You Might Have Missed' section, an Intervention Menu, and surviving severity tokens — or that a --per-pass artifact carries an Editor Note and a 'What You Might Have Missed' section. See docs/editor-scaffolding.md."
exit 0
;;
results-guide)
# Results Guide navigation-index integrity (Writer-Question Surface Hardening #5;
# SKILL.md §Results Guide Artifact). The Results Guide (`*_Results_Guide_*.md`) is the first
# file after the editorial letter — a plain-language map from each writer question the run
# produced to the run-folder artifacts behind it. It POINTS, it never DIAGNOSES. One arm, three
# checks, R2 load-bearing: R1 membership (every `### question` heading is a canonical §3 User
# Question from pass-dependencies.md — an invented block is the defect; a run that produced only
# some blocks is legal), R2 referential integrity (LOAD-BEARING — every backtick .md/.json
# citation resolves to a run-folder file; an un-substituted `[…]` placeholder or a dangling
# citation ERRORs; `/coach` / `/audit [name]` command tokens are exempt via the extension guard),
# R3 hygiene (no Must/Should/Could-Fix severity leak, no apodictic:finding block — it must not
# masquerade as a second letter). Takes a run folder (globs *_Results_Guide_*.md; citations
# resolve flat) or explicit files (the guide + an optional run-folder dir). R1 degrades to a skip
# when §3 is unavailable; an unreadable run folder fails closed. Delegates to
# scripts/results_guide.py; degrades to an advisory WARN without python3.
RG_DIR=$(cd "$(dirname "$0")" && pwd)
RG_HELPER="$RG_DIR/results_guide.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$RG_HELPER" ]; then python3 "$RG_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; results-guide is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$RG_HELPER" ]; then
if [ $# -lt 1 ]; then echo "Usage: $0 results-guide <run_folder|files...> [--strict] | --self-test"; exit 2; fi
python3 "$RG_HELPER" results-guide "$@"; exit $?
fi
echo "WARN: python3 unavailable — results-guide skipped; perform the citation trace inline: every backtick .md/.json filename in the guide resolves to a run-folder file (no un-substituted [placeholder]); every '### question' heading is a §3 User Question; no Must/Should/Could-Fix token; no apodictic:finding block. See SKILL.md §Results Guide Artifact."
exit 0
;;
position-pair-register)
# Position-Pair Register consumer firewall (stance-consistency PR 2; SPEC v4+v5). The consumer
# renders SETEC's `position_pair_register` envelope — passage PAIRS that address the SAME question,
# relation-free, in document order — to a markdown register and presents it. This arm carries the
# no-relation posture MECHANICALLY over three inputs (the register artifact, the JSON envelope, and
# the manuscript the consumer holds): Q1 a two-layer recursive banned-KEY walk over the envelope
# (relation keys never legitimate anywhere; generic verdict keys scoped to results.pairs — KEYS
# only, since claim_license VALUES legitimately carry relation words), Q2 a verbatim re-check of
# every quote against the manuscript with the F1 punctuation-fold (a fabricated/paraphrased quote
# DROPS the pair with an inspectable log line + a counted pairs_dropped_quote_mismatch disclosure +
# WARN; --strict promotes to FAIL — a drop is a disclosure, not an error), the A3/X-gate (no
# Must/Should/Could-Fix token, no apodictic:finding block — same content_advisory firewall), the F5
# presentation-prose gate (the consumer's OWN framing text carries no relation vocabulary; the
# `>`-blockquote evidence lines are exempt — the author's quotes may carry such words), and a
# document-order check (the artifact must present pairs in envelope order — re-ranking is a
# judgment channel the posture forbids). Explicit file args only (no run-folder resolution).
# Delegates to scripts/position_pair_gates.py; degrades to an advisory WARN without python3.
PPR_DIR=$(cd "$(dirname "$0")" && pwd)
PPR_HELPER="$PPR_DIR/position_pair_gates.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$PPR_HELPER" ]; then python3 "$PPR_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; position-pair-register is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$PPR_HELPER" ]; then
if [ $# -lt 3 ]; then echo "Usage: $0 position-pair-register <artifact.md> <envelope.json> <manuscript> [--strict] | --self-test"; exit 2; fi
python3 "$PPR_HELPER" position-pair-register "$@"; exit $?
fi
echo "WARN: python3 unavailable — position-pair-register skipped; verify inline that the envelope carries no relation/verdict KEY (contradiction/opposes/conflict/tension/stance/… anywhere; verdict/label/score/relation inside results.pairs), every quote is a verbatim substring of the manuscript, the register carries no Must/Should/Could-Fix token and no apodictic:finding block, its framing prose (outside the '>' quote lines) carries no relation vocabulary, and the pairs are in document order. See position-pair-register.md."
exit 0
;;
stance-calibration)
# Argument-register / rhetorical-stance Triage record gate. Shape + exact Argument_State
# joins only; the validator never decides whether a move is rhetorically earned.
SC_DIR=$(cd "$(dirname "$0")" && pwd)
SC_HELPER="$SC_DIR/stance_calibration.py"
if [ "${1:-}" = "--self-test" ]; then
if command -v python3 >/dev/null 2>&1 && [ -f "$SC_HELPER" ]; then python3 "$SC_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; stance-calibration is advisory without it)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$SC_HELPER" ]; then
if [ $# -lt 3 ]; then echo "Usage: $0 stance-calibration <findings_ledger.md> --argument-state <Argument_State.md> | --self-test"; exit 2; fi
python3 "$SC_HELPER" stance-calibration "$@"; exit $?
fi
echo "WARN: python3 unavailable — stance-calibration skipped; verify the optional register/stance/calibration_effect fields and exact cash_out_ref joins inline."
exit 0
;;
validator-conventions)
# Fleet meta-linter (docs/validator-conventions.md): a validator that validates the validators.
# M1 every AGG_VALIDATORS entry has a dispatcher case that handles --self-test; M2 no validator
# classifies inputs by a raw marker scan/membership op on a literal apodictic:<type> (resolvers
# must classify on parsed blocks — the _has_block / art.parse_blocks idiom — the signature
# anti-pattern of the 2026-06-20 resolver-hardening sweep); M3 the advertised count is DERIVED from
# AGG_VALIDATORS (never hand-typed); M4 every *.schema.json filename stem in the resolved schema dir
# is referenced by some validator (degrades to WARN if the resolver is unavailable); M5 no validator
# detects an override marker by a bare "<!-- override:" substring scan OR a local compiled/inline
# override regex (the override-marker sibling of M2 — use override_marker.has_override /
# override_targets / override_payloads / the _has_override bash helper); M6 no validator builds a
# local code-span / fence stripper (delegate to override_marker.strip_code_spans). Mechanizes the
# conventions that manual review left to drift. Reads validate.sh + the sibling *.py from its own
# dir. Delegates to scripts/meta_lint.py; degrades to an advisory WARN without python3.
MTL_DIR=$(cd "$(dirname "$0")" && pwd)
MTL_HELPER="$MTL_DIR/meta_lint.py"
if [ "${1:-}" = "--self-test" ]; then
# Bash-side regression for the shared `_has_override` helper (M5's companion: M5 GATES the
# anti-pattern, this proves the hardened REPLACEMENT). The Python override_marker.has_override is
# covered by each validator's --self-test (severity-floor / quality-risk / etc. decoy+suffix
# cases); this covers the bash arm — which the Python self-tests never reach — including the
# fenced-block decoy (Group 4: bash used to be wrongly MORE permissive there).
HO_R=0; HO_S="severity-floor-weak-axis"
_ho_case() { # <label> <expect found|miss> <body>
if printf '%b' "$3" | _has_override "$HO_S"; then _g=found; else _g=miss; fi
if [ "$_g" = "$2" ]; then echo " ho_$1: OK ($_g)"; else echo " ho_$1: FAIL (got $_g want $2)"; HO_R=1; fi
}
_ho_case genuine_emdash found "<!-- override: $HO_S — reason -->\n"
_ho_case genuine_noreason found "<!-- override: $HO_S -->\n"
_ho_case nospace_dash found "<!-- override: ${HO_S}—reason -->\n"
_ho_case flex_whitespace found "<!-- override: $HO_S -->\n"
_ho_case suffix_collision miss "<!-- override: $HO_S-but-not-really — decoy -->\n"
_ho_case inline_codespan_decoy miss "Use \`<!-- override: $HO_S -->\` here.\n"
_ho_case fenced_block_decoy miss "before\n\`\`\`\n<!-- override: $HO_S -->\n\`\`\`\nafter\n"
_ho_case indented_fenced_decoy miss "x\n \`\`\`\n<!-- override: $HO_S -->\n \`\`\`\ny\n"
_ho_case multi_backtick_decoy miss "\`\`<!-- override: $HO_S -->\`\`\n"
_ho_case tilde_fence_decoy miss "before\n~~~\n<!-- override: $HO_S -->\n~~~\nafter\n"
_ho_case tilde_with_backtick_line_decoy miss "~~~\n\`\`\`\n<!-- override: $HO_S -->\n\`\`\`\n~~~\n"
_ho_case multiline_inline_decoy miss "a \`open\n<!-- override: $HO_S -->\nclose\` b\n"
if [ "$HO_R" -ne 0 ]; then echo "Self-test: FAIL (_has_override bash regression)"; exit 1; fi
if command -v python3 >/dev/null 2>&1 && [ -f "$MTL_HELPER" ]; then python3 "$MTL_HELPER" --self-test; exit $?; fi
echo "Self-test: PASS (degraded — python3 unavailable; validator-conventions is advisory without it; _has_override bash regression passed)"; exit 0
fi
if command -v python3 >/dev/null 2>&1 && [ -f "$MTL_HELPER" ]; then
python3 "$MTL_HELPER" validator-conventions "$@"; exit $?
fi
echo "WARN: python3 unavailable — validator-conventions skipped; check inline that every AGG validator has a --self-test dispatcher case, resolvers classify on parsed blocks (no raw apodictic:<type> marker scan), the count is derived, no schema is orphaned, and no gate detects an override by a bare \"<!-- override:\" substring (use the _has_override helper). See docs/validator-conventions.md."
exit 0
;;
argument-carve-behavior-preservation)
# Carve-equivalence SMOKE gate (Workstream A, §2.4): a lightweight regression guard that the
# nonfiction-argument-engine modularization did not break the MECHANICAL resolvers (deterministic
# Python only — the LLM editorial layer is non-deterministic and is not tested here).
# Re-runs two resolvers on fixed pre-carve fixtures and diffs each SUMMARY line against a committed
# golden: (1) audit-signal-propagation on argument-editorial-letter.md + argument-findings-ledger.md;
# (2) decision-layer-check (Argument-DE class) on argument-editorial-letter.md.
# SCOPE / HONESTY: this asserts the resolvers still classify the argument fixture as Argument-DE and
# do not regress to error post-carve. It is NOT the full §2.4 field-level diff (no row-by-row
# Findings-Ledger id/severity/evidence_refs diff, no annotation anchor-map diff); the propagation
# summary line is invariant on this fixture, so treat this as a smoke check, not the proof.
# The AUTHORITATIVE behavior-preservation guarantees (which have teeth) are elsewhere:
# - `audit-signal-propagation --check-registry` — all 45 signal-emitting audits still have §4e rows
# (FAILS if the split fragment is dropped); and
# - the byte-identical §4e extraction proof in evals/fixtures/argument-carve/4e-before-after.diff.
# "Identical" = both summary diffs empty (exit 0). Skips when evals/ is absent (repo-only gate);
# fails if a fixture is missing within a present evals/ or a golden drifts.
# Pure shell + python3 (via validate.sh sub-calls). No helper script.
ACB_DIR=$(cd "$(dirname "$0")" && pwd)
# The argument-carve fixtures live only at repo root (evals/ is not shipped to host workspaces and
# is not mirrored under plugins/apodictic/). Resolve from EITHER validate.sh mirror copy:
# ../../../evals (plugins/apodictic/scripts/) or ../evals (root scripts/); resolve-and-skip when
# absent rather than fail — matching the argument-groundtruth-check convention above.
ACB_FIXTURE_DIR=""
for ACB_CAND in "$ACB_DIR/../../../evals/fixtures/argument-carve/precarve" "$ACB_DIR/../evals/fixtures/argument-carve/precarve"; do
if [ -d "$ACB_CAND" ]; then ACB_FIXTURE_DIR="$ACB_CAND"; break; fi
done
if [ -z "$ACB_FIXTURE_DIR" ]; then
if [ "${1:-}" = "--self-test" ]; then
echo " fixture_present: SKIP (evals/ not present — repo-only gate)"; echo "Self-test: PASS"
else
echo "argument-carve-behavior-preservation: SKIP (evals/ not present — repo-only gate)"
fi
exit 0
fi
if [ "${1:-}" = "--self-test" ]; then
ACB_R=0
# Self-test: verify the fixture files exist and the validators produce the golden outputs.
ACB_LETTER="$ACB_FIXTURE_DIR/argument-editorial-letter.md"
ACB_LEDGER="$ACB_FIXTURE_DIR/argument-findings-ledger.md"
ACB_PROP_GOLDEN="$ACB_FIXTURE_DIR/propagation-output.txt"
ACB_DL_GOLDEN="$ACB_FIXTURE_DIR/decision-layer-output.txt"
for ACB_F in "$ACB_LETTER" "$ACB_LEDGER" "$ACB_PROP_GOLDEN" "$ACB_DL_GOLDEN"; do
if [ ! -f "$ACB_F" ]; then echo " fixture_present: FAIL (missing: $ACB_F)"; ACB_R=1; fi
done
if [ "$ACB_R" -eq 0 ]; then
echo " fixture_present: OK (4 fixture files found)"
# Run propagation check and diff against golden
ACB_PROP_OUT=$("$0" audit-signal-propagation "$ACB_LETTER" "$ACB_LEDGER" 2>/dev/null | tail -1)
ACB_PROP_EXPECTED=$(cat "$ACB_PROP_GOLDEN")
if [ "$ACB_PROP_OUT" = "$ACB_PROP_EXPECTED" ]; then
echo " propagation_golden: OK"
else
echo " propagation_golden: FAIL"
echo " expected: $ACB_PROP_EXPECTED"
echo " got: $ACB_PROP_OUT"
ACB_R=1
fi
# Run decision-layer-check and diff against golden
ACB_DL_OUT=$("$0" decision-layer-check "$ACB_LETTER" 2>/dev/null | tail -1)
ACB_DL_EXPECTED=$(cat "$ACB_DL_GOLDEN")
if [ "$ACB_DL_OUT" = "$ACB_DL_EXPECTED" ]; then
echo " decision_layer_golden: OK"
else
echo " decision_layer_golden: FAIL"
echo " expected: $ACB_DL_EXPECTED"
echo " got: $ACB_DL_OUT"
ACB_R=1
fi
fi
if [ "$ACB_R" -eq 0 ]; then echo "Self-test: PASS"; exit 0; else echo "Self-test: FAIL"; exit 1; fi
fi
# Normal run (no args): runs both mechanical validators on the pre-carve fixture and diffs
# outputs against the committed goldens. Usage as a gate: exit 0 = behavior preserved; exit 1 = drift.
ACB_LETTER="$ACB_FIXTURE_DIR/argument-editorial-letter.md"
ACB_LEDGER="$ACB_FIXTURE_DIR/argument-findings-ledger.md"
ACB_PROP_GOLDEN="$ACB_FIXTURE_DIR/propagation-output.txt"
ACB_DL_GOLDEN="$ACB_FIXTURE_DIR/decision-layer-output.txt"
ACB_R=0
for ACB_F in "$ACB_LETTER" "$ACB_LEDGER" "$ACB_PROP_GOLDEN" "$ACB_DL_GOLDEN"; do
if [ ! -f "$ACB_F" ]; then
echo "ERROR: argument-carve-behavior-preservation fixture missing: $ACB_F"
exit 1
fi
done
ACB_PROP_OUT=$("$0" audit-signal-propagation "$ACB_LETTER" "$ACB_LEDGER" 2>/dev/null | tail -1)
ACB_PROP_EXPECTED=$(cat "$ACB_PROP_GOLDEN")
if [ "$ACB_PROP_OUT" != "$ACB_PROP_EXPECTED" ]; then
echo "ERROR: argument-carve-behavior-preservation: audit-signal-propagation output drifted from golden"
echo " expected: $ACB_PROP_EXPECTED"
echo " got: $ACB_PROP_OUT"
ACB_R=1
fi
ACB_DL_OUT=$("$0" decision-layer-check "$ACB_LETTER" 2>/dev/null | tail -1)
ACB_DL_EXPECTED=$(cat "$ACB_DL_GOLDEN")
if [ "$ACB_DL_OUT" != "$ACB_DL_EXPECTED" ]; then
echo "ERROR: argument-carve-behavior-preservation: decision-layer-check output drifted from golden"
echo " expected: $ACB_DL_EXPECTED"
echo " got: $ACB_DL_OUT"
ACB_R=1
fi
if [ "$ACB_R" -eq 0 ]; then
echo "argument-carve-behavior-preservation: PASS (smoke: resolvers still classify the argument fixture as Argument-DE post-carve; authoritative guarantee = audit-signal-propagation --check-registry + the byte-identical §4e diff)"
exit 0
fi
echo "argument-carve-behavior-preservation: FAIL"
exit 1
;;
*)
echo "Unknown command: $COMMAND"
usage
;;
esac
SHA-256: 6c4cb8b80b766eab272d64248fc8785d52aabb649a41746a826321292b37537c