Files
harness-tools/skills/document/unslop/references/pipeline.md
T
2026-08-24 16:33:11 -04:00

22 KiB

Tiered Execution Pipeline

Core release protocol: Luna versus Luna

The product comparison uses gpt-5.6-luna for both arms on the same unfamiliar source. The treatment arm receives the frozen UNSLOP contract, scanner candidates, span-bounded rewrite flow, and validation gates. The raw arm receives neutral editorial guidance and no UNSLOP rules, repository access, scanner output, or validation feedback. A blinded gpt-5.6-sol judge sees randomized candidate labels. Report per-case calls, uncached input tokens, output tokens, elapsed time, detection precision and recall, repair success, preservation, damage, clean no-op, net improvement, and paired wins.

Run a fresh public split only after its corpus, annotations, thresholds, model controls, runner, scorer, acceptance code, and per-case budgets are frozen. Open the sealed holdback only after public acceptance passes. The current beta has no fresh valid paired result: the newest independently annotated corpus failed its preregistered composition floor before either arm ran. Earlier paired runs are directional evidence only.

The cross-provider tables below are ancillary engineering history. They do not answer the core release question and do not substitute for Luna-vs-Luna evidence.

The skill still works with one agent, but orchestrators should use the cheapest executor that can do each job.

Tier 0: Deterministic Gates

Run first on the source text:

python3 scripts/banned_phrase_scan.py <<< "$INPUT"
python3 scripts/structure_scan.py <<< "$INPUT"
python3 scripts/extract_constraints.py <<< "$INPUT"
python3 scripts/readability_metrics.py <<< "$INPUT"

Run again on output:

python3 scripts/banned_phrase_scan.py <<< "$OUTPUT"
python3 scripts/structure_scan.py <<< "$OUTPUT"
python3 scripts/validate_preservation.py original.txt transformed.txt
python3 scripts/readability_metrics.py <<< "$OUTPUT"
python3 scripts/diff_check.py original.txt transformed.txt

Output gates are blocking. Use structure_scan.py --genre docs or --genre social only when the final text truly belongs to that genre.

Tier 1: Small Detector Agents

Run one small detector per (pack x chunk). Use paragraph chunks up to about 500 words. Run pack-register-guards on the whole text because scope, negation, and legal/security force need context.

Detector agents only read their assigned pack from references/packs/ and return findings:

{"span":"...","rule":"...","pack":"...","severity":"hard|soft","note":"..."}

They do not rewrite, score, or import rules from other packs.

Tier 2: Rewriter

Send one strong-enough rewriter the original text, merged Tier 0/Tier 1 findings, extracted constraints, and the selected preset. It rewrites from findings instead of re-running detection in prose. It must preserve register-guard and fact findings, remove phrase/structure/voice findings, and avoid introducing anti-slop-register tells.

Model Tiers

Job Default executor Escalate when
Tier 0 scripts local deterministic never; fix the script or input
Detection packs cheapest tier (see Model Parity) JSON is malformed or pack scope is violated
Span replacement / short rewrite cheapest tier; gates carry safety (parity 2026-07-06: 8/8) output fails a blocking gate twice
Full rewrite of register-sensitive text (legal, medical, security, load-bearing hedges) strongest practical model + mandatory Tier-0 re-scan start here; cheap tiers erode register
Macro structure (restructuring, coda/preview removal) machine-gated AND machine-corrected via the structure climb (generate→scan→directive→regenerate); both frontier tiers converge fast, cheapest-that-converges is model-dependent (Anthropic haiku-4-5 at a slightly larger round cap; OpenAI's cheap tier not yet shown to converge) never trust a model's own macro self-check — feed the scanners' directives back and re-scan (see Macro structure under the climb)
Judge/eval model specified by evals/BEHAVIORAL-EVALS.md benchmark protocol changes

The model-dependent rows above are not set by taste. They are set by evals/run_model_parity.py (see Model Parity), whose live matrix was recorded 2026-07-06: span replacement clears on the cheapest tier because the output gates carry safety, while full rewrites of register-sensitive text and any macro restructuring escalate to the strongest practical model with a Tier-0 re-scan. Re-run the harness when the model features change and update these rows from its output.

If Tier 2 output fails the same blocking gate twice, escalate one model tier. Do not add more rules to the prompt; the failure is execution quality.

Model Parity

The pipeline depends on a model in exactly two places: Tier-1 pack detection and Tier-2 replacement generation. Everything else is deterministic. Whether a cheap model may own those surfaces is a measured question, not an assumption.

evals/run_model_parity.py measures it against a fixed seeded corpus:

  • Task A (detection). Each model reads one pack file plus a short seeded chunk (the Tier-1 contract above) and returns JSON findings. Grading is deterministic: recall of the seeded findings and a count of false findings, scored against a frozen manifest. Six fixtures span the phrases-core, voice, and register-guard families (two are register-guard cases, where the load-bearing hedge must be caught).
  • Task B (replacement). Each model is handed one seeded finding (span + rationale) and asked for a span-minimal replacement. Grading applies the co-writer contract: the replacement removes the flagged tell and adds no new one (both scanners), preserves every fact/number/negation, and stays span-minimal.

The matrix is config-driven — entries are {name, kind, model_id} where kind is claude-cli (claude -p --model <id>, the Anthropic spectrum) or openrouter (the GPT spectrum and others, key read from the macOS keychain service OPENROUTER_API_KEY). The harness emits a per-model / per-task score table (JSON and a markdown summary) plus a --dry-run mode that grades canned response fixtures with no network for the PARITY-* eval rows.

Rule (binding). Any change to the co-writer, mimic, or detector-pack model features must run this harness across both the GPT and Anthropic spectrums before merge, and the detection/replacement rows of the tiering table above must be updated from its output. If cheap models match the strong reference, use cheap everywhere; if they do not, keep the strong model for the passes where the gap appears and record which. The PARITY-* rows in evals/adversarial-evals.json gate the grader itself (dry-run, no network); they do not substitute for the live run.

Recorded results

Live matrix recorded 2026-07-06. evals/run_model_parity.py re-measures on demand; rerun it after any change to the co-writer, mimic, or detector-pack model features and update these tables from its output.

Task B — span replacement. One seeded finding (span + rationale) per trial, graded on the co-writer contract: the replacement removes the flagged tell, adds no new one (both scanners), preserves every fact/number/negation, and stays span-minimal. Eight seeded findings per model.

Model Kind Task B pass rate
claude-haiku claude-cli 8/8
claude-sonnet claude-cli 8/8
gpt-5.4-mini openrouter 8/8
gpt-5.5 openrouter 8/8
claude-opus claude-cli 7/8

The single miss was opus dropping the fact "8:30" from a replacement — caught by validate_preservation, not by model choice. On the mechanical span contract the cheapest tier ties the flagships; the gates carry safety, the tier does not.

Behavioral parity matrix — full rewrites. Eight register/structure cases per model, graded deterministically across both the Anthropic and GPT spectrums.

Model Kind Full-rewrite pass rate
claude-opus claude-cli 7/8
gpt-5.4-mini openrouter 7/8
claude-sonnet claude-cli 6/8
gpt-5.5 openrouter 6/8
claude-haiku claude-cli 5/8
gpt-5.4-nano openrouter 5/8

MACRO-01 (structure_clean) failed for all six models, opus included: each kept a conclusion coda the prose instruction told it to drop. No model self-checks macro structure from prose. The cheap-tier misses were register and fact erosion in full rewrites — softened never/all, a dropped roughly, a dropped legal arguably/negation, an emitted X, not Y contrastive tail. The lab ladders are symmetric (7/6/5 on both spectrums).

Measured conclusions.

  • Span replacement = cheapest tier, with gates on. The output gates enforce fact and tell safety, so the smallest model is safe for span-minimal replacement.
  • Full rewrites of register-sensitive text = frontier. Legal, medical, security, or any text with load-bearing hedges/negation goes to the strongest practical model with a mandatory Tier-0 re-scan; cheap models erode register in unsupervised full rewrites.
  • Macro structure = always machine-gated, never self-checked — but machine-correctable. A Tier-0 scan surfaces it as an explicit directive, because no model, flagship included, reliably catches it from prose instructions. Fed those directives back in a scan-regenerate loop (evals/run_structure_climb.py), both frontier tiers converge to clean fast; whether the cheap tier converges is model-dependent, not a uniform "cheap tier" property -- see "Macro structure under the climb" for the recorded before/after and the follow-up experiments.

Open-weights spot check — 2026-07-07

Live matrix run against the current open-weights flagships of three Chinese labs, each the newest general chat release of its family on OpenRouter: DeepSeek V4 Pro (deepseek/deepseek-v4-pro), Kimi K2.6 (moonshotai/kimi-k2.6), and GLM 5.2 (z-ai/glm-5.2). Same harness and contracts as above; six detection fixtures (Task A) and six replacement fixtures (Task B) per model. Models file: evals/fixtures/parity/models_openweights.json.

Model Kind Task A mean recall Task A false findings Task B pass rate
glm-5.2 openrouter 1.00 0 6/6
deepseek-v4-pro openrouter 0.92 1 6/6
kimi-k2.6 openrouter 0.92 0 6/6

All three clear the span-replacement contract at 6/6, and the only detection misses were one seeded voice span apiece (fixture A4) for DeepSeek and Kimi; on both model-dependent surfaces the open-weights flagships sit level with the recorded GPT and Anthropic ladders.

Macro structure under the climb — 2026-07-07

The recorded parity matrix above closed on a flat verdict: single-pass macro-structure self-checking failed on every model. Told in prose to drop the coda / connective scaffold / outline echo, each one shipped it anyway. But the deterministic scanners SEE those failures precisely, so the failure is correctable even when it is not self-checkable. evals/run_structure_climb.py closes that loop:

generate -> scan (structure_scan + silhouette_scan) -> if clean, done
         -> else turn each finding into a TARGETED directive that names WHERE and
            WHAT -> feed the directives + current draft back -> regenerate, capped at
            N rounds (default 4).

The core is the directive builder (build_directives): a deterministic map from scanner findings to instructions that locate the tell, because the model cannot see it. It re-reads the draft with the scanners' own helpers, so a conclusion_coda flag becomes "the final paragraph (Ultimately, ...) restates the opening; end on a new fact", a connective_paragraph_openers flag names the offending paragraph numbers and their opener words, and a silhouette callback_content flag names the exact opening vocabulary the ending loops back to. Every macro flag both scanners can emit maps to a directive (gated by CLIMB-06). A preservation guard (validate_preservation, anchored to --source-file when given) validates every round, so climbing toward clean structure can never eat a fact; a regression aborts the climb. Honest terminal states (converged / capped / preservation_violation) carry distinct exit codes. The mock path (CLIMB-* rows, evals/fixtures/climb/) exercises all of this offline; the live path drives real models via --generate-cmd.

Live run on the same SKILL-MACRO-01 robotics-essay fixture as Task C, --max-rounds 4, preservation anchored to the source essay:

Model Tier Single pass (round 0) Climbed Rounds Terminal Facts
claude-sonnet-4-5 frontier dirty: signpost_density, callback_content (penalty 7.5) both scanners clean 3 converged preserved every round
claude-haiku-4-5 cheap dirty: callback_content (penalty 2.5) 1 residual structure flag 4 capped preserved every round
gpt-5.5 frontier pending pending n/a pending (OpenRouter HTTP 402) pending
glm-5.2 open-weights pending pending n/a pending (OpenRouter HTTP 402) pending

Read honestly:

  • Single pass ships macro tells on both Claude tiers, reconfirming the thesis: told to remove macro tells, the model does not catch its own document shape.

  • The climb recovers macro on the frontier model where the single pass failed. Sonnet converged to both-scanners-clean in three rounds (violation trajectory 2 → 1 → 0) with every fact preserved. This is the doctrine headline: with the loop, macro structure becomes machine-corrected, a step past machine-gated.

  • On the cheap tier the recovery is partial, not full. haiku-4-5 consumed the silhouette recap loop on the first directive and held silhouette clean from round 1 on, but it oscillated on surface structure (re-introducing a coda, then a uniform cadence) and did not converge within four rounds, capping at one residual structure flag. The loop improves the cheap tier but does not guarantee it at cap 4; a residual macro flag on a cheap model still routes to a stronger model or another round, not to shipping.

  • The preservation guard held live throughout (source-anchored): no round on either model eroded a fact.

  • The OpenRouter spectrum (GPT + open-weights) is pending on account billing (HTTP 402 Payment Required at run time), not on the harness. Exact reproduction commands:

    # Anthropic spectrum (native claude -p; run above):
    python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
      --out out/haiku --generate-cmd "claude -p --model claude-haiku-4-5" --max-rounds 4
    # OpenRouter spectrum (needs OPENROUTER_API_KEY credit):
    python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
      --out out/gpt --generate-cmd "python3 evals/model_generate.py --kind openrouter --model openai/gpt-5.5" --max-rounds 4
    python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
      --out out/glm --generate-cmd "python3 evals/model_generate.py --kind openrouter --model z-ai/glm-5.2" --max-rounds 4
    

Two follow-up experiments — 2026-07-07

The OpenRouter 402 blocked the GPT spectrum above, and the cap-4 haiku row left one open question (does the cheap tier ever converge, or does it just stall). Two follow-ups, both run against the identical SKILL-MACRO-01 fixture and source-anchored preservation:

A. A local alternative to OpenRouter for the GPT side. The codex CLI (OpenAI's agentic coding CLI) is installed locally and can drive one model call per round the same way claude -p does, sidestepping the billing block. codex exec is an AGENT CLI, though: its stdout is an interleaved transcript (tool calls, hook lines, token counts), not a clean document, so it cannot be piped straight into the climb loop. Its -o/--output-last-message FILE flag writes only the agent's final text turn with no wrapping -- verified by hand against this fixture before wiring it in. evals/model_generate.py gained a codex kind (call_codex) that runs codex exec --sandbox read-only --ephemeral --skip-git-repo-check -o FILE -m <model> -, reads that file, and discards the transcript. KNOWN RISK: codex exec has been observed to hang silently in this environment; call_codex runs it in its own process group with a hard wall-clock timeout (180s default) and SIGKILLs the whole group on expiry, so a hang surfaces as an honest failed round instead of blocking the climb forever. CLIMB-07 exercises this offline against a fake codex binary (success, nonzero-exit, empty-output, and hang paths; the hang case is SIGKILLed in about a second against a 1s test timeout).

B. Does the cheap Anthropic tier ever converge, or does cap 4 just cut it off early. claude-haiku-4-5 re-run with --max-rounds 8 (same fixture, same generator) to see whether the one residual flag at cap 4 was a hard ceiling or a round short.

Results, same fixture and settings as the recorded matrix above except where noted:

Model Tier Kind Single pass (round 0) Climbed Rounds (cap) Terminal Facts
gpt-5.5 frontier codex exec (default model) dirty: sentence_burstiness, conclusion_coda, preview_fulfillment, callback_content (4 flags) both scanners clean 4 (4) converged preserved every round
gpt-5.4-mini cheap codex exec dirty: sentence_burstiness, opener_repetition, preview_fulfillment, callback_content (4 flags) 2 residual flags, oscillating (4 → 1 → 5 → 2) 4 (4) capped preserved every round
gpt-5.4-mini cheap codex exec, retried dirty: sentence_burstiness, preview_fulfillment, callback_content (3 flags) 5 residual flags, oscillating (3 → 3 → 2 → 1 → 6 → 2 → 1 → 5) 8 (8) capped preserved every round
claude-haiku-4-5 cheap claude-cli, retried dirty: callback_content (1 flag) both scanners clean 5 (8) converged preserved every round

Read honestly:

  • The -o extraction is clean and codex is a real generator for this loop. No stray transcript text ever reached the scanners; every round's draft was bare prose. gpt-5.5 through codex exec converged in the same 4-round budget sonnet used (3 rounds), matching the frontier-converges finding on the Anthropic side.
  • Codex's cheap tier does not converge, even given double the round budget. gpt-5.4-mini was run twice -- once at the recorded cap of 4, once at cap 8 -- and never reached clean either time. Its violation trajectory does not fall monotonically the way sonnet's or haiku's did; it swings between 1 and 6 flags round to round. The loop still holds every fact (preservation passed all 12 rounds across the two runs), but macro structure on this model is not machine-correctable at any round budget tested here, only machine-gated.
  • The cheap Anthropic tier was one round short of converging, not permanently capped. claude-haiku-4-5 re-run at --max-rounds 8 converged in 5 rounds (violation trajectory 1 → 1 → 1 → 1 → 0), the same residual callback_content flag from the cap-4 run finally clearing once the loop got one more pass. Every round preserved every fact.
  • This changes the doctrine. "Cheap tiers partially recover" (the prior wording) was true of the one cheap model measured so far, but it is not a property of "cheap" in general -- it is model-dependent. claude-haiku-4-5 fully recovers with a slightly larger round cap; gpt-5.4-mini does not recover with an even larger one. Read the two model families separately, not as one "cheap tier" line.
  • Cheapest model that does this well: claude-haiku-4-5, with --max-rounds raised from 4 to 6 (5 rounds measured to converge here; 6 gives one round of margin). On the GPT side, the floor that has actually been shown to converge is gpt-5.5 (frontier) via codex, not gpt-5.4-mini -- the OpenAI cheap tier is not yet a substitute for this task on this evidence.
  • The OpenRouter spectrum itself (gpt-5.5 via API, and glm-5.2) is still pending on account billing (HTTP 402), not on the harness. Codex gave a working local alternative for measuring OpenAI's frontier and cheap models on this task, but it drives the model through Codex's own agent scaffolding and system prompt, not the raw chat-completions endpoint -- read the codex-CLI numbers above as "the GPT models, as codex CLI calls them," not as an exact stand-in for a raw OpenRouter measurement once billing clears. glm-5.2 has no local CLI alternative and remains unmeasured.

Exact reproduction commands:

# Anthropic spectrum, cheap tier, extended round cap:
python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
  --out out/haiku8 --generate-cmd "claude -p --model claude-haiku-4-5" --max-rounds 8
# Codex CLI, GPT spectrum (frontier default, then cheapest):
python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
  --out out/codex-gpt5.5 --generate-cmd "python3 evals/model_generate.py --kind codex --model gpt-5.5" --max-rounds 4
python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
  --out out/codex-mini --generate-cmd "python3 evals/model_generate.py --kind codex --model gpt-5.4-mini" --max-rounds 8
# OpenRouter spectrum (still needs OPENROUTER_API_KEY credit; unchanged from above):
python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
  --out out/gpt --generate-cmd "python3 evals/model_generate.py --kind openrouter --model openai/gpt-5.5" --max-rounds 4
python3 evals/run_structure_climb.py --prompt-file <macro-prompt> --source-file <essay> \
  --out out/glm --generate-cmd "python3 evals/model_generate.py --kind openrouter --model z-ai/glm-5.2" --max-rounds 4

Cost Note

For a 1000-word document:

Path Calls Approx tokens
Tier 0 local scripts free
Tier 1 about 5 small detector calls about 10k small-model tokens
Tier 2 one rewrite call source + findings + preset
Monolithic one large call with full skill and all references often 8k-15k strong-model tokens before output

The tiered path spends cheap tokens on detection, reserves the stronger model for rewriting, and gives the final Tier 0 gates authority.