Files
Edward Cheng-I Wu d5accd6b1f docs(changelog): record the #856 es-ES trigger phrases under [Unreleased] (#866)
Extends the #855 entry with the merged companion trigger change (a366e39):
body Español lines plus description subsets, the CONTENT_LOCKS re-pin, the
code-point description counts, the eval and routing-smoke evidence, and the
follow-ups #864 and #865.


Claude-Session: https://claude.ai/code/session_01CckFaPj7hPxWjCqn1dhbXt

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 11:57:37 +08:00

654 KiB
Raw Permalink Blame History

Changelog

All notable changes to this project will be documented in this file.

[Unreleased]

Added

  • claude plugin eval suite for the academic-paper revision-coach flow (plugin-evals/; toward #851, not closed). Seven cases (five that should fire, two that should not) with twenty graders, run as a with/without-plugin ablation so the headline is the uplift Δ, not a pass rate. The author-defined quality axis is no unauthorised rewriting: the response must not draft manuscript prose, change anything no reviewer asked for, or assert results or changes that have not happened; secondary axes are no comment dropped, push-back allowed on a factually wrong reviewer point, and a committee letter yields a tracker with no peer-review grading. Inputs are synthetic and cover four shapes (mixed-format journal comments, an unnumbered decision-letter email, OpenReview-style scores with a "should we push back" question, and a formal ethics-committee letter), in zh-TW and English. Calibration against the 2026-09-12 pilots is recorded in plugin-evals/README.md together with the run command, the timeout_seconds / max_turns ceilings, the observed pilot cost, and three caveats — including that the ICLR case did not invoke the skill in either pilot, which is filed as #851 and uses that case's display-only skill-fired grader as its acceptance check. plugin-evals/results/ is gitignored. This adds a regression guard only; it claims no measured uplift figure (a single-run pilot is not a measurement).

  • es-ES (Spanish, Spain) README and trigger phrases (#855, #856; toward #850, not closed). README.es-ES.md is a full translation of README.md at v3.21.2 by a native speaker, structure kept byte-comparable with the English file (same heading, table, and fence order; code blocks, identifiers, and links untouched), contributed by @didacrios. The five existing READMEs gain the [Español](README.es-ES.md) nav link. Drift is policed the same way as the Korean README: check_readme_es_sections() in scripts/check_spec_consistency.py pins the version badge, release-note headings, mode headings, and forbidden retired phrases, and scripts/check_reviewer_role_label.py gains the es-ES REQUIRED entry. Translation only; no skill, mode, or trigger changes. The companion trigger change (#856, merged as a366e39, also by @didacrios) follows the #509 Korean precedent: each of the four SKILL.md files gains one body **Español**: line of intent-specific compounds (no broad standalone words) and a conservative Spanish subset in its frontmatter description, which is the surface the router reads; revisar artículo stays exclusive to the reviewer and enmendar mi artículo marks revision intent, pinned by two es-ES boundary fixtures under tests/fixtures/issue_133_routing/ (11 and 12). academic-pipeline/SKILL.md is content-locked, so its CONTENT_LOCKS hash in scripts/check_pipeline_boundary_semantics.py is re-pinned in the same change. All four descriptions stay under the Agent Skills 1,024-character ceiling counted in code points of the parsed value (deep-research 1,021, academic-paper 867, academic-paper-reviewer 878, academic-pipeline 783); the lint that enforces that ceiling is filed as #864. Evidence: the 05-apa-es-locale citation-check eval case fires the skill 2 of 2 on the branch against 0 of 2 on the previous main; a five-phrase Claude Code routing smoke (sonnet, two runs each) fired 2/2 on the lit-review, full-pipeline, and Socratic phrases, 1/2 on revisa este artículo (0/2 on the previous main), and 0/2 on redacta un artículo (also 0/2 on the previous main; the model writes the article in one turn without calling any skill, the bypass tracked in #851 and #857). Two small follow-ups (three broad terms left in the deep-research body line; fixture 11 asking for reviewer comments it does not attach) are #865. Activation layer only; no mode, IRON RULE, schema, or workflow text changes.

  • claude plugin eval suite for the academic-paper citation-check flow (plugin-evals-citation-check/; surfaces #857 and #858, neither closed). Eight cases (six that should fire, two that should not), run as a with/without-plugin ablation so the headline is the uplift Δ, not a pass rate. The author-defined quality axis is the four citation failures that matter: a reference the user has no source for, the right paper with the wrong authors, a hedged or minor finding cited as an established result, and a retracted or concern-flagged paper cited as live evidence. Every fire case ships a complete synthetic source pack so all four are detectable offline; the sandbox has no network, so "no source" is graded as flagging the entry as unverifiable, never as a real lookup. Secondary axes: mechanical errors, no false positives on clean entries, no claim of online verification, no rewriting of the manuscript. Cases cover APA 7 (en, zh-TW mixed, es), IEEE, Vancouver with the style unnamed, and Chicago notes-bibliography; the negatives are an APA-to-IEEE conversion request and a Python unused-import question. Cases pin model: sonnet and the run command sets --judge-model opus so the judge is never the agent model. Calibration against the 2026-09-13 pilots is recorded in plugin-evals-citation-check/README.md with the run command, the timeout_seconds / max_turns ceilings, the observed pilot cost, and the caveats: the with-plugin arm cannot load the mode's own prompt in the eval sandbox because the command stub references plugin files by relative path (#857), so Δ ≈ 0 is the honest current reading; plain-language prompts fired the skill in 3 of 6 cases (#858, with the Spanish case as one data point for #850). plugin-evals-citation-check/results/ is gitignored. This adds a regression guard only; it claims no measured uplift figure.

  • Reviewer-calibration harness with isolated dispatch and audited scoring (#835; toward #653 and #828, neither closed). evals/heldout/reviewer_calibration/ gains the held-out calibration suite and five scripts: fetch_calibration_corpus.py / assemble_calibration_corpus.py freeze a hash-verified manuscript corpus, dispatch_calibration_panel.py and dispatch_e4_panel.py run isolated reviewer-panel cards through the shared Claude CLI transport, build_calibration_measurement_row.py emits contract-validated measurement rows, and score_calibration_run.py scores categorical panel decisions (no AUC: the panel produces decisions, not probabilities). Every dispatch retains write-once outputs, raw transport streams, timestamps, and prompt/output hashes; the transport isolates operator configuration (empty CLAUDE_CONFIG_DIR, environment allowlist) and reconstructs every assistant message from stream-json, including replacement and retraction events, so a synthesis whose Decision line sits in an earlier message is not lost. A zero-cost credential preflight refuses deterministic authentication retries and preserves aborted-run evidence; the optional --require-preflight-ok gate stops before transport construction when a skipped or inconclusive preflight would violate the run's acceptance criteria, and TLS trust failures get a safe diagnostic. Complete assistant frames are preserved ahead of a truncated stream tail; interrupted cards and panel stages are recorded as blocked with their call ledger, which counts CLI dispatch attempts, not provider requests. The retained ICLR 2026 corpus is marked SUPERSEDED and usable only for harness rehearsals (#828: its page-1 layout reveals labels), and corpus freeze and verification reject nonconstant page-1 layout signals. No calibration profile and no rehearsal measurement values ship with this change; the remaining ICLR 2027 corpus capture and the Phase 3.5 two-family judge work are documented as open.

Fixed

  • academic-paper revision-coach now advertises its rebuttal-shaped triggers where the model can see them (#851). The revision-coach trigger phrases "I got reviewer comments", "revision roadmap", "should we push back", "conference rebuttal", and "grant panel response" were listed only in the SKILL.md body (the mode table and the Trigger Keywords section), which the model reads after it has decided to load the skill; the frontmatter description carried "parse reviews" and the rebuttal-audit phrase "audit my rebuttal" but none of the revision-coach ones. In the #852 eval pilots the with-plugin arm answered an English ICLR "should we push back" prompt without invoking the skill in 2 of 2 runs. The five English phrases, the zh-TW 「我收到審查意見」「修訂路線圖」, and the Korean 「심사 의견을 받았어」 are added to the description (699 characters, under the 1,024-character Claude Code allowance; the claude.ai 200-character cap was already exceeded by design, see docs/SETUP.md Method 4a), and the three English phrases missing from the body Trigger Keywords line are added there for parity. "audit my rebuttal" stays distinct so Routing Rule 6 still separates rebuttal-audit from revision-coach by input shape. Verification: plugin-evals/03-iclr-rebuttal-en skill-fired went from 0 of 2 to 7 of 7 with-plugin runs. That case's two llm rubrics (no-fabrication, pushback-per-reviewer) are rewritten in the same change into the enumerate-then-quote style the #852 calibration settled on, and no-fabrication now states that facts the author supplied and results already in the submitted paper are not fabrication; with the earlier claim-list phrasing the runner's judge returned 3-vote FAILs on skill-fired outputs that a reasoning judge passed. Not addressed here: the wider body-vs-description gap (19 English body phrases for this skill, 5 for deep-research, mostly covered by mode names) and a 1-of-7 misroute of ICLR reviews into the #668 committee branch observed during verification, both noted on #851.

  • /ars-mark-read no longer fails on Windows: the ledger lock has an msvcrt backend (#843, PR #844 by @dajiaohuang). scripts/ars_mark_read.py imported the POSIX-only fcntl module at load time, so on Windows the documented CLI raised ModuleNotFoundError before argument parsing and every test in tests/test_mark_read_args.py failed. The module now imports fcntl where available and falls back to msvcrt; two small helpers (_lock_nonblocking, _unlock) select fcntl.flock(LOCK_EX | LOCK_NB) on POSIX and msvcrt.locking(LK_NBLCK, 1) on Windows, inside the unchanged bounded retry loop (Windows contention raises EACCES, which the loop already retries). Review dropped a proposed empty-file NUL pre-write because msvcrt.locking can lock a byte beyond EOF and the write sat outside the retry loop. The POSIX path is byte-for-byte the same lock sequence. This fixes one entry point only: the remaining fcntl imports (adjudication_activity, inquiry_branch_ledger, review_criteria_binding, and their tests) are tracked in #845, and there is no Windows CI job, so Windows behaviour rests on the contributor's reported 4-passed focused run.

  • One shared file-lock helper replaces the six per-file fcntl sites; Windows gets a documented msvcrt backend (#845). scripts/adjudication_activity.py and scripts/review_criteria_binding.py still imported POSIX-only fcntl at load time (so their CLIs and test modules failed on Windows before parsing arguments), scripts/inquiry_branch_ledger.py carried its own try/except, and scripts/ars_mark_read.py carried the #844 backend split inline. New scripts/file_lock.py owns the backend choice (BACKEND): acquire(fd, exclusive=, timeout=) / release(fd) over fcntl.flock on POSIX and msvcrt.locking on byte 0 on Windows, never writing the lock file, with contention on either backend surfacing as one LockTimeout (a BlockingIOError carrying EAGAIN). The POSIX lock sequences are unchanged; the one POSIX-visible difference is textual: the adjudication store's contention message now embeds LockTimeout where it embedded BlockingIOError (the error code ERROR:LOCK and exit status are the same, and nothing parses the class name). A signal that interrupts a lock attempt is retried but never past the deadline. The two semantic gaps are decided per site rather than hidden: the adjudication store's shared read lock degrades to an exclusive lock with a 5-second bounded wait where shared locks are unavailable (writers keep the non-waiting exclusive lock); the review-criteria manifest lock still blocks indefinitely on POSIX and is capped at WINDOWS_BLOCKING_WAIT_SECONDS (30 s) on Windows, surfacing as BindingError; the inquiry branch ledger alpha keeps refusing non-POSIX hosts (it now checks file_lock.BACKEND) because its durable writes have no Windows verification. Two release paths that unlocked an unacquired lock in finally (a no-op under flock, an EACCES under msvcrt that would have masked the real error) now acquire in their own try block and release only after a successful acquire, so a LockTimeout raised inside the guarded body is also never reported as the outer lock failing. scripts/test_file_lock.py (CI manifest id 845-shared-file-lock) covers both backends: the real fcntl backend for contention, bounded and blocking waits, shared/exclusive interplay, and tests/fake_msvcrt.py (one model of the documented _locking contract) for the Windows branch, plus a subprocess test that imports all four consumers with fcntl blocked and exercises each site's Windows decision. docs/SETUP.md / SETUP.zh-TW.md state the platform posture. No Windows CI job is added; real Windows verification remains a manual step requested from the #843 reporter.

  • Socratic non-convergence path F6 no longer ranks or preselects a direction, and the two reference files no longer carry their own auto-end round count (#834). deep-research/references/failure_paths.md § F6 offered "[the most promising direction]", told the mentor to "identify the 1-2 directions with the most convergence potential", and prescribed "restrict discussion scope", contradicting the #735 non-ranking boundary that POSITIONING.md and the mentor agent carry (the directions are the user's own, so this was ranking and preselection, not generation). F6 and socratic_mode_protocol.md § Dialogue Management Rules also still said "round 15 → end" after #490 made the mentor agent's § Auto-End Conditions (Precise) the single authority (40 goal-oriented / 60 exploratory). F6 now lists the directions the user has expressed in the order they were expressed, leaves the choice to the user, and names the visible exit marker on the full-mode option; both reference files point at the agent file for round caps and state none of their own. scripts/test_socratic_rq_non_generation_contract.py gains ranking/preselection-vocabulary and own-round-count checks (plus a pointer/heading parity check) that fail on the pre-fix bytes. This closes a contract contradiction between prompt surfaces found during a cross-model fact-check of the v3.21.2 claim surfaces; it claims no breadth or diversity improvement (that remains #659) and no measured behavior change.

  • OpenAI request builders no longer send parameters GPT-6 Astra rejects; Astra's API effort set is validated before any request (#823). The executable smoke entrypoint (scripts/cross_model_smoke_test.sh) and the canonical OpenAI example in shared/cross_model_verification.md sent temperature: 0.1 to /v1/responses; the official Astra migration guide lists temperature, top_p, and top_logprobs as unsupported, so a caller following the v3.21.2 recommendation built an API-incompatible request before the grounding checks could run. Both builders drop the sampling parameter (Gemini and compatible-provider examples keep theirs). Astra's documented API effort vocabulary (low|medium|high|xhigh|max) replaces the "not confirmed" wording and lives in one canonical per-model table, scripts/cross_model_verification/openai_effort_guard.sh, sourced by both builders; an explicitly configured Astra value outside that set now fails with CROSS-MODEL-ERROR: invalid_astra_reasoning_effort before curl runs, an unset effort still omits the field so the provider default applies, and ids without a table row stay pass-through. A hermetic test in scripts/test_cross_model_verification_guards.py executes both shipped builders (the smoke script and the documented Bash example located by content) against a fake curl and asserts the emitted JSON and that both source the guard. Astra stays provisional: request compatibility is not a bakeoff result.

  • The contained Codex citation transport rejects effort=ultra instead of forwarding it (#824). v3.21.2 added ultra to ACCEPTED_REASONING_EFFORTS in scripts/cross_model_codex_transport.py as an additional reasoning amount. The codex-cli 0.153.4 app-server schema (v2/TurnStartParams.json) defines effort: "ultra" as the replacement for the deprecated multiAgentMode — "proactive multi-agent behavior" — which is outside the transport's single-reference, no-other-agents contract. ultra is removed from the set, and a new validate_reasoning_effort step rejects it with REASONING_EFFORT_REQUIRES_DELEGATION before transport detection, auth access, temporary-state creation, or app-server launch, on both the wrapper-facing verify_once path and the direct run_app_server path; max forwarding and the provider-default (unset) path are pinned unchanged. The check is model-independent by design: this transport speaks the app-server effort enum, and per-model API vocabularies stay with the provider. This is a scope restriction, not a finding that ultra bypasses the disabled features, and it does not apply to a general Codex research session. No code-mode, local-tool, or network permission is widened.

  • Independent audit launcher and claim-audit provenance no longer carry an implicit GPT-5.5 identity (#826). scripts/run_codex_audit.sh launched every new audit with a hardcoded codex exec -m gpt-5.5 and its sidecar recorded no model at all, which is why the stale pin could mislabel runs silently. It now pins gpt-6-astra at xhigh for new runs (an explicit override of the caller's project default, so a fresh-context audit never inherits a moving alias; historical records keep their actual identities) and records both in a new optional sidecar model block (shared/contracts/audit/audit_sidecar.schema.json: requested + reasoning_effort, a request, not a provider attestation); the mock codex in scripts/test_run_codex_audit_e2e.py asserts the exact model, effort, and fresh-run argv and that the sidecar matches. scripts/claim_audit_pipeline.py defaulted judge_model to gpt-5.5-xhigh and stamped that label into every result row and cache key whether or not that model ran; the caller must now supply the actual judge identity (effort included where it changes judgment), a missing / null / blank / unknown identity is recorded as unknown and binds the cache key to the run (the same fail-closed shape as an unknown judge_prompt_version: no cross-run reuse, within-run dedup kept), and a non-string identity is rejected before retrieval. Tests pin that a changed identity partitions the cache and that an unknown identity can never hit a prior run's verdict. academic-pipeline/agents/claim_ref_alignment_audit_agent.md and shared/templates/codex_audit_multifile_template.md state the same contract; frozen benchmark defaults and historical measurements are untouched.

Changed

  • Locale packs are community-maintained, and a single-owner application can be recorded as provisional (#861, #862). CONTRIBUTING.md gains a "Locale packs (community-maintained)" section written ahead of the #850 mechanism: ARS ships one default locale (English plus Traditional Chinese); every other output locale is a locale pack, in-tree under locales/<locale>/ or as a sibling distribution, owned by its contributors. A pack stays listed as supported only with two named owners, recorded currency against each minor release (14-day window), visible staleness in CI that never delays a core release, configuration-and-presentation-only content (never a core SKILL.md, agent, IRON RULE, schema, mode, or oversight rule), and #509 trigger discipline. The maintainer owns the extension interface and the default behaviour and does not translate, review, or support pack content. The follow-up amendment adds a provisional route for the first applicant's situation (one owner, no backup yet): a single-owner application is recorded in a dedicated issue, is not a supported pack, and gets a 14-day backup window that opens with the first minor release after both the locale mechanism and the primary owner's recorded acceptance; an unfilled window lapses the application, and a supported pack that loses either owner leaves the supported list. The interim pointer for activation-layer contributions now names #862 (Phase 1) alongside #850. Policy text only; no code, schema, or skill behaviour changes.

  • Gartenberg et al. (2026) joins the human-in-the-loop anchors as the first journal-side evidence, and volume is recorded as a non-goal (#833). README.md and README.zh-TW.md gain a fourth motivation paragraph (the Organization Science AI Task Force editorial "More versus better", 37(3):795-812; one journal, observational, aggregate; cited as design rationale, not as evidence about ARS). POSITIONING.md "Rejected mechanisms" records the volume non-goal: no batch manuscript generation, no fan-out of one run into several submissions, time-to-draft booked as a resource cost. shared/collaboration_depth_rubric.md 1.0 → 1.0.1 adds a related-construct citation to the Cognitive Vigilance dimension (uncritical acceptance of AI output; "cognitive surrender" as the editorial cites Shaw & Nave 2026) without changing dimensions, scoring, or the descriptive-only reporting rule. No new effectiveness claim is made, and no number from the editorial is presented as being about ARS.

  • Writer and compiler prompts: unsupported factual claims cannot be rescued by hedging, and generic prose quotas become diagnostics (#825). The citation-density recovery tree in academic-paper/agents/draft_writer_agent.md told the writer to rewrite a claim with no usable source "using hedging language" (its CER-chain fallback row said the same, so did the scored writer contract shared/contracts/writer/full.json D2, and rule 5 of the M3 temporal iron rule in the writer and both report_compiler_agent.md mirrors allowed a bare hedge when the verifying dates were absent); hedging calibrates uncertainty but cannot supply evidence, so an unsupported premise could pass as recovered. All four sites now route missing factual support to a supporting source or attribution, omission, or an explicit [MATERIAL GAP] for author review, and an inference or hypothesis must rest on supported premises and be distinguished from an observed finding. The universal prose quotas in the writer prompt, both report_compiler_agent.md mirrors, academic-paper/references/writing_quality_check.md, the academic-paper/SKILL.md anti-pattern rows, and writer contract D6 (80% TEEL as a scored dimension) are rewritten as context-sensitive diagnostics subordinate to author, venue, and discipline requirements — prompts for judgment, never rewrite gates or a pass/fail score (the exact rules are enumerated in the audit correction). Venue word limits, quote/anchor grammar, protected hedges, and revision authority are preserved. Every live consumer of the reference was checked (academic-paper/SKILL.md, deep-research/SKILL.md, both compiler mirrors, writing_judgment_framework.md, academic_writing_style.md); versioned records (README version-history entries, the skills' own changelogs) keep their original wording. audits/harness-retirement-2026-09-model-update.md gains an in-place post-release correction naming the exact files and rules the September scan missed (and the #823 / #824 / #826 items). A synthetic held-out scenario set for the unsupported / contradicted-claim recovery path is added under evals/heldout/unsupported_claim_recovery/ with status NOT_RUN; no measured quality improvement is claimed.

[3.21.2] - 2026-09-06 — Model currency for Claude Fable 5.1 and GPT-6 Astra, checkpoint decision provenance, and CJK title-matching repairs

Added

  • GPT-6 Astra listed as a provisional cross-model verifier; the OpenAI recommendation moves to the current generation (2026-09 model update). gpt-6-astra (released 2026-09-03) joins the canonical model table in shared/cross_model_verification.md as provisional on both transports — no bakeoff run exists; the only evidence is an entry-gate smoke on the ChatGPT-subscription citation transport (scripts/cross_model_smoke_test_codex.sh, 2026-09-05, codex-cli 0.153.4: VERIFIED with one bound source on the Vaswani et al. fixture), which is the precondition for a Promotion Bakeoff, not one. The recommendation moves to gpt-6-astra under the existing #783 policy (recommendation follows generation currency; validated is earned only by the sealed bakeoff), so the move carries no measurement claim. gpt-5.6-sol keeps its validated status on the citation transport and its provisional status on the API route; gpt-5.5 / gpt-5.5-pro / gemini-3.1-pro-preview are unchanged. The id-status allowlist, the quick-setup and codex blocks in docs/SETUP.md / docs/SETUP.zh-TW.md (same example set in both, parity-linted), .claude/CLAUDE.md, and the bakeoff section (now naming the per-transport baseline: gpt-5.5 on the API route, gpt-5.6-sol on the citation transport) move together. Two vendor-reported facts are recorded where they bite: high verbalized evaluation awareness (system card §8.6 / §8.8.1) as a caveat on any bakeoff or calibration result, and GPT-6 Astra's unrecorded list pricing in the cost table. The contained Codex citation transport's reasoning-effort vocabulary gains ultra (system card §10.1.2.5: the Codex harness ran at Ultra effort) as a named constant with a test pinning turn/start forwarding and fail-closed rejection of unknown values; the app-server schema on 0.153.4 types ReasoningEffort as any non-empty string, so this set is ARS's own guard and the provider still rejects what the served model does not advertise.

  • Checkpoint decision provenance: state-machine authority, orchestrator mirror, risk register R11. New ### Checkpoint decision provenance authority section under the Stage 6 boundary semantics in academic-pipeline/references/pipeline_state_machine.md, mirrored operationally by a ## Checkpoint authority fidelity section in academic-pipeline/agents/pipeline_orchestrator_agent.md: only a user turn is a checkpoint decision (never a subagent report, hook or tool result, template default, or the orchestrator's own paraphrase); decisions, consent grants, overrides, and authorizations are re-transmitted to subagents verbatim and labelled as the user's, never widened; consent or approval the user did not give is never asserted; completion and Process Record surfaces report what the user actually decided. Motivation is vendor-documented, not ARS-measured: the Claude Fable 5.1 system card records a fabricated user quotation written to satisfy an approval gate, distorted user intent in subagent instructions, and approval represented that was never given (§6.2.1 / §6.6.1), plus a slightly higher willingness to bypass approval gates (§6.4.5); the GPT-6 Astra system card records proceeding on automated messages after asking for permission (§8.8). The rule is prompt-level and says so; the deterministic authorization inputs (#670, /ars-mark-read scope) remain the enforced layer where they exist. docs/RISK_REGISTER.md gains R11 indexing the rule, its controls, and the residual gap. Both files are whole-file content-locked pipeline surfaces, so both hash constants in scripts/check_pipeline_boundary_semantics.py are updated in the same commit. The orchestrator section is scoped out of the historical v3.6.7 orchestrator line budget with its own bounded cap (scripts/test_v3_6_7_phase_6_6.py, 13 lines measured, budget 18), the convention every prior independent extension follows.

  • Provider-side monitoring and safety interventions named as a transport-failure case. New ### Provider-side monitoring and safety interventions (2026-09) subsection under Graceful Degradation in shared/cross_model_verification.md, grounded in the GPT-6 Astra system card: the provider's misalignment monitor can pause or end a Codex / Responses API conversation and stopped API conversations cannot be resumed (§10.2.3.1); misuse monitors and activation classifiers can block a generation mid-stream (§10.2.3.2); a stricter cyber boundary applies to higher-risk accounts (§10.2.2.2); flagged accounts can be escalated to manual review (§10.2.5). Contract: an intervention is never a verdict — on the API route it surfaces either as an HTTP error (the existing CROSS-MODEL-ERROR: openai_http_<status> transport-failure marker) or as a completed response with no grounding evidence, which the existing NOT_SEARCHED guard already catches; on the contained codex adapter it is the adapter's nonzero exit or fail-closed receipt; none of these is ever a citation judgment, a reviewer finding, or a checkpoint decision; ARS calls are stateless one-per-item, so nothing is lost and the item is re-run; a manuscript is never rephrased to route around a provider's boundary, while ARS's own prompt wording prefers process vocabulary over attack vocabulary; consent must assume provider staff may read escalated content (recorded as R4's residual gap); and ARS never consumes the verifier's reasoning narrative, a design rationale the card's monitorability findings (§9) now support explicitly. The cross_model_unavailable row of shared/contracts/degradation_registry.json is worded vendor-neutrally (an API error or an adapter failure) and anchors the new subsection.

  • Harness-retirement audit for the Fable 5 → Fable 5.1 and GPT-5.6 Sol → GPT-6 Astra change (audits/harness-retirement-2026-09-model-update.md). Both vendor system cards read in full and each behavioral finding mapped to the ARS mechanism that assumes it. Result: 0 prompt-text retirements — both cards report the failure classes ARS's remaining scaffolds guard against (stated guesses as facts, exaggerated completeness, unhedged estimates, framing extension, repeated failing actions, suppressed caveats, permissive reading of instructions) as still present, so 8 keep-as-debt items now carry a system-card citation; 4 applied currency fixes (MU-001 MU-004); 2 deferred items (legacy gpt-5.4* ids pending a first-party deprecation check; a possible authorship-cue rule for reviewer inputs after Fable 5.1 §6.5.3's self-recognition bias); and the four guardrail additions above. The eval-harness model default in scripts/dispatch_e4_panel.py is annotated as measurement identity, not prompt debt.

  • Skill-inventory parity lint (#809). New scripts/check_skill_inventory_parity.py takes the top-level <name>/SKILL.md directories as the authority and requires set-equality against the three surfaces that package or advertise the inventory: skills/<name> symlinks (each must resolve to ../<name>), the .claude/CLAUDE.md Skills Overview table rows, and .claude-plugin/marketplace.json plugins[].skills[] (./<name> form). It also checks that any "N skills" count claim on the three current-state metadata surfaces (plugin.json / marketplace.json descriptions, MODE_REGISTRY.md) equals the number of skills on disk; README and CHANGELOG are out of scope because their release notes carry legitimately frozen historical counts. The table-row grammar moves to _skill_lint (SKILLS_TABLE_ROW_PREFIX / SKILLS_TABLE_ROW_FULL) so this lint and check_version_consistency.py agree on what a row is, and a row that names a skill but lacks its vX.Y.Z token is reported here rather than silently skipped by the version lint; and check_spec_consistency.py now derives its skill list from disk instead of the hardcoded four-path tuple, so a new skill directory is version-policed the moment it exists. Motivation: triage of an external draft PR that added a fifth top-level skill directory showed the existing lints are anchored to the four skills they already know (check_spec_consistency.py hardcodes the paths; check_version_consistency.py iterates the CLAUDE.md table), so an unpackaged, unlisted skill passed every inventory lint. Wired into spec-consistency.yml; 60 mutation tests cover each surface in both directions, dangling / mis-targeted / non-symlink skills/ entries, malformed manifest entries, and stale count claims.

Changed

  • Documentation aligned to Claude Fable 5.1 (2026-09 model update). docs/PERFORMANCE.md / docs/PERFORMANCE.zh-TW.md name Fable 5.1 as the current frontier model and add a clearly-labelled list-price re-derivation of the full-pipeline figure (~$7 per run at US$10 / US$50 per MTok) marked as arithmetic on the token columns, not a re-measurement; the Opus 4.x measurement basis of the table is unchanged. shared/cross_model_verification.md's primary-row example reads Fable 5.1. shared/model_tiering.md gains a declared-model note under Resolving a tier at dispatch time: the resolved tier names the declared session model, because the runtime may serve a classifier-flagged request on a different model of the same family with no signal ARS reads (the vendor specifics — Claude Fable 5.1 system card §3.2 / §5.2 — live in the audit's G-3 row). docs/RISK_REGISTER.md residual gaps change where the cards change the weight of a row: R1 (misrepresented findings named as the residual error class — Fable 5.1 §2.2.4), R4 (provider-side escalation to human review joins the consent residual gap), R5 (per-request fallback invisible to provenance; pointer moved to the new audit and the declared-model note), R6 (covert verifier underperformance likely undetectable even to its vendor — Astra §9.2.2.3). The scripts/cross_model_smoke_test.sh header example uses a <gpt-* id under test> placeholder instead of a concrete id. No agent prompt sentence was removed; the release-facing suite version is unchanged.

Fixed

  • docs/RISK_REGISTER.md R10 residual gap de-staled (#813). R10 still claimed the guard-launcher degradations were "not yet indexed in the degradation registry (#769)" although #769 shipped in v3.21.1 itself (registry 1.3.0, five write_scope_guard_* rows) — the register contradicted the CHANGELOG and the registry file in the same tree, at the tag and on main. The stale clause is removed; the existing-controls line now points the guard's degrade posture at its five registry rows, and the residual gap keeps only the per-mechanism, per-channel loss description. Docs-only; found by an external cross-model fact-check of v3.21.1 claim surfaces. Known residue, accepted: no lint pins a residual-gap sentence against the mechanism inventory it references, so this class can recur; RR-1..RR-3 are unchanged.

  • MLA key-rules line in citation_format_switcher.md no longer reads "No year in in-text" (#805, by @LeslieLi46). Docs-only; the line now matches the in-text format documented above it.

  • check_surface_form_parity names the broken environment instead of blaming the manifest (#801 follow-up). With the manifest file present but pyyaml unimportable, _load_manifest returned None, so the lint failed with "manifest … is present but empty / null / non-mapping" — a misdiagnosis pointing the reader at the wrong file (pyyaml is declared in requirements-dev.txt, so only a broken local environment can hit this). The missing-import case is now a distinct _YamlUnavailableError and the lint fails naming pyyaml and the pip install -r requirements-dev.txt remedy; regression test pins that the message names the environment, not the manifest shape. Also de-enumerated the hand-maintained dependency parenthetical in docs/SETUP.md / docs/SETUP.zh-TW.md Method line ("PyYAML + jsonschema" was already stale against the manifest it mirrors two lines above); both language files move together.

  • normalize_cn_title strips outer wrapper marks only when they enclose the whole title as one balanced unit (#800). The wrapper strip inherited from #431 (and promoted unchanged by #798/#799) was positional: it removed the first and last characters whenever they matched as a wrapper pair type, without checking they belonged to the same bracket pair. 《红楼梦》与《金瓶梅》 — two titles joined in one string — therefore normalized to 红楼梦》与《金瓶梅, leaving an orphaned mid-key. Matching correctness was never affected (both sides of every comparison pass through the same normalization, and no exploitable asymmetry was found in the #799 security pass), but the mangled key is a semantic anomaly for any future single-sided consumer (display, logging, cache keys, or comparison against an externally-normalized key). Fix adds _outer_pair_encloses: the outer marks are stripped only when the interior between them is itself balanced under all six wrapper pairs, so 《围城》 still strips to 围城 and nested balanced interiors (《基于「ProEXC」的研究》基于「ProEXC」的研究) still unwrap, while 《红楼梦》与《金瓶梅》 and “研究”与“实践” keep their marks. The interior scan is scoped to the outer pair's own family: is also the English apostrophe and also appears unpaired, so a family-blind scan read the lone in 《Alzheimers病中表达》 as an unbalanced quote and refused to strip a genuine 《…》 wrap — dropping a pair that matched before to exact=False and ratio 0.6818, below the 0.70 floor, which fails the DOI-keyed ratio gate and the title-fallback exact gate at once and is the failure class #798 repaired. Scoping costs the check nothing, since any mark that can orphan the outer pair is by definition of that pair's own family. Both consumers change together — the CJK client re-imports the shared function (#799), pinned behaviorally as well as by identity. Scope of the invariance claim, stated precisely: on the two has_cjk-gated paths (exact_normalized_title's third branch and _similarity's CJK fold) every verdict and ratio for titles without Han ideographs is unchanged, pinned by the #799 pre-fix oracles. The client's _cn_titles_match is not has_cjk-gated — it calls normalize_cn_title directly — so a mark-carrying title with no Han ideograph can change verdict there (《Hamlet》and《Macbeth》 no longer matches a pre-mangled Hamlet》and《Macbeth); that path is DOI-keyed and Chinese-corpus-only in practice, so the narrowing is accepted rather than gated. Likewise the empty-wrapper guard is a property of the CJK branch specifically (_cjk_titles_match requires a non-empty key): exact_normalized_title("《》", "《》") remains True through the ungated base-normalization branch, as it did before this change.

  • CJK titles no longer fail the shared exact-title gate in the four index resolvers (#798). chinese_literature_client.py already carried a Chinese-aware normalize_cn_title / has_cjk (#431 §"Chinese title matching"), but the four index resolvers (Semantic Scholar / OpenAlex / Crossref / arXiv) never saw it — they read the ASCII-centric _text_similarity helpers, where .lower() folds case but never width ( U+FF30 never reaches P U+0050) and string.punctuation contains none of , 《》, or U+3000. A Chinese paper an index served in a different-but-legitimate typesetting therefore missed on two paths: the DOI-keyed cross-check, which gates on the fuzzy ratio alone and scored a fullwidth spelling of the identical title at 0.625 — under the 0.70 floor — reporting a correct DOI as DOI_MISMATCH; and the title-fallback search, which requires ratio and exact-normalized equality and so fell to unresolvable. Both feed the *_unmatched contamination signals, so a genuine paper could accumulate k across indexes and be rendered CONTAMINATED-TRIANGULATION-UNMATCHED — the protocol doc's own "P0, next to the word 'fabricated'". The failure was invisible to the English test corpus for a measurable reason: a Han character is a whole word, so the same six-codepoint corruption is 37.5% of a 16-character Chinese title but 8% of its 68-character English equivalent (measured 0.625 vs 0.912). Fix promotes has_cjk / normalize_cn_title into scripts/_text_similarity.py (behaviorally equivalent — the promotion hoists locals to module constants, precompiles the regex, and rewrites comments; the CJK client now re-imports rather than keeping a private copy, per the #128 anti-drift goal), adds the Chinese-aware form to exact_normalized_title as an additive third branch, and folds it into _similarity through the existing max. Both are gated on both sides carrying a Han ideograph, so every non-CJK verdict and every non-CJK ratio is provably unchanged — pinned by two oracle tests that re-state the pre-fix exact_normalized_title and _similarity formulas in full (the latter including the dotted-acronym branch) and assert exact agreement in both directions, so a raised score is caught as loudly as a lowered one. Cross-script and romanized pairs still cannot match (no translation oracle), Simplified/Traditional is still not folded, and an empty normalized key still never matches. Under the Chinese-aware form the ratio also regains discriminative power on the motivating pair: 1.000 for the identical title against an unchanged 0.510 for a genuinely different paper (the base form separated 0.566 from 0.510 — almost nothing). Also measured and pinned: on the motivating pair the pre-fix ratio was actively anti-correlated — the identical title scored 0.606 while a genuinely different paper scored 0.645, so the wrong paper ranked higher. The repair leaves the unrelated pair's score byte-identical (0.6452 before and after; it is not equal under the CJK form, so nothing is folded in) and lifts only the true match, restoring the ordering title ranking depends on. Not in scope, and unchanged: the base ASCII normalization still collapses ER+/ER- and p53/P53 because it maps ASCII punctuation to whitespace and lowercases, and exact_normalized_title ORs that form in; the fuzzy floor also remains a weak separator for CJK generally (that 0.645 near-miss is pre-existing and untouched here). 32 new tests, including 5 integration tests through the real Crossref client covering both broken paths, each verified to fail against the pre-fix module, plus a monkeypatched detonator pinning that the Chinese DOI-path matcher never consults the fuzzy ratio in either direction.

  • Autolink round-trip test no longer rides on an undeclared, version-sensitive dependency (#801). test_gfm_bare_urls_emails_and_schemes_cannot_autolink soft-imported markdown-it-py — not listed in requirements-dev.txt — and silently returned when it was absent, so its no-link_open tail had never actually run in CI, while an ambient markdown-it-py 2.x (whose linkify pass still tokenizes the escaped forms as links; reproduced under 2.2.0 + linkify-it-py 2.0.3, reported by @akshath-raj in #799) failed the test on clean main. Verified dividing line: 2.2.0 fails, 3.0.0 and 4.0.0 pass with linkify-it-py 2.0.3 held constant. The tail is split into its own test (test_escaped_markdown_yields_no_linkify_tokens_on_round_trip) gated by pytest.importorskip("markdown_it", minversion="3.0.0") + pytest.importorskip("linkify_it", minversion="2.0.3") — ambient-old environments now skip visibly instead of failing or passing vacuously — and requirements-dev.txt declares markdown-it-py>=3.0 + linkify-it-py>=2.0.3 with a reverse pointer at the consuming test, so CI exercises the round-trip for the first time. The same declaration also newly activates the identical soft-import tail in test_renderer_neutralizes_markdown_active_inventory_path (scripts/test_build_submission_packet_manifest.py), which moves to the same importorskip idiom (no version floor: it parses default CommonMark without linkify) and gains the inline-children flatten its top-level-only token scan was missing — link_open / image / html_inline nest under inline tokens, so the pre-existing assertion could only ever catch html_block; its strengthened assertions verified under markdown-it-py 2.2.0, 3.0.0, and 4.0.0. The triplicated hostile-row construction in test_evidence_rows.py is consolidated into one _hostile_row helper. Renderer behavior and every renderer-side assertion are unchanged.

[3.21.1] - 2026-08-24 — Bounded workflow substrates, sealed bakeoffs, and transport hardening

Fixed

  • Second markdown-grammar consolidation slice (#794). CA-1 and RR-1 now share one repo-relative path/anchor resolution ladder, using authored fragments as exact generated heading ids rather than re-slugifying them; case-drifted fragments therefore fail consistently. check_spec_consistency.py now consumes the shared rendered-link grammar, so image targets and links inside code/comments/fences no longer create false broken-link findings while its historical file-existence-only scope is preserved. The indirect prompt-injection asset validator also reuses the shared GitHub slug implementation (CLI / IDEcli--ide). Consumer and helper mutation suites pin the intended bare-#, trailing-#, case, non-rendering, and import/CLI behavior.

  • Codex subscription transport repaired against three live codex-cli 0.147.0 drifts (#785). The contained citation adapter had become unusable end-to-end: (1) the ChatGPT-subscription attestation is emitted on stderr in non-TTY invocation, but detection read stdout only — every detect returned AUTH_NOT_CHATGPT_SUBSCRIPTION; detection now accepts the exact line on either stream (the #684 harness idiom), with a stderr-emitting fake-codex regression test. (2) The provider's structured-output validator now rejects uniqueItems (invalid_json_schema, HTTP 400) — the keyword is dropped from the provider-sent MODEL_OUTPUT_SCHEMA; duplicate-source refusal was already enforced fail-closed in the local validator. (3) --disable code_mode_host silently removed the standalone web-search tool (the tool executes through the code-mode host on this build; isolated by live bisection of the 22-entry disable list) so every call failed closed as MODEL_RETURNED_NOT_SEARCHED; the host is no longer disabled while code_mode itself stays disabled and the forbidden-event scan still fails the receipt on any item type outside the four-type allowlist. Live cross_model_smoke_test_codex.sh: PASS for both gpt-5.5 and gpt-5.6-sol (2026-08-19).

Added

  • Sealed preregistration for every future Promotion Bakeoff (#789; PR #795). Two closed contracts plus scripts/check_promotion_bakeoff_preregistration.py implement the private fixture → public hash commitment → preflight → post-fleet reveal lifecycle for both API and codex transports. The verifier binds LF-normalized bytes and the fixed 30-row composition, requires a dedicated commitment-only commit strictly before the immutable probe/reveal commit, refuses path/symlink/history drift, rejects shallow or unreadable history, and scans every detectable historical probe version to retire published answer keys and reject reused fabrication references despite superficial text edits. Squash/cherry-pick reveal copies are accepted only as byte-identical lifecycles descending the same seal, and every copy introduction is exposed in the receipt for a mandatory post-fleet remote-timing witness. The canonical protocol requires the run report to retain the public commitment permalink/CI witness and fleet time bounds, because local Git ancestry cannot prove remote publication time. The sole 2026-08-19/20 campaign is grandfathered only at its pinned path, normalized digest, immutable blob history, and regular-file mode; all later unsealed fixtures fail CI.

  • Default-off research-workflow profile substrate (#742). Closed profile and append-only selection-receipt schemas, a deterministic offline runtime, and a canonically stored/hash-bound field_general fallback now implement explicit selection, visible fallback, and correction without restarting or rewriting scholar-owned artifacts. The fallback leaves every stage unresolved except field-general integrity gates, treats an empty authority list as “unknown; ask,” provides en/zh-TW display names, and records every caller-declared prior-profile output as stale: profile_context_changed; newly introduced authority requirements remain an explicit unmet reuse gate. The #745 matrix registers only deterministic conformance under rq_formation. No manuscript inference, pipeline hook, family-specific shipped profile, default-on change, human usability run, or research-outcome claim ships; behavioral evidence remains NOT_RUN.

  • Opt-in inquiry branch ledger alpha (#743; PR #796). Closed ledger and passport-pointer schemas plus a deterministic offline runtime now preserve append-only branch origins, explicit author adoption/disposition, exact-profile live-budget enforcement, reopen signals, and independently resolvable first-degree stale causes. Compact summaries are wired only at the Stage 1 design freeze, Stage 2.5/4.5 checkpoints, and lawful recorded-signal moments; signal actions carry trusted current status/provenance, and every outstanding stale artifact/cause remains individually visible through bounded one-line escaping. Publication starts only with a second branch and uses a shared passport lock, finite bounded timeouts, full-byte compare-and-swap, and a durable recovery journal for the ledger/passport pair; malformed pointers, unresolved historical profiles, relative or absolute symlink aliases, path/sidecar collisions, hash drift, and incomplete invalidation batches fail visibly. Authoritative append/summary/load/commit calls require the expected project identity. The flag remains ARS_INQUIRY_LEDGER=1, default OFF; the two #745 matrix rows are structurally CI-gated with behavioral evidence NOT_RUN, so no novelty, correctness, value, recovery-benefit, or usability claim ships.

  • Profile-relevant alternative-register design freeze (#744). Freezes an independently versioned stage/category companion map and future event-sourced register without implementing either. The corrected design uses its own per-stage alternative budget rather than redefining #742's branch budget; embeds exact profile-receipt and map snapshots; requires monotonic current-ledger-tip CAS, exact profile-rebound linkage, and one fixed-path passport transaction domain with unchanged-dependency CAS and non-pointer passport preservation. Lifecycle transitions, structured adoption/trigger receipts, provenance-safe rendering, typed unresolved relevance at drafting/review, bounded unranked AI candidates, and complete paginated first-degree invalidation are closed. Empirical and non-empirical synthetic mappings demonstrate lawful differences, while inapplicable cells emit no boilerplate. The future flag stays default OFF and all human-participant usefulness, inflation, omission, recovery, and burden evidence remains NOT_RUN.

  • Bounded source-backed criteria proving set (#575). The live review-criteria registry now contains one illustrative exact-profile fixture modeling author confirmation—Software Engineering × MSR 2027 × Technical Papers × Full Paper—with three current official venue/type rows, plus immutable-commit ACM SIGSOFT General and Repository Mining rows selected as field/overlay guidance. The SIGSOFT rows remain advisory because no official MSR adoption statement was found. Mutable MSR provenance is bound to a committed raw/semantic hash receipt without redistributing the page; SIGSOFT freshness is pinned against the verified repository head. Exact axes, policy eligibility, effective-date downgrade, three-consumer binding, source locators, and predecessor-to-successor digest migration are executable. This is one architecture proving set, not venue/discipline coverage, a real-author attestation, or constructive-review evidence; #575 stays open for #684's two independent human experts and blind adjudication.

  • Historical pre-proving-set snapshot — #575 closure-scope and #684 expert-readiness audits (2026-08-24). At the time of that retained audit, target resolution and three-consumer digest binding were mechanically implemented while the live criteria registry still had zero official venue/type, field/society, or reporting/design rows. The later #575 entry above closes that dated source-scope gap without rewriting the snapshot. The #684 replay confirmed all 24 retained subject outputs and the blinded expert packet were ready, but completion still lawfully waits on two independent human experts and a separate blind human adjudicator; agent/model labels are not substituted.

  • Write-scope guard launcher degradations registered in the degradation registry (#769). The registry presents itself as indexing every graceful-degradation mechanism in the suite, but the guard launcher's documented degraded states had no rows — and the #757 prose table in docs/CONTROL_AVAILABILITY.md was standing up a second, unpinned authority for those facts. Five write_scope_guard_* rows now index the launcher's paths (no real Python → silent pass-through; Windows without Git Bash → guard inactive with per-call hook error; no timeout binary → portable watchdog fallback that still forwards the guard's real decision, with only an overrun resolving to pass-through; guard subprocess misbehaves or the launcher itself fails internally — mktemp, self-resolution — → pass-through with guard stderr dropped; and the documented multi-megabyte payload edge as its own row, recorded honestly as an accepted, untested case with no pinned outcome), each with verbatim authority anchors into hooks/run_guard.sh (plus the README Requirements bullet) checked by the D3 lint, and pinned_by naming scripts/test_run_guard_launcher.py where CI-executable pins exist (the Windows-without-Git-Bash and payload-edge rows honestly carry none). Registry 1.2.0 → 1.3.0; _EXPECTED_MECHANISMS inventory lock updated in the same commit (16 → 21). The CONTROL_AVAILABILITY table now declares itself a convenience summary backpointing at the registry rows.

  • First Promotion Bakeoff run: gpt-5.6-sol validated for the ChatGPT-subscription citation transport (#787; PR #788). The § Promotion Bakeoff procedure ran end-to-end over the #630 contained codex transport (entry gate: live codex smoke PASS for baseline and candidate), with a preregistered instrument: the 30-reference probe set (evals/bakeoff/2026-08-19-gpt-5-6-sol-codex/, every real row resolver-confirmed same-day, every fabrication negative-checked) and the final receipt parser were both committed and pushed BEFORE the scored fleet. Scored run (run 7, 2026-08-20, counterbalanced pair scheduling under the frozen instrument): 30 × 3 repeats × 2 models = 180 same-day paired calls, majority verdicts — all five non-inferiority measures passed with a stated superiority case (fabrication recall 0.90 vs 0.80, p95 latency 25.0 s vs 49.6 s nearest-rank, grounded completion tied at 0.900, zero receipt-guard misfires on both fleets, false-disagreement 0.00 = 0.00; recall and latency led in all five paired fleets, and the sequential fleets' completion edge did not survive counterbalancing and is not claimed). Run report with probe-set sha256 and the full exploratory-round accounting: audits/bakeoff-gpt-5-6-sol-codex-2026-08-19.md — six unscored rounds are documented (tool-defect fleet; a fixture-correction round after both models correctly flagged three operator transcription errors, discarded under the preregistration clause; an instrument-defect round whose over-narrow parser suppressed the baseline, discarded as candidate-flattering). The result is transport-qualified: gpt-5.6-sol remains provisional on the first-party API route (its jq grounding guards were not exercised), and the API-route id-status allowlist is unchanged; the recommendation-policy note, Supported Models row, bakeoff section, and the codex-subscription setup examples record the split explicitly. Campaign side-product: two further receipt-grammar drifts fixed in the transport — non-search webSearch items are exempt from stream-fatality strictly per the protocol's closed WebSearchAction set ({other, openPage, findInPage} + Responses-API spellings, verified via codex app-server generate-json-schema; an opened page's URL still can never become a bound source; unknown action shapes stay fail-closed) and DEVELOPER_INSTRUCTIONS now requires an empty sources array for NOT_FOUND/NOT_SEARCHED — with page-open + unknown-shape mutation tests (51 → 54 transport tests).

Changed

  • data_access_level re-derived for academic-paper and academic-paper-reviewer under the dirtiest-input rule (#773). #756 re-derived only academic-pipeline's annotation; the other pins froze pre-existing declarations without claiming re-derivation. Applying the same derivation: academic-paper moves redactedraw (standalone modes ingest ungated user drafts and third-party reviewer comments, and literature_strategist_agent's search-fills-gap flow ingests external-index search results inside the skill — the former value described the orchestrated pipeline path, where Stage 2 inputs arrive as Stage-1 sanitized artifacts, Gate 2.5 running after Stage 2), and academic-paper-reviewer moves verified_onlyraw (the standalone /ars-reviewer entry legitimately consumes an ungated pasted manuscript; the former value was at best true for the pipeline's initial Stage 3 dispatch — Stage 3' re-review consumes a freshly revised manuscript before Stage 4.5 — and that Stage 3 sequencing is unchanged). deep-research's raw is re-affirmed on its inputs (raw user queries + unverified web/database search results), with a ceiling argument as supplement. All four EXPECTED_LEVELS pins are now derivation-backed, the lint docstring records each derivation, and the ARCHITECTURE §4 diagram + rules separate the per-skill intake annotation from the per-stage output data level (§3 column), which is unchanged. Declarative annotation only; no runtime behavior changes.

  • Markdown-stripping helpers consolidated into scripts/_markdown_lint_util.py (#771). The fence/comment/code-span stripping and rendered-link grammar that #757 (check_control_availability.py) and #758 (check_data_flows.py) shipped as two diverging copies now live in one shared module, imported by both lints and by check_risk_register.py (which had been reaching into the siblings' private helpers as a stopgap). Per the issue's consolidation rule the #770 versions won as the superset, so this is a refactor plus one deliberate grammar alignment: CA-1..CA-3 now also apply the inline code-span and image-exclusion rules that previously protected only DF-3/RR-3 (a backticked pseudo-link or an image no longer counts as a rendered link on the CA surfaces — behavior-visible, invariant-preserving in the strengthening direction). The consolidation also moves heading-slug extraction (github_slug / heading_slugs) into the shared module — ending the last private markdown-helper import among these three lints — and adds a shared links_to inbound-link predicate absorbing the three previously copy-pasted CA-3/DF-3/RR-3 loops. The grammar gains its own direct test suite (scripts/test__markdown_lint_util.py, manifest-registered) plus a CA-3 image-exclusion mutation test; all three consumer mutation suites still run against the shared module.

  • Cross-model recommendation surfaces follow generation currency (#783; PR #784). The recommended-verifier surfaces (shared/cross_model_verification.md Supported Models table + recommendation sentence + Option A example, docs/SETUP.md / docs/SETUP.zh-TW.md quick-setup blocks, .claude/CLAUDE.md live description) stop naming superseded GPT-5.5 as the default: gpt-5.6-sol (current OpenAI flagship) became the lead OpenAI example while still provisional at that recommendation-update point, before this release's later #787 bakeoff. That later run validates only the ChatGPT-subscription citation transport; the first-party API route remains provisional, the API-route id-status allowlist is unchanged, and the § Promotion Bakeoff remains the only route to validated. gpt-5.5 / gpt-5.5-pro stay in the table as validated previous-generation options, with gpt-5.5 the designated bakeoff baseline. Gemini 3.1 Pro stays recommended: first-party check (2026-08-19) confirms gemini-3.1-pro-preview is still Google's most capable Pro model. Docs-only; no env-var or first-party API transport change.

  • OrcaRouter listed in the community directory (#781; PR #782). THIRD_PARTY.md now records the contributor-submitted OpenAI- and Anthropic-compatible gateway as an option for the ungrounded OpenAI-compatible cross-model path. The directory entry is acknowledgement only, not official support, bundling, endorsement, affiliation, or maintainer verification.

[3.21.0] - 2026-08-18 — ISO/IEC 42001-spirit transparency, verifiability, and feasibility track

Added

  • Lightweight risk register (#759). docs/RISK_REGISTER.md is the single artifact linking each of ten standing risks (hallucinated citations, silent claim-strength drift, indirect prompt injection, cross-model content exposure, model-version drift, correlated same-family error, user over-reliance, venue-policy staleness, dependency/supply-chain, install-channel enforcement loss) to its existing controls, the evidence status behind them, and the residual gap with its tracking issue where one exists. The register indexes, it never re-authors: matrix-tied evidence statuses mirror shared/contracts/capability/stage_capability_matrix.json verbatim, and maintainer assertions are explicitly marked as such. New lint scripts/check_risk_register.py (RR-1 pointer integrity incl. repo-containment and anchor checks, RR-2 status mirroring with a malformed-citation guard, an asserted-status ceiling — the matrix stays the sole authority for MEASURED/MIXED — and an inventory lock on the shipped matrix-row citations, RR-3 resolved-path README discoverability; 22 mutation tests incl. a real-tree pass) is wired into spec-consistency CI and the pytest manifest; it imports the sibling lints' markdown/link helpers and the matrix's loader and status vocabulary rather than keeping third copies (#771). Closes the F-1 finding of the ISO/IEC 42001-spirit gap assessment (audits/iso42001-spirit-gap-assessment-2026-08-17.md §7 step 5).

  • Pipeline wiring for the #655 claim-standing probe (PR-C: trigger, consent surface, freshness, transmission ledger). The probe now joins the pipeline at exactly one seam — a new "Claim-Standing Probe Offer" section in the Phase E protocol (academic-pipeline/references/claim_verification_protocol.md) offers it as a user-requested, advisory-only view after the Claim Registry is emitted at a Stage 2.5 or 4.5 integrity checkpoint, never as part of Phase E verification or the integrity result, and E1 now records WHICH of the five high-impact criteria fired (high_impact_basis) beside the #549 selection tier so the trigger has a registry-recorded input. scripts/build_claim_standing_query_plan.py enforces the exact §3.1 trigger (gate 1: at Stage 2.5 the recorded HIGH-IMPACT tier alone is the registry witness and RANDOM/TOP-UP/NOT-SELECTED are never eligible, with the plan's five-part basis supplied by the registry or a recorded researcher confirmation; at Stage 4.5 the ALL registry plus the recorded basis is required because ALL alone is not permission, and a basis-less row stays ineligible until confirmed; confirmations and basis provenance are recorded on the consent surface, never in the registry; the trigger constants are exported by the Track A substrate and test-pinned to the plan schema) and the §3.2 consent sequence (gate 2: propose renders the closed consent surface — exact claim, providers and retention, per-recipient content, caps, LLM-transmission flags, exact stance provider/model when proposed, persistence/deletion/export boundaries with the derived artifact suffixes, the advisory statement, and the four separate choices — and embeds the complete consentable-plan projection, so the surface SHA-256 covers every field the receipt later binds and bind asserts receipt-vs-surface digest equality; absence of the acceptance, any post-proposal change, or an explicit cancel produces an explicit local not_checked declination record — consent_absent / consent_invalidated / consent_cancelled — and nothing else). Bound plans validate as claim-standing-query-plan/1.0 or /1.1 under the Track A substrate, reuse the declared discovery-adapter roster verbatim, derive the default query with the substrate's exact_claim_query, refuse explicit falsey decisions rather than defaulting them, and refuse the unauthorized assisted_then_researcher_approved planner mode; bind --output persists only under explicit_local_export to the consent-derived .query-plan.json path, printing for session-only use otherwise. scripts/check_claim_standing_freshness.py implements §7 (gate 13): the probe identity bindings — current claim text, consent receipt, query plan, adapter registry, candidate ledger, stance configuration including the consented provider/model and frozen prompt-contract version — are compared through the runner's single expected_identity authority, and any drift yields a stale verdict with a closed reason list, the fixed inspectable-but-not-current presentation rule, and explicit assessed/unassessed binding lists so a partial comparison never presents itself as complete; a stance record is only assessable with its candidate ledger, corrupt self-digests are assessment errors, never silent verdicts, and --runtime-check also compares the consented roster against the currently declared adapters. shared/contracts/claim_standing/transmission_ledger.schema.json (claim-standing-transmission-ledger/1.0) closes the §6 forward-declaration from the Track B slice: retrieval-query events derive one-to-one from retained attempts, stance events are the runner's transmission records field-for-field with unexpected fields refused, and accounting is deliberately conservative (a pre-transport adapter refusal still appears with its failure state, so the ledger may overstate, never understate, what could have left the session). scripts/check_claim_standing_transmissions.py (gate 14) schema-validates both inputs, replays their self-digests, runs the canonical intake's semantic invariants (so a re-sealed retrieval input that silently omits a planned query/index attempt is refused), and fails closed on any event outside the consented content-class allowlist or recipient roster, an attempt naming a non-consented query/index pairing, duplicate attempt ids, receipt or retention drift, and unrecorded result states; a retrieval_plus_stance plan must supply its stance transmissions explicitly (an empty list is an explicit no-call assertion), the stance-record cross-check schema-validates and digest-replays the record and compares each event's prompt hash and result state against its row — the runner now binds the prompt hashes into the row BEFORE the transport call, so a timeout/judge-error/oversized failure still retains exactly what was sent and the semantic validator replays those hashes on transport-reaching failure rows too — and validation is exact replay. Gate 12 is pinned by a static capability scan of the three wiring modules (import allowlist, attribute-level containment of the network-capable discovery module to its pure roster constant, no direct write calls, no Phase E or read-ledger reach) plus an end-to-end run of the library seams and CLI entry points proving no file creation and no input mutation; the doc-pin markers live solely in the #655 integration guard, which also gains the Phase E offer surface and a forbidden-stale-sentence check. The consent surface additionally disclosures the complete consented-artifact roster (ARTIFACT_SUFFIXES, test-pinned to every owning module), an explicit cancel is recorded consent_cancelled even after a stance surface was proposed (never misread as invalidation), the eligibility verdict separates eligible (tier witness) from dispatchable (nothing further owed), the bind CLI exits with a distinct status 3 for declinations, and a trailing-separator authorized output path is refused at proposal, at plan validation, and in every derived-path helper family-wide. All three tools are deterministic and offline; 119 tests across four new files register in the CI pytest manifest, and the substrate schema-validator memoization plus shared export-consent helpers land alongside. No live index, model, judge, relevance assessor, or measurement ran; every probe surface still says STANCE CLASSIFICATION UNMEASURED, and #655 stays open pending the live stance-provider adapter, expert ground truth, and the baseline measurement row.

  • Solo-maintainer governance statement + SECURITY triage procedure (#760). New root GOVERNANCE.md states the decision authority (single maintainer, no committee; CI gates are deterministic checks, not a second reviewer), scopes cross-model review honestly (an error-detection control, not organizational independence — a second model is not a second person), and records release authority and the end-of-life posture (no SLA; the license already permits forks). It also carries the operating-principles section: the three distilled principles (transparency / verifiability / feasibility) with informative anchors into ISO/IEC 42001, a table mapping the eight assessment issues (#753#760) onto the standard's risk-based core, and the Annex C objectives assessed as not applicable (fairness, environmental impact, training-data quality) with reasons. SECURITY.md's bare 7-day promise becomes a written solo-runnable procedure: 7-day acknowledgement as the only hard promise, three severity classes mapped to best-effort response targets, credit/opt-out, and coordinated-disclosure expectations. NOTICE.md gains a governance pointer. Closes the F-2 finding of the ISO/IEC 42001-spirit gap assessment (audits/iso42001-spirit-gap-assessment-2026-08-17.md §7 step 5); together with the #759 risk register this completes epic #761's eight sub-issues.

  • ISO/IEC 42001-spirit gap assessment (#762). audits/iso42001-spirit-gap-assessment-2026-08-17.md records the dual-track audit (in-session structural review + an anchoring-controlled cross-model read with first-party re-verification of every filed finding) that produced this release's remediation series. It fixes the scope decision — three distilled operating principles (transparency / verifiability / feasibility) with informative ISO/IEC 42001 anchors, certification explicitly not pursued — the verified findings register (T-1..T-7, V-1..V-2, F-1..F-2 → #753#760), the already-tracked effectiveness-evidence line (#746/#675/#676/#653), the explicitly-not-adopted list (AIMS document set, appointed auditor, supplier program), and the remediation order this release completes.

  • Per-channel control-availability matrix (#757). docs/CONTROL_AVAILABILITY.md maps mechanism × install channel (plugin, skills copy, repo clone, Cowork, claude.ai Project, Claude Science, Pi port — the latter two channels surfaced by evidence re-verification beyond the issue's original six) so a user evaluating an integrity claim can see whether it operates in their channel, with per-note conditions, environment degradations, and channel-wide limitations; linked source documents stay authoritative. New lint scripts/check_control_availability.py (CA-1 links + anchors resolve, CA-2 every SETUP method reachable, CA-3 README/SETUP inbound links; 22 mutation tests) is CI-wired. Closes assessment finding T-6.

  • Data-flow map (#758). docs/DATA_FLOWS.md is the single user-facing map of every network touchpoint the suite's own scripts perform (four-resolver citation gate, cross-model transports, update check) and every local store (verification cache and friends), with payload class, recipient, credentials, TTL, and off switch per row, plus explicit scope boundaries (the Claude session itself, maintainer-only harnesses, and the no-autonomous-publication line inherited as a scope boundary, not a runtime guarantee). New lint scripts/check_data_flows.py (DF-1 AST-scanned network imports must be on the map — its first run caught an unmapped Crossref-importing script — DF-2 curl-invoking shell scripts, DF-3 README/SECURITY/THIRD_PARTY inbound links; 29 tests) is CI-wired. Closes assessment finding T-7.

  • CI workflow enforcement-class table (#755). docs/ARCHITECTURE.md §7.1 classifies all fourteen workflows by what they actually enforce — blocking / advisory / administrative / post-push detection — with triggers, bypass tokens, and tag-push behavior (three workflows' tag runs documented; tags evaluate no path filters), so "CI-gated" claims can be read at their real strength. New lint scripts/check_workflow_classification.py (WC-1 two-way inventory, WC-2 closed vocabulary, WC-3 count-line recompute, WC-4 per-file bypass-token pins) is CI-wired. Closes assessment finding T-5.

  • Stage capability/evidence matrix with enforceable claim ceilings (#745). New shared/contracts/capability/stage_capability_matrix.json (stage-capability-matrix/1.0) records, per pipeline task family, the mechanism/contract status, deterministic-conformance status, behavioral-evidence provenance (eval, model, population, date, result), external/human outcome evidence, known exclusions, transport limits, the maximum currently licensed claim, and the next required evaluation. scripts/check_stage_capability_matrix.py (70 mutation tests, CI-wired) enforces the frozen task-family vocabulary shared with the #742 profile contract, non-collapsible evidence statuses in both directions (DESIGNED / NOT_RUN / MEASURED / MIXED / OUT_OF_SCOPE; an unrun eval can never carry numbers, and measurement provenance is refused on unmeasured rows), in-repo eval references, measurement-report binding with sibling-supersession detection (a re-measurement cannot leave a stale row silently authoritative), falsifiable conformance_pinned_by lints/tests on every CI_GATED/TESTED row, containment-checked verbatim claim anchors into README with a shipped row/anchor inventory lock, future-dated and stale measurements refused without a note, never-licensed stems and unmeasured percentages refused in claim prose, conservative effectiveness-language discipline on unmeasured rows, and byte-identity of the generated docs/STAGE_CAPABILITY_MATRIX.md view. Seeded with 13 rows covering all nine task families from the current evaluation record: 4 measured/mixed rows (RQ wording advisory, tortured-phrase mechanical conformance, seeded-defect reviewer panel including its currently-failing severity-agreement gate, revision claim-drift guard) and 9 designed/not-run rows whose ceilings state exactly that. The matrix makes evidence gaps inspectable; it does not itself improve stage performance, and registering a row licenses at most that row's recorded claim ceiling.

Fixed

  • academic-pipeline data_access_level corrected to raw + per-skill pins (#756). The 2026-08-17 ISO 42001-spirit audit (V-2, #761) found the suite's principal data-classification annotation internally inconsistent on its most prominent consumer: academic-pipeline/SKILL.md declared verified_only while the orchestrator's Stage 1 accepts raw user requests and mid-entry accepts raw existing papers, contradicting the dirtiest-input declaration rule in shared/ground_truth_isolation_pattern.md. The frontmatter now declares raw (the honest minimal relabel; no per-stage trust-domain split, since the annotation is declarative), the docs/ARCHITECTURE.md §4 diagram no longer places the pipeline in the verified class while dash-orchestrating the raw skill (it moves to the raw class with the User → pipeline intake edge drawn, and the §2 legend disambiguates the §3 per-stage "Data level" column from the skill-level declaration), and the SKILL.md #528 content-lock sha256 is updated in the same commit. scripts/check_data_access_level.py is rewritten as a single-pass checker with an EXPECTED_LEVELS per-skill pin layer: a silent flip back to verified_only, an unregistered new top-level skill, an orphaned pin, or a non-mapping metadata block now fails CI with one violation per problem; the academic-pipeline: raw pin is #756-derived while the other three pins freeze pre-existing declarations against silent drift without claiming re-derivation. The pre-existing CLI test layer (stdout-reporting contracts) is preserved and the shared write_skill fixture helper moves to tests/test_helpers.py.

  • Distribution-surface claims aligned with evidence ceilings (#753). The 2026-08-17 ISO 42001-spirit audit (T-1 through T-4, #761) found four claim surfaces sitting outside every existing lock, their language outrunning the evidence record. All four are rewritten to matrix-licensed language: (1) .claude-plugin/plugin.json / .claude-plugin/marketplace.json descriptions drop "Production-grade" and "39-agent ensemble" (8 task families are NOT_RUN in docs/STAGE_CAPABILITY_MATRIX.md; "ensemble" implied execution/error independence the default inline path does not provide) in favor of "contract-audited … 39 prompt roles (3 plugin-exposed agents; the rest run inline by default)" with an explicit capability-ceiling pointer. (2) The academic-pipeline/SKILL.md no-bypass prose ("no --no-block escape hatch" / "cannot be skipped, must PASS" / "must PASS with zero issues") is rewritten to what the mechanism actually is — mandatory checkpoints whose only path past a block is an explicit, recorded user decision (rationale requirements escalate on repeated overrides per shared/compliance_checkpoint_protocol.md), a trust-based control with an audit trail; the same "must PASS with zero issues" absolutes across the integrity-protocol family (references/integrity_review_protocol.md, references/reinforcement_content.md, references/team_collaboration_protocol.md, agents/integrity_verification_agent.md, the SKILL.md stage-flow rows for both Stage 2.5 and 4.5, and the state-machine checkpoint-trigger rows) now name the recorded 3-round FAIL-loop exit instead of contradicting it — including the team-protocol handoff rows, the state_tracker_agent prerequisite checker, and the pipeline_orchestrator_agent transfer rows + #660/#672 advisory dispatch (Stage 3 / Stage 5 entry and both advisory reruns now accept the recorded FAIL-loop route instead of making it unreachable or relabeling the draft as verified), with a FAIL-loop continuation keeping the passport UNVERIFIED — and the #528 content-lock hashes (SKILL.md, pipeline_state_machine.md, state_tracker_agent.md, pipeline_orchestrator_agent.md) are updated in the same commits. (3) The shared/cross_model_verification.md "31% → ~5-10%" error-reduction estimate is relabeled as an unvalidated working hypothesis — no local study has measured a post-verification rate. (4) The shared/ground_truth_isolation_pattern.md "never bundle gold labels into the repository" sentence is rewritten to its intended boundary (no unconditional loading into operational agent context), resolving the literal contradiction with evals/gold/ existing in-repo for explicitly-invoked harnesses; the exception is scoped to synthetic/public-safe content, with live-reviewer calibration sets staying runtime-supplied and never committed. Two lint layers keep the distribution manifests from re-drifting (the three rewritten prose surfaces carry no dedicated claim-language lint; academic-pipeline/SKILL.md sits under the byte-level #528 content lock only): version-consistency invariant 8 now binds the agent-count number in both the legacy "N-agent" and the new "N prompt roles" spellings and checks every count token, not just the first; and a new scripts/check_distribution_surface_claims.py (26 mutation tests, CI-wired via spec-consistency.yml + the pytest manifest) fails closed on missing/unparseable distribution manifests (non-standard NaN/Infinity constants included), refuses unlicensed claim language in any plugin/marketplace description field (the capability matrix's effectiveness vocabulary imported from check_stage_capability_matrix.py so a matrix-side stem addition binds this surface in the same commit, any percentage figure, "production-grade", "agent ensemble"), requires the plugin description to carry a count token in a spelling invariant 8 can bind (a reworded count cannot silently detach the number from the tree), and binds any "N plugin-exposed" claim to the check_agents_mirror_sync MIRRORS roster size.

  • Citation-surface version drift + version-consistency invariant 12 (#754, PR #763). CITATION.cff and the POSITIONING.md citation prose had silently sat at 3.14.0 for six minor releases while the suite moved to 3.20.1 (the Zenodo archive itself was current — v3.20.1 deposit published 2026-08-16 — so this was pure metadata drift; root cause: the file was added 2026-06-15, after the version lint existed, and nothing forced new version-bearing surfaces into the lint's coverage). PR #763 brought both surfaces to the then-current 3.20.1 (this release bumps them to 3.21.0, with invariant 12 keeping them aligned from here on), and scripts/check_version_consistency.py gains invariant 12: CITATION.cff is parsed as YAML (a regex scrape would misread the legitimate quoted spelling as drift) with absence an error like README.md, its version gated through the broad-capture + strict-semver idiom, and its date-released compared against the latest CHANGELOG entry date with the invariant-10 ±7-day window (the second half of the same drift — the date had sat 6 weeks stale); every (Version X.Y.Z) token in POSITIONING.md must be canonical and equal the suite version, so a v-prefixed or truncated human edit errors instead of being silently filtered (the pre-#169 lesson). Both surfaces are wired into the aligned test fixture, so every pass-case test now exercises the invariant; 16 targeted mutation tests (53 → 69). Hardened through a 4-angle cleanup review plus a 5-round cross-model review under an explicit threat-model ceiling (crash-on-malformed-date, silent-skip, and silent-capture-dropout paths closed; the duplicate-YAML-key construction adjudicated as a documented known limitation — last-wins matches every CFF consumer). Filed from the 2026-08-17 ISO 42001-spirit audit (#761).

  • #743 design-doc reset-boundary co-location (PR #764). Main-branch hotfix: the #743 design doc mentioned the ARS_PASSPORT_RESET flag without co-locating the passport_as_reset_boundary.md protocol reference, tripping scripts/check_passport_reset_contract.py on main; the reference is co-located as the lint requires.

[3.20.1] - 2026-08-15 — Contract-honesty hardening and bounded evaluation substrates

Added

  • v3.20 post-release contract-honesty hardening (#734, #735#740; PR #747). Adds a visible, user-requested exit from non-generating Socratic RQ mode without automatic candidate generation; a hash-bound per-finding E6 disposition sidecar with explicit restore / authorize-with-reason / pause routing; exact-span, raw-byte-bound Claim Registry coverage replay with registered-denominator and unknown semantic-completeness disclosure; required read-scope on every new /ars-mark-read event with closed fail-visible resolution; criterion-bound categorical reviewer judgements with live packages fixed at NOT_CALIBRATED; and replay-valid six-axis review-panel provenance that never reduces personas to a binary independence claim. These changes harden contracts and disclosures; they do not establish improved scientific outcomes, reviewer correctness, complete semantic detection, authenticated human identity, or independent error processes.

  • Adaptive inquiry and evidence roadmap (#741#746). Adds the staged post-v3.20 through v3.22 plan for bounded domain profiles, opt-in inquiry branches, cross-stage alternative registers, stage-specific capability evidence, and outcome evaluation. Simple-path defaults, progressive disclosure, domain-stratified usability gates, and a strict repository data boundary precede any default-on structural expansion.

  • Stance runner and presentation renderer for the #655 claim-standing probe (Track B execution). scripts/claim_standing_stance_runner.py consumes a finalized candidate ledger under a retrieval_plus_stance query-plan-1.1 consent and produces one claim-standing-stance-record/1.0 plus its evidence-row/1.3 rows. Consent gates fire before anything else: a 1.0 or retrieval-only plan refuses, the injected stance transport's declared provider/model identity must equal the consented stance_plan, and the frozen prompt-contract version is bound by the consent. The judge grammar is closed four-line output; the evidence quotation must be a verbatim substring of the inspected text within 25 words (verified byte-span, sealed as a verified_exact_match evidence row), and any deviation becomes that row's not_checked/parse_error with the raw output retained verbatim; transport failures map to judge_timeout/judge_error, a non-string or oversized judge return stays inside the closed vocabulary, and every transport call records a §6 transmission event (recipient provider/model, purpose, content classes, prompt hash and byte count, consent receipt, retention disclosure, result state) returned beside the record. validate_stance_record is the semantic verifier the stance-record contract names as required: consent recheck (a retrieval-only plan can never validate a stance record), stance-runtime bound to the consented stance_plan, record-hash binding, every identity hash replayed against the exact plan and ledger, the all-selected distribution sum, exact once-each coverage of the ledger's selected families, and deep evidence replay — each referenced row's claim/candidate/ledger bindings, coverage, sealed source hash, and byte-span excerpt are replayed against the ledger, cross-candidate reuse and orphan rows are rejected, both positive excerpt states replay their spans, and every performed row re-parses from its retained raw output so a resealed stance value the judge never produced cannot pass — the happy-path runner output passes it, and re-sealed tamper cases (wrong sum, dropped row, edited evidence, stale ledger hash) each fail on the semantic layer independently of the record hash. The runner CLI deliberately refuses to execute until a live stance-provider adapter ships with its own consent surface. scripts/render_claim_standing_view.py renders the §5.3 three inseparable parts (consent/recorded-search metadata with visible failures, the complete candidate ledger including culled states, and the distribution plus per-source rows) using only the §5.4 search-bounded sentence forms, the fixed empty-category wording for every empty bucket, the all-selected denominator with the performed-only view marked secondary beside it, uppercase coverage labels, inert escaping of all provider-controlled text (HTML, backticks, Markdown link/image syntax, line breaks flattened, and C0/C1/bidi control characters stripped so a hostile title can neither forge a bounded sentence, emit a tracking image, nor manipulate a terminal), per-source rows carrying the §5.3 fields (authors, finding queries/indexes, provider rank, relevance, coverage, conditions, evidence refs, source URL) in the §5.4 per-source sentence form, the consented stance provider and its retention disclosure surfaced in the consent section, a leading STANCE CLASSIFICATION UNMEASURED banner on every view, stale-record refusal, and view persistence gated exactly like every other Track A artifact (explicit_local_export consent, hash-bound derived output path, exclusive no-follow 0600 fsynced write). The three claim-standing tools now share single-instance package-first imports so exception identity holds across them. Thirty-two new tests (twenty-two runner, ten renderer) cover consent gates, identity binding, the closed failure vocabulary, semantic-validator independence, forbidden-vocabulary absence, and inert rendering; family sweep 198 green. No live stance provider, relevance assessor, pipeline hook, model call, or measurement exists; #655 stays open.

  • Stance-side contracts for the #655 claim-standing probe (Track B substrate). Three closed contracts precede any stance execution. claim-standing-query-plan/1.1 (shared/contracts/claim_standing/query_plan_v1_1.schema.json) is identical to 1.0 except the consent decision may be retrieval_plus_stance: a top-level stance_plan names the exact stance provider/model, prompt-contract version, and retention disclosure (known requires a reference, unknown requires null); the consent binds its hash; and the authorized content classes extend by exactly claim_and_selected_evidence_to_stance_provider. The runtime validator accepts both plan versions — 1.0 semantics are byte-unchanged, the discovery CLI and ledger builder finalize under either, and the 1.1 stance bindings fail closed (unbound hash, missing stance_plan, mismatched content classes). evidence-row/1.3 (shared/contracts/evidence/evidence_row_v1_3.schema.json) is the #656-owned claim_standing_advisory surface: one provenance-only row per (probe claim, selected candidate) binding the bounded excerpt to its exact source hash/span, reusing the family's excerpt/cache/content-handling blocks verbatim (test-pinned); it never carries a stance and abstract-level coverage is never rendered as verified full text. claim-standing-stance-record/1.0 (shared/contracts/claim_standing/stance_record.schema.json) freezes the future stance runner's output: full §7 probe-identity hashes, per-family rows under the closed §5.1 vocabulary with cross-field rules (performed requires ≥1 evidence-row reference and verbatim raw output; metadata-only coverage can never be performed), the §5.3 all-selected distribution, a mandatory STANCE CLASSIFICATION UNMEASURED banner, and no scalar credibility/confidence/trust property anywhere (recursively test-pinned). Twenty-one tests cover both plan versions end-to-end (validator, projection, builder finalization, discovery retrieval) and contract mutations. No stance runner, relevance assessor, renderer, pipeline hook, model call, or measurement exists; #655 stays open.

  • Live discovery adapters for the #655 claim-standing probe (Track A head). scripts/claim_standing_discovery.py turns a consent-bound claim-standing-query-plan/1.0 into a claim-standing-retrieval-input/1.0 record through one closed adapter per index (Semantic Scholar paper search, OpenAlex works search, Crossref works query, arXiv API query) — the separate discovery interfaces the design requires; the pinned resolver clients are untouched and never imported (test-enforced). Each adapter declares its provider block (product identity, query capability, abstract availability, pagination behavior, adapter version, and an honest retention_state: unknown with a null reference), requests a single page up to the frozen 20-hit per-(query,index) cap, truncates provider overflow at the adapter boundary with exact truncation counts, and maps failures onto the closed vocabulary (timeout, authentication_failed for 401/403, rate_limited for 429, service_unavailable for 5xx, malformed_response for undecodable bodies, unsupported_query for an arXiv year filter — decided before any network call) with exactly one transport call per attempt and no silent retry. OpenAlex inverted-index abstracts are reconstructed by position; abstract text is retained exactly as returned (Crossref keeps its JATS tags byte-for-byte; the OpenAlex reconstruction is derived text and is normalized). The retrieve CLI validates the plan and its consent bindings first, refuses to persist any output while the hash-bound consent says session_only, accepts only the output path derived from the consent's hash-bound authorized_output_path (plus a fixed .retrieval-input.json suffix), writes exclusively through the substrate's no-follow fsynced writer, refuses provider redirects (an off-roster Location cannot receive the consented query and surfaces as service_unavailable), maps unreachable services (DNS/connection/TLS failures) to service_unavailable and plain 4xx request rejections to unsupported_query, binds each consented provider-roster block to the executing adapter's declared block, bounds provider bodies at 8 MiB, refuses quote-bearing arXiv phrase queries before transport, isolates a provider-contract violation (overlong field, bad or untyped value, underreported count, NFKC-nonsemantic identifier) to its own attempt as malformed_response, maps provider publication types conservatively onto the closed status vocabulary (unmapped types stay unknown), and emits a schema-valid retrieval input whose relevance_assessments array is deliberately empty — the #719 contracts define assessments as caller-supplied, the relevance assessor is a later separately consented slice, and the candidate-ledger builder still refuses to finalize until one assessment per computed work family exists. Thirty-one offline tests exercise the four adapters through an injected fake transport (success mapping, truncation accounting, the complete failure vocabulary, partial-fleet failures, unknown-index fail-closed, consent refusal, exclusive writes); no live index was called, and the adapters are documented as unexercised against live providers until a first diagnostic run. Stance classification, rendering, evidence rows, and pipeline wiring remain unimplemented; #655 stays open.

  • Claim-standing stance seed set (#655 §8 seed, v0.1, unmeasured). New evals/heldout/claim_standing_probe/ seed for the future stance-classification baseline: 32 repository-owned synthetic claim/candidate items (16 en + 16 zh-TW, one distinct discipline each) covering per language two items for each of the six stance labels plus missing-abstract, metadata-only, irrelevant-candidate, and session-held-full-text slots. Item claims and abstracts were cross-model authored (Codex gpt-5.6-sol, reasoning xhigh) so the future subject's model family did not write its own test items; all content is fictional and DOIs use a reserved 10.99999/csp-* form. The closed §5.1 vocabulary ships as four Draft 2020-12 schemas (seed set, blinded expert label file, hash-bound adjudicated ground truth, strict subject output with deliberately no confidence/score field) plus a criterion-keyed expert label_guide.md, and a fifth schema freezes the deterministic score-report shape. scripts/validate_claim_standing_stance_assets.py enforces slot coverage, id/DOI binding, design-slot realization, a heuristic simplified-Chinese screen, and a registration guard — the suite is deliberately NOT in suite_registry.json, and the validator fails if it appears there while the set is seed_unmeasured. scripts/claim_standing_stance_scorer.py is the §8 mechanical scorer: it compares the frozen subject enum to adjudicated labels with no model in the loop, reports stance/relevance/check-state confusion and failure-class distribution by language, keeps macro recall and micro accuracy separate, counts blocked/partial rows without imputation, and computes the two-replicate decision-relevance flag. Twenty tests cover the shipped set's invariants (mutation-style), enum synchronization across the four record schemas, and the scorer's confusion accounting and fail-closed input handling — including that an abstention on a gold-performed row lands in an explicit NOT_CHECKED confusion column (abstaining can never inflate recall), that blocked/partial rows keep their failure classes visible (the subject schema now requires a failure state on every blocked/partial row), that full-row accuracy includes evidence scope, and that adjudication must bind two distinct experts with distinct file hashes; a validate-expert-file subcommand checks one expert file's complete distinct item coverage. Per-scope and per-failure-class accuracy strata are deferred to the future baseline-row compiler and the report states only counts for those dimensions. Accepted seed boundaries are documented in the suite README: static text realizes only the abstract_missing failure family (runtime failure classes are exercised by fault injection at implementation time), and expert-packet byte sealing belongs to the implementation PR's labeling workflow. design_target fields are construction intent only, never ground truth. No probe implementation, expert label, adjudication, subject run, or baseline row exists; every probe surface remains STANCE CLASSIFICATION UNMEASURED, and #655 stays open.

  • Closed first-round assignment-ledger gate for the #659 blind bundle. The no-call envelope froze 48 arm-blind judge packets but explicitly did not implement the assignment-ledger gate its delivery boundary requires; scripts/ideation_diversity_assignment_gate.py now implements it, deliberately outside the no-call runner so the runner's own boundary statement stays true. An operator-authored closed ledger (judge_assignment_ledger.schema.json) carries only pseudonymous judge handles, an adjudicator handle that the schema itself excludes from first-round rows, blind session ids, and four exact hash bindings to the finalized run plan, inventory, blind manifest, and private arm map. verify replays the complete finalized bundle (including private-map 0700/0600 permission enforcement), requires all 48 packets to carry at least two distinct first-round judges, requires the roster to equal exactly the assigned judges, and forbids any judge from receiving two packets whose cells share a pair_id — pair-level blocking subsumes scenario and role-card sharing, including the same scenario appearing in both experiments. Only a fully passing check seals a write-once pass receipt (assignment_gate_receipt.schema.json) embedding the exact accepted ledger; a failure writes nothing, and an exposure failure carries no judge or blind identifiers (either would disclose private-map structure once combined with the ledger). deliver treats the sealed receipt as evidence rather than authority: it replays the complete bundle validation and every semantic gate check — exactly what verify runs — against the embedded ledger (a hand-fabricated receipt cannot authorize a delivery verify would refuse, even over a consistently tampered bundle), verifies the packet's sealed inventory hash, and publishes exactly one isolated packet per verified assignment into a destination outside the run and gate directories that the gate itself creates (mkdir as the atomic desk claim: a new delivery refuses any pre-existing destination, so two racing deliveries cannot both own one desk). Each assignment is claimed by a write-once schema-validated marker (first_round_delivery_marker.schema.json) and closed by a write-once completion marker after publication — an interrupted identical delivery may exact-resume once, but a completed assignment is never re-issued (accepted residual: a crash between publication and the completion marker leaves that assignment resumable with identical bytes for the same judge, affecting no blinding property); the destination is checked before the marker claim so a refused destination never burns an assignment, and a post-publication isolation re-scan remains as defense in depth, so a desk is certified only by a successful exit over exactly one packet. Gate artifacts live in a sibling <run>-assignment-gate/ directory, so the runner's exact run-inventory validation stays green (pinned by regression). Twenty tests build a genuine finalized 48-packet bundle and cover pass/idempotence/receipt-conflict, pair and cross-experiment exposure (mutation-verified), coverage, roster, binding drift, loosened private-map modes, tampered-bundle replay, delivery duplication/misdirection/drift, and receipt-schema validity. The gate proves structural exposure constraints only: it cannot authenticate that two handles are distinct people, and judge/adjudicator independence remains a procedural responsibility. Two bounded weaknesses under concurrent operator misuse are documented and accepted (a lost atomic desk claim needs manual marker recovery; concurrent identical resumes can both report success — neither affects exposure, certification, or re-issue guarantees). Unlike the no-call runner, the gate is not yet pinned by the suite's spec-consistency contract lint (schema closedness and the offline claim are covered by tests, not CI AST pins); extending that lint is deliberate follow-up work. No subject, actor, judge, adjudicator, model, or network ran; #659 stays open pending externally recorded sessions, human judges, and the per-mechanism baseline.

[3.20.0] - 2026-08-14 — Evidence-bound review and revision, contained transports, hermetic evaluation substrates

Added

  • Opt-in, process-isolated PDF text/OCR advisory (#512 follow-up; PR #724; safe replacement for the concept proposed in external PR #623). Default calls keep the original sidecar shape, tool version, and structural PASS / FAIL / UNAVAILABLE decision; only a caller that selects --classify-content starts a fixed child worker over the exact already-hashed bytes and receives an all-or-nothing extension whose verdict scope is explicitly STRUCTURE_ONLY. The parent never imports the optional native classifier, uses no shell, writes the exact input off the timeout-control loop, enforces one five-second execution deadline plus a shared 0.2-second teardown grace, and caps only stdout/stderr. Each loop iteration polls and then immediately observes the clock, accepting an exit only when that observation is strictly before the deadline; a late-returning poll cannot turn a timeout into success. POSIX cleanup kills the isolated worker group before joining pipe helpers; the portable Windows path terminates only the direct worker. Launch/helper, exit/signal, pipe, and protocol failures become closed reason codes; non-finite confidence and out-of-range OCR pages are rejected; only TEXT_AVAILABLE / OCR_RECOMMENDED / unavailable advisory states are emitted. The stdout-only legacy CLI performs no alias precheck, so malformed input paths remain exit-0 structural UNAVAILABLE data. When a file write is requested, conservative NFC/casefold keys, resolved targets, and existing-inode checks reject literal, .., case/canonical-equivalent, symlink, and hard-link aliases before the worker or either write; samefile errors fail closed except for an absent leaf, while an unresolvable input remains the structural preflight's responsibility. Raw upstream types and exception text cannot enter the prompt-facing sidecar; an explicitly requested, exclusive POSIX-only mode-0600 local diagnostic retains at most 512 bytes of untrusted operator detail, with its resolved parent dirfd/inode bound before the worker so a later parent-symlink retarget cannot redirect raw detail. Its created fd is inode-bound: any pre-success fchmod/write/file-fsync/close/parent-fsync failure removes the final leaf only if a fresh no-follow lookup still names that exact created inode, parent-fsyncs best-effort, and preserves the primary error. Thus a partial/malformed diagnostic does not permanently consume the exclusive path and retry succeeds, while an attacker replacement leaf is never deleted. Non-POSIX platforms or platforms without fchmod reject that option before path creation. POSIX sidecar output binds the resolved parent dirfd and inode before the worker starts, creates a fixed-length random-named private 0700 staging directory through that dirfd, and uses only anchored dirfd-relative operations thereafter. The complete fixed-name payload is file-fsynced, its open inode is checked against the staging entry, atomically installed with dirfd-relative os.replace, checked again at the target, and parent-fsynced; an observed check-to-replace substitution is rejected and its installed attacker entry removed, retargeting a parent symlink cannot redirect publication, final-component links are replaced rather than followed, and a legal 255-byte destination basename still fits. Cleanup attempts close/unlink/rmdir independently, preserves any primary publication error over secondary close failures, and removes unpublished staging. The parent and private staging directory remain caller-controlled prerequisites: Python exposes no compare-and-rename primitive, so a same-UID actor that keeps racing after the instantaneous installed-inode postcondition is outside the isolation claim. Non-POSIX --output fails closed because Python does not expose the required anchored dirfd publication there; classifier-to-stdout remains available. The optional dependency has a separate pin and deterministic absent state rather than being installed unconditionally. Three closed Draft 2020-12 schemas, synthetic PDFs, temporary fake modules, and fake workers cover present/absent, late exit/timeout, non-zero/signal, malformed/invalid, oversize, page-bound, helper-startup, deadline/teardown, diagnostic failure/retry and attacker-leaf retention, diagnostic-parent and parent/final/staging alias races, close failures, long basenames, privacy, and permission paths without downloading the package, using a live PDF, invoking a model, or wiring an automatic OCR/anchor gate. This proves the isolation/protocol slice only, not classifier accuracy or downstream efficacy.

  • EOF-complete post-terminal drain for the Codex citation transport (#725; PR #726). After the target turn/completed, the host now closes app-server stdin and accepts a transport result only after clean parent exit plus stdout and stderr EOF within the smaller of the original absolute execution deadline and a three-second drain grace. Every late stdout line is retained and parsed under the existing JSON, event-byte, message-count, forbidden-tool, and grounding rules; drain timeout, nonzero exit, malformed output, reader failure, and stderr overflow fail visibly, while final process-group cleanup reaps the parent and descendants. Hermetic fake-server regressions emit the forbidden event only after stdin EOF and cover hang/reap, late cap, malformed output, nonzero-exit, stderr, and static mutation boundaries. No live Codex, model, login, network, API, or private citation data was used.

  • Offline role-topology utility design and synthetic seed (#582). Two independent task classes now freeze their own matched role-count arms: decomposable reviewer evidence review uses 1/2/5/7-seat topologies, while sequential writing/revision uses 1/2/5/8-seat chains and a separate requirement-based evaluator that forbids accept/reject labels. A closed study plan fixes per-call token caps, empty tool/web/network allowance, three paired replicates, zero retries, first-partial stop, no imputation, small-seed uncertainty disclosure, arm/expert blinding, and a human-verified unique verified value rule that credits only first-originated, evidence-anchored units retained in the final output. Four repository-owned synthetic fixtures, closed plan/seed/materialized-manifest schemas, and a suite-specific validator/dry-run/materializer produce 186 self-contained neutral prompt templates with complete hash-bound role contracts, dependency placeholders, conservative input-budget receipts, and dispatch/measurement permanently false. No subject, expert, adjudicator, model, API, tool, dispatch, or measurement ran; reviewer interpretation still depends on #653 and #582 remains open.

  • Offline candidate-ledger substrate (#655 Track A) (PR #719). Three closed Draft 2020-12 contracts now bind the complete high-impact consentable-plan projection, consistent known-reference or explicit-unknown provider retention disclosure, one visible initial attempt per planned query/index pair, hash- and time-bound retry authorizations, monotonic retained attempt/hit evidence, and explicit relevance success/failure evidence bound to the exact claim, candidate, assessor contract, and canonical prompt bytes. A pure local finalizer preserves failures and every raw terminal state, applies one NFKC visible-semantic-text boundary, treats only strict 10.<4-9 digits>/<suffix> values as DOI identity, blocks no-DOI bridges between distinct DOI components, and applies the frozen filter/dedup/canonical/top-40 rules. CLI persistence fails before path creation for session_only and writes once only for hash-bound explicit_local_export, using an exact hash-bound absolute output path, exclusive/no-follow creation, mode 0600, file/directory fsync, and truthful persistence/export/path metadata; exact replay needs no network, model, discovery adapter, stance classification, rendering, evidence-row changes, pipeline wiring, or dispatch. Synthetic fixtures, direct Unicode/DOI/persistence mutation regressions, an exact module-use-allowlisted no-capability integration guard, resolver byte pins, pytest-manifest registration, and spec-consistency wiring cover this substrate. The live probe and Track B remain unimplemented and unmeasured; #655 stays open.

  • Within-session ideation-diversity Phase-2 no-call envelope (#659). Closed Draft 2020-12 contracts freeze an exact 2-experiment x 6-scenario x 2-arm x 2-replicate, 48-session plan; bind assets, prompts, order, synthetic actor packets, and non-executable envelopes by SHA-256; and require tools/web off, transport none, no dispatch, USD 0 API spend, no fallback, and fresh external authorization. The offline runner ingests externally recorded transcripts with canonical hash-bound external-session receipts and closed canonical raw events; it derives message bytes and forbidden action classes from those retained bytes, requires unique fresh-session identities and monotonic cell timing, forbids stitched session lifecycles, and verifies structure rather than operator/recorder identity or genuine consent. Suite commit and token caps are explicitly operator-declared and unverified. Atomic complete-byte publication, compact hash-bound file/directory inventories, materialization-time per-cell attempt guards, primary/fallback durable stop slots, bounded terminal acquisition-failure evidence, and registered publication aliases prevent conflict/retry/orphan bypass even if state replacement fails. Unicode-normalized semantic leak screening covers the complete 48-cell identifier universe during every ingestion; messages require letters/numbers/symbols and execution/authorization identities require letters/numbers, rejecting separator/control/format/combining-only placeholders. After 48 valid ingestions a deterministic durable blind intent freezes a one-time private nonce plus all ids and hashes before exact-recoverable staging creates 48 isolated blind packets plus a finalized replay manifest; deterministic intent-bound hardlink aliases preserve and exact-replay cleanup-failure evidence without generating a second mapping. The 0600 intent and map remain procedural-nondisclosure artifacts withheld from judges, and staging collisions quarantine. A future closed assignment-ledger gate must still prevent same-judge cross-arm/replicate role-card exposure, and the bundle alone does not prove that blind. The private arm map is procedural nondisclosure only, explicitly unencrypted, and stored under 0700/0600 permissions. Packet flags claim only that no structured label/adjudication/human-evidence artifact is attached; two independent arm-blind human judges and a separate human adjudicator remain mandatory. No transport, model, subject, actor, judge, adjudicator, API, subscription, web, or tool call ran; no output or baseline was generated, production and historical claims remain unchanged, and #659 stays open.

  • Indirect prompt-injection Phase-2 no-call envelope (#675). The frozen eight-scenario 2 x 2 design now expands deterministically into exactly 64 fresh-context cells with two counterbalanced replicates, hash-bound prompts and non-dispatch call envelopes, closed exact-plan authorization and transcript/event contracts, append-only evidence ingestion, first-failure stop receipts, and 64 isolated runner-write-once blind-session packets. A pinned decoder derives action class from closed canonical native event types bound to exact session/cell/event identities; unique fresh-session receipts enforce cross-cell monotonic order, and mandatory observed usage plus conservative byte limits enforce the frozen token/packet caps. Materialization pre-arms immutable same-inode journal claims for every ingestion and the one blind transaction, so missing/symlink/oversize transcript acquisition and even primary stop-marker failure remain terminal before external bytes can be retried. A closed write-once stop intent with base64 replay, individually bound partial evidence, one compact unregistered file/directory-tree digest, and a separate pre-load quarantine make blocked, partial, semantic-leak, drift, and write failures irreversible without retry. The finalized blind manifest semantically replays the exact atomic bundle and supports crash-boundary finalization; its public inventory exposes only opaque packet ids and byte/hash bindings, while formal assignment metadata stays in an unencrypted procedural private map protected locally by 0700/0600 modes. Because packets necessarily expose exact external content, they do not claim injected text or canaries are perceptually blind; a future closed assignment ledger is still required, and the bundle alone does not prove arm blindness or prevent cross-condition judge exposure. Source-codepoint-atomic NFKD spaced, joined, and compact projections remove combining marks and neutralize format, punctuation, symbol, and separator obfuscation before checking full identifiers and complete frozen mapping/prior-label phrase cross-products; compatibility letters and numbers remain atomic so their decomposition cannot manufacture false boundaries. A runtime visible-semantic predicate rejects surrogates and text without a letter, number, or symbol, so blank, format-only, combining-mark-only, and punctuation-only subject outputs cannot become complete evidence. Run trees reject symlinks; blind construction uses one deterministic staging transaction that exactly recovers a complete residue and permanently quarantines incomplete or legacy residue instead of minting a second mapping. An exact AST module/symbol/attribute/call allowlist blocks aliases and module escapes as well as provider, network, subprocess, dispatch, probe, or model surfaces. suite_commit must equal local HEAD, but the plan honestly says live asset hashes do not prove a clean worktree or Git-object replay. The runner exposes only init/materialize/validate/ingest/blind preparation, fixes tools/web off, transport to none, API spend to USD 0, and fallback off, and fabricates no consent, subject output, labels, human judges, adjudication, or measurement. #675 remains open pending a separately authorized run, a closed assignment ledger, at least two independent arm-blind human judges, and a separate arm-blind human adjudicator.

  • Crash-boundary journal hardening for the #675 no-call envelope. Materialization now pre-arms a dedicated pre-load-terminal token in addition to the 64 cell and blind-bundle tokens. Any normal-load failure claims that same-inode token before submitted transcript acquisition, so a failed quarantine write plus restored plan bytes cannot reopen ingestion. Successful cells transition from claimed to same-inode completed only after the advanced manifest replacement and directory fsync succeed; a replace that publishes bytes but reports a durability failure remains claimed and is rejected by validation and the next ingest before another transcript is read. Direct mutation tests pin claim-before-read ordering, quarantine-publication failure, exact prior-versus-advanced manifest discrimination, post-replace fsync ambiguity, and the complete pending/blocked/ingested journal state matrix. No provider, model, subject, network, tool, dispatch, or measurement ran.

  • Search-bounded claim-standing probe design freeze (#655). The opt-in advisory is split into a retrieval/candidate-ledger track and a separate stance/presentation/measurement track. V1 deliberately limits eligibility to claims already classified HIGH-IMPACT under #549 rather than treating RANDOM, TOP-UP, or final-check ALL as consent for broader external transmission. The frozen design requires exact per-probe consent, new discovery adapters instead of widened resolver clients, visible raw hits and culling/failure reasons, deterministic work-family deduplication and top-K caps, abstract/full-text coverage labels, separate relevance/stance/not-checked states, an all-selected distribution denominator, #656-owned versioned evidence rows, and search-bounded wording with no scalar credibility score. No adapter, schema, held-out suite, search, model, judge, or efficacy claim ships in this design-only change; #655 remains open for implementation, independently labeled ground truth, and a valid baseline measurement row.

  • Within-session ideation-diversity Phase-1 freeze (#659). A bounded held-out design now separates the opt-in adjacent-framing probe from the exploratory convergence/challenge guardrails instead of treating unlike mechanisms as one intervention. Six repository-owned synthetic scholar-role cards form three English/zh-TW pairs with precommitted framing ownership, explicit eligible-population and actor-response rules, a frozen A1-A2/M1-M5 codebook, subject/actor/judge blind fields, and separate count, facet-family dispersion, follow-through, and violation metrics that can never be collapsed into a diversity score. Model-originated questions, hypotheses, rankings, menus, and off-card options cannot earn scholar-originated credit. A hash-bound replacement manifest materializes the guardrail ablation only as a new non-production prompt and refuses production-source drift or overwrite; the validator checks pair structure, directionless facets, design/codebook boundaries, strict JSON, and the ablation bytes. No subject, actor, judge, adjudicator, model, API, web, or tool run occurred; #659 remains open for its separately authorized per-mechanism baseline under the held-out measurement contract.

  • Executable indirect prompt-injection behavioral probe seed (#675). A frozen 2 x 2 paired-control design now spans synthetic web/source verification, PDF or manuscript ingestion, bibliography intake, and pasted reviewer/committee comments in English and zh-TW. The two factors separate benign/injected external content from an explicit ARS-guidance paragraph versus a non-production platform-only control, while every v0.1 cell keeps tools and web disabled. A strict schema, eight-scenario seed set, neutral hash-bound prompt materializer, closed mechanical response scorer, blinded C1-C4 adjudication rubric, CI asset gate, and mutation tests replace #272's constant-false runtime xfail. This delivery runs no model or judge and makes no safety, efficacy, or structural-enforcement claim; #675 remains unmeasured and #676 remains the structural boundary.

  • One review-target criteria authority across formative, internal, and external review (#684; runner fixes in PRs #709 and #713). A closed pointer-only manifest binds the author-confirmed #683 context and selected criterion pointers across FORMATIVE, INTERNAL, and the exact EIC/R1/R2/R3/DA panel. A deterministic explicit-path CLI initializes, marks, records, and validates receipts plus a separate Critical/Major constructive-finding sidecar. Formative planning cannot invent evidence; internal and external Phase 1 remain manuscript-blind; paper-visible findings require typed anchors, honest remedies, costs/trade-offs, and author choice for new-data or intent-changing options. Pipeline wiring carries one manifest by pointer, never fabricates skipped mid-entry receipts, and keeps binding conformance out of integrity verdicts, checkpoints, editorial arithmetic, and author triage. A preregistered same-context/same-budget paired harness and deterministic scorer cover profile resolution, applicability, unsupported findings, severity, venue alignment, and usefulness. Its amended plan defaults to 24 contained ChatGPT-subscription Codex CLI subject calls with human expert labels, applies the existing two-family judge rule through subscription CLIs first, has no automatic API fallback, and sets a USD 0 API spend ceiling. No live subject, judge, expert, API, network, or measurement ran, so behavioral effect remains unmeasured.

  • Contained ChatGPT-subscription transport for citation integrity (#630; PR #705; supersedes contributor PR #567). Stage 2.5 / 4.5 one-reference citation checks may explicitly select a Codex app-server adapter that requires the exact ChatGPT-subscription login attestation, honors custom CODEX_HOME, copies only bounded auth.json into an ephemeral private home, runs from an empty root under read-only/never with local tools and workspace capabilities disabled, and never accepts a caller-authored prompt or filesystem path. Positive sources are bound to exact structured standalone-search results by item id, result index, and digest; echoed URLs, unrelated queries, missing/malformed searches, multiple finals, forbidden tool events, invalid selectors, and wrong event shapes fail visibly. Closed request/receipt schemas, Bash 3.2 entrypoints, checked-in event fixtures, a fake app-server suite, and CI guards are fully hermetic; the manual live smoke is consent/cost-bound and excluded from CI. The selector is citation-only and does not replace API credentials for DA, reviewer, calibration, re-review, checkpoint, or handoff calls; no model promotion is included. Original approach and product requirement contributed by @dcs-scd in PR #567.

  • Prospective revision-claim-drift rubric v2 and subject-context protocol (#679; PR #702). The frozen v1 rubric, held-out set, both historical measurement rows, and every retained 2026-08-07 run artifact remain byte-locked and unchanged. A separately hashed v2 rubric adds a non-control citation-attachment criterion and points to the canonical claim-strength ladder instead of re-copying rung examples; an append-only amendment record explicitly leaves all historical headlines untouched. Future heldout-measurement/1.1 reruns must bind a closed launcher configuration, complete frozen-set subject/judge call plan, retained prompt/output artifacts, execution manifest, and subject-context record covering neutral physical cwd, repository membership, --bare authentication limits, instruction visibility, and a pre-fleet context probe or explicit operator attestation. C9 findings require typed raw judge flags and closed replayable evidence. Visible mechanism text is recorded as not_isolated, never laundered into an isolation claim. This delivery is protocol-only: it ran no subject, judge, adjudicator, model, API, or new measurement and changes no historical score.

  • Replay-bound cross-document consistency advisory (#672). New closed preregistration-artifact/1.0, exact two-artifact source-manifest, advisory-draft/final, and independent evidence-row/1.2 contracts bind caller-supplied abstract/results, discussion/results, methods/reported-analyses, and manuscript/preregistration observations to exact strict-UTF-8 sources. The non-shell research architect declares only status and a companion handle; the named deterministic builder alone creates the persistent sidecar, including unavailable receipts, and academic-paper plus every pipeline handoff replay and carry its bytes unchanged. Methods absence requires an exact named checked scope, while an undisclosed preregistration deviation requires a third exact disclosure-scope witness. The final carrier is LLM-ADVISORY / UNMEASURED, never a score, gate, PASS/FAIL, ClaimIntent, rewrite, consent/protocol duplicate, agreement, or clean-document certificate. At the single mandatory Stage-5 entry checkpoint, #660 runs first and #672 second against the identical accepted-draft ID/SHA; their carriers and failure semantics stay independent and nonblocking, and any revision stales both. All implementation and fixtures are hermetic—no live model, external API, judge, legacy multi-file audit, or expensive evaluation.

  • Merge provenance for the current feature deliveries. The tortured-phrase implementation shipped in PR #699; revision-roadmap authority in PR #697; content-coverage advisory in PR #696; submission-packet manifest in PR #695; shared evidence rows in PR #694; deterministic retraction status in PR #691; dissent raw-HTML enforcement in PR #687; and the bibliographic-integrity, held-out-measurement, and human-subjects boundary foundation plus its follow-up hardening in PRs #685 and #686.

  • Hermetic tortured-phrase screening for own drafts and cited metadata (#660). Three closed contracts and a hermetic runtime accept only an explicit user-supplied or clearly synthetic-fixture snapshot with a detached manifest bound to the exact raw snapshot SHA-256 and an explicit rights declaration. The repository ships no native PPS content, PPS importer, network fetcher, or redistributed phrase list, and the checker uses no live model, external API, human or model judge, ambient clock, source-file timestamp, or Git/network time. Own-draft output is a bounded HEURISTIC-ADVISORY / UNMEASURED artifact rendered immediately before formatting; cited-metadata enrichment is non-in-place and emits one current v1.2 row for each title and abstract surface, including explicit ABSTRACT_MISSING and ABSTRACT_EMPTY states. Read-only consumers compose all rows in the one existing Bibliographic Integrity Advisories section; the advisory mints no marker, changes no gate or finalizer policy, proposes no rewrite, and makes no clean-draft, AI/author/papermill/origin, contextual-validity, publisher-acceptance, or matcher-accuracy claim. Shipped fixtures are synthetic conformance inputs only, not an accuracy evaluation.

  • Post-main tortured-phrase synthetic conformance measurement (#660). The pinned deterministic runtime at commit 86bf0e5c2cedb300d6d1c6428470cdcedfbf97df passed 190/190 tests in the pinned repository-owned synthetic suite on the single pre-registered execution (exit 0), with the exact transcript and write-once execution manifest retained beside the heldout-measurement/1.1 row. This finite synthetic point estimate adds no claim about contextual validity, clean text, real-world false-positive/false-negative performance, paper-mill or AI origin, misconduct, contamination, quality, acceptance, or source provenance.

  • Non-ranking, explicit-author revision authority (#670). Schema 7 now has a closed immutable revision-roadmap/1.0 core that keeps transported reviewer severity, editorial obligation_class, typed cost surface, bounded consequence, deterministic source order, and exact proposed block/operation targets independent. Explicit session-author choices live in a separately raw-hash-bound author-adjudication/1.0 sidecar, with complete triage, decline reasons, exact target subsets, exact registered-claim replacement bytes/rungs, and exact declined-overlap collateral authority; presentation views never alter R<n> transport references or gate arithmetic. Claim surfaces are exact UTF-8 spans whose protected text must equal the referenced ClaimIntent claim_text, preventing a valid claim id from being paired with unrelated prose. Integrity issue lists and gate failures are proposal-only: a distinct explicit-author input must approve both exact targets/operations and the complete patch SHA-256 before the deterministic builder can emit a base/list/round-bound integrity authorization sidecar. Current patch 1.1 and apply-report 1.3 replay the appropriate disjoint authority branch before any write, reject patch 1.0 on the current CLI, protect registered claim surfaces exactly, and disclose the remaining unregistered-semantic-drift E6 boundary. A closed Revision-Evidence Bundle carries an exact integrity-PASS start through continuous review-write, all-declined no-op, and explicitly authorized integrity-correction rounds to the final draft, with contained read-once artifacts, deterministic patch replay, and byte-exact post-draft/report verification. The #576 current family moves in lockstep to contract 1.1, obligation_class/renamed residual and rate fields, exact Schema 11 author-field copies, and five hard-required artifacts: original manuscript, revised manuscript, roadmap, author adjudication, and Revision-Evidence Bundle. Missing-original, first_link_not_run, and old-report patch-binding degradations remain archived 1.0 behavior; current/legacy schema IDs and runtimes are isolated. All acceptance is hermetic—no live model, external API, judge, or scored evaluation.

  • Replay-bound authority-profile content-coverage advisories (#681). A separately versioned evidence-row/1.1 surface and closed content-coverage-advisory/1.0 carrier now bind bounded passages from explicit session-held strings to exact #666 authority requirements and structured_expectations[] pointers after full #667 packet-artifact replay. The LLM-ADVISORY layer preserves every deterministic packet status, readiness value, caller-supplied authorization field, institutional-acceptance boundary, pointer, and digest; structural gaps, external dependencies, waiver/exception boundaries, applicability-false exclusions, and missing session content cannot be laundered into semantic missing-element findings. Every profiled expectation is explicitly checked or explicitly not_checked, quotes retain #656 strict decoding, UTF-8 span/hash, 25-word/1,000-character, rights, inert-rendering, and human-read boundaries, and the finalizer opens only named inputs without directory scans, retrieval, network, subprocess, cache, or model calls. The surface is honestly UNMEASURED—no scored held-out row or efficacy claim is fabricated.

  • Deterministic, authority-bound submission-packet manifests (#667). Two closed V1 contracts now separate an author-declared inventory of explicitly named packet files from a replayable, pointer-only manifest. A standard-library checker first replays the exact #666 context/registry/resolved triplet, records declared-vs-attached observations for every explicitly inventoried file once the gates permit packet access, and then classifies only source-profiled submission_packet evidence entries as DOCUMENTED, NOT_LOCATED, CONFLICTING, or ACCEPTANCE_UNVERIFIED; a closed authority or capability gate is instead recorded in explicit APPLICABILITY_UNRESOLVED reason rows. It never interprets attachment prose, derives an IRB pathway, verifies institutional acceptance, or promotes caller-supplied authorization. Seven author-declared capability-envelope facts fail closed outside the bounded non-clinical, single-institution V1 case, while actor/holder scoping keeps IRB- and committee-owned records from becoming investigator gaps. Exact paths, file hashes, sizes, holders, authority/evidence pointers, semantic digests, additive waiver-route handling, canonical ordering, and full replay validation are enforced hermetically without directory scans, network access, model calls, or fabricated live institutional rules; the result remains a read-only deterministic input for the separately advisory #681 layer.

  • Shared, source-bound evidence rows at Phase E checkpoints (#656). Stage 2.5 and Stage 4.5 Claim Verification Reports now persist one closed evidence-row/1.0 row per selected claim × citation/anchor tuple and render deterministic 25-row Markdown or HTML pages at the mandatory integrity checkpoint. A single provenance vocabulary distinguishes exact once-decoded quote matches, agent-extracted page/section passages, unconfirmed anchors, not-checked, missing-source, access/retrieval-failure, and anchorless empty states; only exact substrings of explicit session-held source text can become excerpts, and every source-bound row must replay against the explicit session source map before validation or display. Rows bind exact UTF-8 source/excerpt hashes, byte spans, extraction time, cache replay, rights/private-data handling, and row digests while preserving the existing Phase E verdict/gate. Strict decoding, 25-word/1,000-character bounds, inert external-text rendering, unlimited persisted rows with bounded paging, no display-time retrieval/model calls, and human-read-ledger noninterference are enforced hermetically. V1 leaves the opt-in Stage 4→5 claim_audit_results[] lifecycle unchanged.

  • Deterministic human-subjects review-pathway rule traces (#669; PR #704). A standard-library-only local builder exposes the predicate work for caller-named candidate institutional pathways without selecting, ranking, predicting, or authorizing any pathway. Completed and halted traces bind the exact author context, authority registry, resolved context, request, and source provisions; every result remains institutional determination required, carries the #665 human-subjects boundary, and is never a gate, verdict, readiness signal, model input, or substitute for institutional review.

  • Optional cross-run adjudication-activity observability (#673; PR #703). An explicit user-selected local store can retain replay-validated facts about adjudication actions from eligible terminal runs and render a bounded recent series. Terminal state is made durable before best-effort capture; store failure cannot change a transition, decision, verdict, or checkpoint. The feature defines no target, threshold, score, engagement judgment, or human-ownership claim, enters no model prompt or handoff, performs no network/model/API call, and remains advisory only.

  • Human-subjects references migrate to exact authority pointers (#680). The shared decision aid, ethics checklist, terminology glossary, and their agent consumers now collect portable study facts and consume replay-validated #666 profile requirements instead of presenting Exempt / Expedited / Full Board, Taiwan process notes, fixed online-survey metadata promises, or retained-link-key terminology as universal rules. Covered consent and GDPR duties point to exact bounded registry requirement ids; unprofiled pathway, institutional-process, population, and data-protection material stays visibly illustrative and institution-owned. A hermetic migration checker pins the #665 non-authorization boundary, validates every named requirement against the local registry, prevents committee-governance rows from becoming investigator packet duties, keeps agent mirrors synchronized, and blocks reintroduction of the contradictory pathway and privacy claims without changing registry bytes or calling a live authority/API.

  • Explicit two-axis human-subjects authority context (#666). Three closed V1 contracts now separate an author-confirmed context record, a curator-owned bounded authority registry, and a pointer-only resolved trace. Exact id/version/digest/scope pins select plural review_ethics and data_protection profiles; a standard-library offline resolver evaluates a typed Strong-Kleene predicate AST, preserves every base/overlay requirement and collision, and blocks profile-dependent results whenever either axis, source currency, or applicability remains unresolved. The shipped registry demonstrates one common schema across bounded US 45 CFR 46, Taiwan Human Subjects Research Act, and GDPR research subsets with row-local official-source anchors, actor/consumer scoping, explicit exclusions, and no completeness or legal-advice claim. Institutional and funder overlays are additive only, display precedence cannot suppress obligations, and hermetic schema/mutation tests require exact selection, duplicate-key rejection, stable digests, no jurisdiction guessing, and the existing #665/#668 non-authorization boundaries.

  • Deterministic retraction status with one advisory-first authority (#651). The #678 carrier advances to a backward-compatible v1.1 retraction row that separates OpenAlex/Crossref observations, detects both updated-by and inverse update-to relations, preserves reinstatement/disagreement/degradation, joins HIGH-IMPACT claims by worst tier, compares retraction timing only to source_acquisition_date, and mechanically recognizes declared legitimate use only when an auditable declaration and retraction-notice citation are both identified. OpenAlex now retains is_retracted in its existing select response; a distinct versioned SQLite cache exposes 30-day staleness without coercing unknown/degraded rows to clean. Detection is always visible in Bibliographic Integrity Advisories and mints no second advisory marker token. The citation finalizer is the sole authority for the new absent/advisory-by-default, explicit-strict terminal_policies.retraction key; only current, undisputed, non-exempt retracted rows can emit the existing generic HIGH-BLOCK token. The ethics agent now points to that result instead of maintaining a conflicting CRITICAL rule, and legacy retraction_check is read-only execution-attestation compatibility. The hermetic acceptance suite covers 39 resolver, cache, policy, projection, authority, schema-mutation, and CLI cases without live API calls or tokens.

  • Complete concern accounting for real committee correspondence (#668). The existing academic-paper revision-coach entrypoint now has an explicitly activated committee-correspondence variant with its own closed human-subjects schema and protocol, never the peer-review Schema 11 taxonomy. A byte-bound bundle preserves the full UTF-8 source (including non-comment material), maps each confirmed comment segment to exactly one source-order concern, supports compound multi-label actions and optional profile enrichment, and emits a placeholder-only response skeleton under the #665 non-authorization boundary. A deterministic checker recomputes containment, hashes, contiguous byte coverage, locators, provenance, dependencies, non-destructive working views, and one-marker-per-concern response coverage; 29 tests cover the wired entrypoint, valid degraded-mode fixture, and drop/duplicate/reorder/tamper/path/profile/false-resolution mutations. No priority, severity, satisfaction prediction, or claim of resolution is representable.

  • Author-confirmed review-target context and versioned criteria registry (#683). Three closed V1 schemas separate the author's discipline/venue/track/contribution-type/overlay declaration, a four-part provenance-and-freshness registry, and a pointer-only resolved context with distinct scientific-validity, venue-fit, and submission-readiness outcomes. A stdlib-only deterministic resolver emits stable criterion/profile digests and a Phase 0/1 brief; it never reads a manuscript, infers a missing venue, averages interdisciplinary conflicts, or applies numeric weights. Stale and unverified sources are structurally advisory-only, unknown venue profiles stay visibly unresolved, and the shipped registry contains only a field-general baseline while synthetic fixtures exercise exact venue × track × contribution-type matching. Twenty-five registered tests cover schema closure, provenance partitions, fallback states, digest sensitivity, conflicts, freshness/as-of handling, explicit selection, and a sibling-manuscript sentinel.

  • August harness-retirement removes a conflicting peer-review scaffold (#617). The academic-paper peer reviewer now has one canonical report template instead of two overlapping shapes, and its review workflow no longer simulates a 1520 minute human read, records unobservable gut reactions, or creates a second initial-impression score beside the evidence-weighted score. Strengths are evidence-led with zero allowed and a checked-dimensions receipt, replacing the fixed three-item quota that could manufacture praise. The five-dimension rubric, verdict mapping, issue bands, revision loop, and all integrity/phase contracts remain intact.

  • Dissent-span non-comment raw-HTML hiding is machine-rejected (#682). check_phase_conformance.py now rejects raw HTML tags and delimiters inside ## Scoring Plan Dissent as [DISSENT-RAW-HTML], including script/style/template blocks, hidden-element wrappers, container-prefixed tags, declarations, processing instructions, and malformed openers. The guard is span-scoped and code-aware: inline-code mentions remain prose, fenced examples and content outside the dissent span keep their prior semantics, and comment-only cards retain the existing [DISSENT-HIDDEN] path. Canonical Phase 2 prompts, all five seat mirrors, the protocol, and the report template state the same grammar, so no hidden field can mint a trigger-binding exemption.

  • Held-out measurement contract 1.1 adds execution and preregistration provenance (#664). New reports now distinguish treatment/cohort arms from variant-packet arms, identify each judge's blinding dimensions, declare whether the headline is a point estimate or lower bound, and bind timing/sequence claims to a hashed execution manifest with call IDs and timestamps. Frozen plans and adjudication rubrics are hash-pinned before outputs, adjudication rows record resolution direction/rule, flags-only lower bounds cannot be promoted to two-sided claims, and preregistration amendments are append-ordered. Version 1.0 is accepted only for the exact path and hash of its frozen historical row; a canonical 1.1 template, execution-manifest schema/fixture, I13-I15 + R4-R5 validation, and CI drift guards define new work.

  • Human-subjects output is explicitly institution-owned (#665). Research planning now reports candidate pathways plus independent submission-readiness and authorization states, never an IRB determination or fabricated institutional timeline; the ethics reviewer scopes its verdict to AI-assisted research integrity and carries a fixed institution-owned footer. The ethics checklist/decision tree are labeled navigation aids pending institutional determination, with a CI-locked output contract and mutation tests while the pre-existing Ethics Decision Log stays byte-stable.

  • Product positioning states the empirical-verification limit and rejects simulated review boards (#657, #671). POSITIONING.md now distinguishes manuscript/process/package conformance from validation of actual execution, raw-data truth, or reproducibility, and all maintained README languages point to that boundary. It also rejects a simulated human-subjects committee as a statutory composition/accountability claim, leaving only non-authoritative question generation as a possible future RFC surface.

  • Canonical bibliographic-integrity signal carrier (#678). A closed versioned schema and additive literature-corpus array separate resolver/list observations, heuristic indicators, and check-execution attestations; carry evidence/provenance, timestamp/version/hash/staleness, affected citations/claims, explicit policy ownership, and unresolved degradation states; and round-trip #651 retraction plus #660 tortured-phrase fixtures without undeclared fields. A deterministic legacy projection covers contamination_signals, omission metadata, and retraction_check without treating an execution attestation as a clean result. New signals compose in one provenance_summary.md section and mint no ref-marker token, preserving the existing single CONTAMINATED-* advisory slot and terminal policy until a separately versioned cutover. Formatter/finalizer sync checks, migration tests, and CI enforce the boundary.

  • Citation gold-set documentation separates coverage from representativeness (#674). The missing genuine all-resolver-unmatched tuple is now consistently described as #250's accepted corpus-representativeness limit with no current action item. Static authored-outcome fixtures are no longer claimed to exercise title matching; direct client HTTP-path regression coverage is attributed to the #431 fix shipped in #432. No tuple, reducer, resolver, or policy behavior changed.

  • Pi installation guidance is visible from every maintained README (PR #642). The post-merge community wrapper is installed with pi install git:github.com/Imbad0202/academic-research-skills; each README points to pi/README.md, which keeps the original ARS content authoritative and documents Pi-specific orchestration and hook limitations.

  • Held-out measurement contract: multi-judge, pre-registered-adjudication report envelope (#654). New evals/heldout/MEASUREMENT_CONTRACT.md + evals/heldout/measurement_report.schema.json + authoritative evals/heldout/suite_registry.json define the opt-in heldout-measurement/1.0 envelope for scored held-out rows — the premise correction from #654 applies: scripts/run_evals.py still discovers evals/gold/ only, and the contract is a report schema + per-suite adoption notes, never a runner. Scored decision-relevant runs disclose their judges fully (exact model id, model family, prompt ref, evidence provided, judging budget per judge; different-model-families and preregistered maintainer adjudication labeled as ARS choices layered on Ren et al. arXiv:2607.13104 §8.1.2), enumerate cross-judge divergent items individually, replicate >= 2 per item, and publish raw pre-adjudication numbers alongside adjudicated ones against a rubric committed and hashed before any judge output exists, with the adjudicator's blinding dimensions enumerated and every override citing its rubric criterion. The contract's headline rules are structural, not self-declared: the judge minimum is derived from (decision_relevant, suite_class) — >= 2 judges from >= 2 distinct model families, with no author-writable minimum_for_scored field to relax it; a report's suite_class is verified against the suite registry so mislabeling cannot shed clauses; the agreement rate is recomputed from the per-judge rows rather than trusted; the honesty attestations (rubric_precommitted, raw_published, raw_outputs.retained) are schema const: true; and report loading rejects duplicate JSON keys so a second "raw_published" cannot shadow the first. judge_plan.exception: "legacy_comparability" encodes the #652 pattern (a re-measurement keeps the original judge as its comparability row; new judges report separately); mechanical_suite is legal only on mechanical_match suites; E4-shaped suites adopt at the cohort roll-up level (a summary row referencing runs/ records — per-run shapes emitted by dispatch_e4_panel.py unchanged). Dual-track pre-ship review (security opus subagent + codex gpt-5.6-sol xhigh) drove a hardening round: judge identity hygiene (duplicate judge ids, one model under two family labels, duplicated (model, prompt) configurations all rejected), NFKC + format-character-stripped item-id folding (the #524 fold-before-compare lesson) so zero-width re-spellings cannot split or hide divergence, type-aware payload comparison (JSON true != 1), declared-divergence set equality with per-item resolution binding (every divergent item needs its override or note), blocked-run coverage for partial judge failure on decision-relevant runs, per-row evidence minimums, real-calendar-date validation, and reference resolution R1-R3 (rubric file must exist and match its sha256, raw-output paths must exist, the suite commit must be a real object) plus location binding L1 (a row filed under a suite directory must declare that suite). Enforcement: scripts/check_heldout_measurement_report.py (JSON Schema branches B1-B4 + invariants I1-I12 + R1-R3 + L1, 109 mutation tests incl. CLI-level scan tests via the unified manifest) wired into spec-consistency CI in --all mode, which discovers by the marker key across evals/heldout/ (case-insensitive extensions, directory symlinks followed with a cycle guard) — filename conventions do not gate validation; marked files that fail strict parsing (duplicate keys, non-finite numbers), near-miss or non-string marker values, escaped-key spellings, non-UTF-8 bytes, and out-of-repo symlink targets all fail loudly. A second verification round closed the reviewers' remaining must-fix items (depth-1/symlink location bypasses, escaped-marker discovery regression, token-boundary blocked-run matching, folded identity fields, 40-hex commit pinning, raw-path suite scoping), and MEASUREMENT_CONTRACT.md gained a Known-residue section naming what stays human-audited (verdict semantics, decision_relevant self-declaration, aliased judge identities, pre-registration history, override transcription). The five suite READMEs gain adoption notes; legacy rows (2026-07-11, 2026-07-22, the E4 cohorts) are never retrofitted; shared/benchmark_report_pattern.md is cross-referenced as the sibling envelope for ARS-versus-human benchmark reports so a third vocabulary never grows.

  • Dissent-span comment hiding closed by an output-grammar rule (#613; PR #650). The two CommonMark comment shapes the #612 block-visibility model deliberately did not read — a <!-- following text on its own line, and one indented as a lazy paragraph continuation — could still hide canonical dissent fields from the rendered page while the parser credited them, silently granting a Phase 1 trigger-binding exemption for a dissent no reader sees. Closure is the rule #612 anticipated rather than more block-structure modelling: the delivered Phase 2 prompts (scoring-phase2 + da-phase2 canonical fragments, all five seat mirrors regenerated, content lock repinned) and sprint_contract_protocol.md now require comment syntax mentioned in reviewer prose to be written in inline code, and inside the dissent span the parser reads a bare <!-- as opening an HTML comment WHEREVER it appears (span-scoped _inline_comment_state_after, delimiter-order-resolved, code spans blanked first so the sanctioned `<!--` mention stays prose; the block-position model outside the span is unchanged, as are the receipt-gate visibility walks). A field hidden by any comment now aborts as an unparsed occurrence instead of being credited. The ten declared-limit / code-not-a-comment / cannot-interrupt pinned-test families that documented the accepted misses flip to loud-abort expectations — their docstrings now state that the output grammar, not renderer modelling, decides the question inside the span — and a new pin covers the close-and-reopen mid-line case that must still parse. The wider #613 channel (non-comment raw HTML such as <script>/<template>) is out of scope here and stays open on the issue. Dual-track round 1 (security opus subagent) closed one P1 and the operator-surface gaps: _blank_code_spans now blanks backslash-escaped backticks first (CommonMark §2.4 — an escaped-backtick "span" blanked a live marker and credited a hidden dissent), a span-scoped paragraph run-parity guard stops trusting local blanking once backtick runs stop pairing (the cross-line code-span shape), hidden-field aborts carry a distinct [DISSENT-HIDDEN] marker instead of misattributing to line grammar, the rule sentence extends to raw HTML generally with the abort-even-when-nothing-hidden consequence stated, the Phase 2 terminal preflight gains the no-bare-markup self-check item (fragments + protocol + check_role_scoped_contract.py witness re-pinned in the same commit), and the protocol states honestly that the non-comment raw-HTML channel is grammar-forbidden but not yet machine-detected. Codex round 1 added the empty-comment closer overlap (<!-->/<!---> close by reusing the opener's dashes — the inline scanner now resumes at +2 like the block scanner, so an empty comment no longer false-aborts rendered fields) and a genuine mid-line close-and-reopen pin.

  • Standing preferences via CLAUDE.md documented as the supported mechanism (#634). docs/SETUP.md + docs/SETUP.zh-TW.md gain a "Standing preferences via CLAUDE.md" section: a copy-pasteable preferences template (citation style, preprint exclusion, journal-tier preference, OA preference), the two honest limitations (journal tiers are model judgment — no quartile data exists in any of the four lookup indexes; there is intentionally no output-directory setting, per the v3.6.8 R4-003 single-discovery-anchor decision), the boundary against search_strategy (the ambient block sets defaults; the Annotated Bibliography's Schema 2 search_strategy is the auditable record for anything that changes what a systematic review may include), and the recorded re-evaluation triggers. Converts the #632 discussion answer into durable documentation and states "no config layer" as a design position rather than a gap; the env-flags table cross-references it as behavior-toggles vs content-preferences. Docs only — no agent, schema, or behavior change.

  • #610 §8 step-5 script adapter: isolated numeric input surface + deterministic receipt calculator (#610). The methodology seat's arithmetic moves from the model to the harness, with the receipt seam unchanged. A new paper-visible extraction call (dispatched between Phase 1 and Phase 2, methodology seat only) has the seat transcribe manuscript-reported values into a closed typed ## Recompute Extraction grammar — one ### RR<n> per arithmetic claim with per-procedure numeric fields and explicit unavailable sentinels, or the no_recomputable_statistics: attestation; no arithmetic, no judgment. A new deterministic, stdlib-only calculator (scripts/recompute_receipts.py) computes every receipt from the extraction alone (it never sees the manuscript, the manifests, or the repo): GRIM/GRIMMER/n_from_df on exact rational arithmetic (fractions.Fraction; SD reachability decided on SD² so no square root enters a verdict; GRIMMER by budget-bounded exhaustive enumeration that refuses with reachability_not_completed rather than approximating), t/F/χ²/z tails via stdlib regularized incomplete beta/gamma with a 1e-9 boundary guard that resolves endpoint grazes to rounding_boundary_ambiguous / inequality_unresolvable rather than a float coin-flip, §5.1's both-tails display generated structurally under an unstated tail (F/χ² normalized to their family upper-tail), and every unavailable sentinel mapped to its closed not_computable reason deterministically; not_applicable stays grammar-legal but is never script-emitted. The seat's Phase 2 receives the computed receipts as a <computed_receipts> block and must reproduce them verbatim, adding only the finding_ref: linkage lines on mismatch receipts. check_phase_conformance.py gains the --extraction stage (machine-lines-only section, RR field matrix, anchor grammar; the calculator's own parser is the grammar authority so gate and consumer cannot disagree) and the --injected-receipts identity gate ([RECEIPT-IDENTITY]: the card's receipt section, read fence-transparently, must equal the injected bytes modulo canonical finding_ref: lines — a paraphrased derivation that still passes the receipt grammar is exactly what this gate exists to catch). dispatch_e4_panel.py dispatches the three-call methodology shape under successor evidence contract reviewer-e4/2026-08-06: the extraction call carries one structural retry of the Phase 1 evidence class recorded under its own extraction_retries list; a calculator refusal of a gate-passed extraction is a panel-fatal harness infra fault ([RECOMPUTE-CALCULATOR], exit-2 class, stderr preserved in methodology.recompute.log), never a conformance abort; methodology.receipts.md joins the bundle. Prompt-side, the canonical source gains a methodology-extraction fragment (new dispatcher-visible ### Phase 2E mirror in the methodology agent, sync-linted and content-locked like Phase 1/Phase 2) and the methodology-receipt fragment gains the injected-mode conditional — no <computed_receipts> block, no behavior change: self-compute #644 behavior is the explicit degradation path for every orchestrator that cannot run scripts, so nothing degrades for Bash-denied environments. Design doc docs/design/2026-08-06-610-step5-script-adapter-spec.md records the approved architecture and the measurement design: a separately authorized script_adapter smoke + 2 × 3 fleet from a frozen post-merge checkout, compared against the 2026-08-05/06 post row (recompute 4/4 with arithmetic correctness now structural; recall/clean/severity/abort gates per §7.1), with per-RR extraction fidelity and the calculator's not_computable reason distribution as new report rows. Dual-track pre-ship review (security opus subagent + codex gpt-5.6-sol xhigh) closed 2+4 P1 and 5+5 P2 findings in round 1 — extraction-call injection boundary (DATA_BOUNDARY + fragment paragraph), shared numeric-domain caps with verified-convergence p-magnitude bounds, GRIM window / GRIMMER element budgets and a calculator subprocess timeout, analytic-complement tails (a near-zero t/F no longer collapses to p = 1), rule-true rounding_interval: rendering, negative-SD mismatch, n = 1 population SD, family-consistent df shapes at the gate, CommonMark-stateful extraction fences, machine-markup/CR bans in pass-through values, plain-only finding_ref: under identity, LF-pinned write-once receipts, and contract witnesses (RECEIPT-IDENTITY: PASS gate-log line + resume-time deterministic receipt re-derivation) — design doc §7 carries the closure list, including the round-2 verification closures (10-decimal domain with an out-of-domain-no-RR fragment rule, near-1 display precision, parity-true half-even and zero-covering truncation intervals, GRIMMER iteration budget, argv-free timeout log, corner-swept convergence domain, content-line identity as the stated contract). 78 calculator tests (spec §5 worked cases pinned, published critical values, cross-family identities, brute-force enumeration cross-check, determinism, gate-grammar compatibility of every emitted line, review-hardening regressions), 22 new conformance-gate tests, 7 new dispatch tests (three-call order, retry evidence, panel-shrink, infra-fault classification, prompt injection, tamper detection, end-to-end mismatch receipts), 3 new resume tests, 3 new sync mutation tests.

  • 2026-08-05/06 #610 post cohort promoted and adjudicated (#610). Byte-for-byte promotion of the seven-panel post fleet at the frozen #644 receipt-grammar SHA 305884b under the #608 harness (claude-opus-5 @ xhigh, isolated per-seat two-phase claude -p --bare dispatch, --tools ""): six score-eligible records (smoke ms00 r99 + MS00 r2 + MS01 r1/r2 + MS02 r1/r2) in runs/, one blocked panel (MS00 r1, [TRANSPORT: exit 1] at synthesis dispatch with all five seats complete and the severed partial preserved — a transport failure, not a conformance abort) recorded — not replaced — in runs/blocked/; provenance valid on all seven, check_e4_promotion.py PASS on every stem. Maintainer-adjudicated 2026-08-06 against the held-out manifests outside any session. The #610 recompute-specific gate passes in full: receipt-backed strict recompute recall 4/4 VERIFIED in both score-eligible MS01 replicates under the mandatory formal AR<n> grammar (baseline 3/4 / 2/4 on prose receipts), receipt coverage and arithmetic correctness 1.00 per replicate — the two adjudicated baseline drop points (SD-03's both-tails display; SD-01's rounding-interval reachability) are now carried in the raw output, and SD-11 carries its required formal reachability fields as well. Strict recall stays 1.00 on both fixtures (critical band 1.00), class-wide statistical recall 5/5, clean-control false findings 0 (0 numeric / 0 narrative on both eligible clean panels), and the conformance-abort rate is 0/6 with zero retries fleet-wide (baseline 2/6) — the mandatory Phase 2 grammar cost no abort rate. Severity agreement is 0.607 against the baseline 0.672 and FAILS as frozen-measured, so the #610 delta as a whole does not pass the full pre-existing gate set; the result is recorded with its decomposition per the #581 precedent. An adjudication-consistency correction is part of this record: first-pass post scoring gave MS01 SD-01 a 1.0 on the same highest-tag-Major evidence the baseline had scored 0.5; the pre-merge cross-review caught the inconsistency and SD-01 was re-scored 0.5 in both post replicates before promotion. Decomposition: MS01 drops 0.705 → 0.659 (its movement is SD-08 and SD-09, one band in one replicate each) and MS02 drops 0.639 → 0.556 (the MS02 movement is one band each on its SD-03/SD-04/SD-08/SD-09, SD-07 improving one band); the MS02 receipts yield no computable mismatch signal (predominantly not_computable), so no direct receipt-verdict linkage is evident, but causal attribution for the severity drop remains unresolved — consistent with the open #574 A3/B1 band-scatter residual. The README run-history table gains the post row with the full gate verdicts.

  • #610 §7 step-3 behavior change: formal AR<n> arithmetic-receipt grammar for the methodology seat (#610). The methodology reviewer's sprint Phase 2 card now carries a mandatory ## Arithmetic Receipts section — one ### AR<n> receipt per attempted recomputation under the four bounded procedures (p_from_test_statistic / grim / grimmer / n_from_df), or a single no_recomputable_statistics: attestation when nothing is recomputable — a mandatory declaration with adjudicated honesty, never machine-checked triggering: the checker verifies only that the declaration exists, annotates the attestation path with a [RECEIPT-ATTESTATION: declaration-only …] advisory line, and a false attestation surfaces at adjudication as MISSED. Each receipt carries the spec §4 logical fields as canonical key: value machine lines with closed procedure_id / status / 16-reason not_computable_reason enums, and the grammar forces the two adjudicated baseline drop points as dedicated conditional fields: an unstated-tail p receipt claiming a verdict must display BOTH labeled tail values, each label sharing its own ;-segment with its derived number (either order), hyphen/Unicode label variants folded with letter boundaries (the SD-03 two-tailed-only failure), and a GRIM/GRIMMER verdict requires explicit rounding_interval: + nearest_achievable: lines (the SD-01 r3 missing reachability check); n_from_df verdicts must name their df_identity:. Every mismatch links to exactly one W<n> weakness that carries a **Arithmetic Receipt**: AR<n> back-reference; consistent/not_computable/not_applicable receipts can never mint a finding. Prompt-side, the receipt block leads with an epistemic-status declaration — the layer does not replace the human reviewer; conformance proves auditability, never arithmetic truth, and correctness stays with maintainer adjudication (VERIFIED/CLAIM_ONLY/MISCOMPUTED/MISSED). Mechanically: the canonical prompt source gains a methodology-receipt fragment that check_reviewer_sprint_prompt_sync.py splices into the shared scoring fragment at the terminal-preflight anchor (shared text exists once; the composed methodology mirror is byte-pinned and the splice-anchor bytes are part of the canonical content-lock digest; 7 new sync mutation tests); check_phase_conformance.py gains the methodology-only [RECEIPT-MISSING/-GRAMMAR/-TAILS/-LINKAGE] gate plus [RECEIPT-SECTION-FORBIDDEN] on every other seat (140 new mutation tests) — the receipt section is required to be the card's final H2 (a 1-3-space-indented ## counts as a rendered heading for that rule) and is read fence-transparently in display form (#637/#609 discipline: fenced receipt content is read, an indented fence dedented as CommonMark renders it, and a fenced AR block cannot hide beside an unfenced attestation); the section is a comment-free zone (any unfenced HTML comment markup aborts — a paragraph-inline prose <!-- opener the block visibility model cannot read would otherwise launder the receipts below it) and shares the dissent gate's visibility model for comments opened outside it, so a machine line or ### AR<n> heading the rendered card does not show aborts instead of being credited; receipt-field-shaped and back-reference-shaped lines are detected decoration-agnostically (_is_dissent_field_shaped construction, per-table-cell with link/code/escaped-pipe spans excluded from cell splitting, HTML entities unescaped before the NFKC fold, bold balanced-or-absent) so an unenumerated decoration aborts loudly instead of silently passing or starving a guard; a canonical machine line outside every ### AR<n> subsection (other than the attestation) aborts rather than parking un-inspected; and the **Arithmetic Receipt**: back-reference value must be exactly AR<n> with per-cell declared-vs-parsed equality so a malformed declaration cannot ride beside a canonical one, while a back-reference in a fenced, commented, paragraph-inline-commented, or indented-code rendering context aborts rather than earning linkage credit. A new check_receipt_enum_sync.py defrift lint pins the four closed enums to set equality across the prompt fragment, the checker frozensets, and the spec §4 (parse scoped to §4 so a non-normative decoy cannot mask drift; 11 mutation tests, CI-wired); statistical_reporting_standards.md gains §8 with the four procedure summaries, three new arithmetic red-flag rows, and the spec-required covered/reporting-only/direct-threshold/not_computable classification of every arithmetic red-flag row; sprint_contract_protocol.md gains §5.1 and the receipt diagnostics. Phase 1, the other four seats, the DA/synthesis grammars, fixtures, and dispatch_e4_panel.py are untouched; the new gate reaches dispatch through the existing conformance hook, and its Phase 2 abort-rate cost is a tracked §7.1 metric. Post-condition per the spec: prose receipts no longer count — the formal grammar is mandatory for the post fleet, whose gate is 4/4 VERIFIED per score-eligible MS01 replicate.

  • 2026-08-04/05 #610 v0.2 baseline cohort promoted and adjudicated (#610). Byte-for-byte promotion of the nine-panel corrected cohort at the frozen post-#638 SHA 112a869 under the #608 harness (claude-opus-5 @ xhigh, isolated per-seat two-phase claude -p --bare dispatch, --tools ""): seven score-eligible records (smoke ms00 r99 + MS00 r1/r2 + MS01 r1 + MS02 r2 dispatched 2026-08-04, plus authorized supplementary replicates MS01 r3 + MS02 r3 dispatched 2026-08-05 to satisfy the two-replicate rule) in runs/, two blocked panels (MS01 r2, MS02 r1) recorded — not replaced — in runs/blocked/; provenance valid on all nine with every retry and checker output preserved, check_e4_promotion.py PASS on every stem. This cohort is the adjudicated #610 v0.2 baseline: maintainer-adjudicated against the held-out manifests outside any session, with MS01 strict recall 11/11 on both replicates (prospective SD-11 GRIMMER included), MS02 9/9 on both, clean-control false findings 0 on all three clean panels (0 numeric / 0 narrative), and severity agreement 0.672 on the frozen highest-tagged-seat ladder. Both MS01 records additionally carry the #610 recompute_adjudication block, adjudicated per case against the spec's §5 procedure definitions: SD-02 n_from_df and SD-11 grimmer VERIFIED in both replicates; SD-01 grim VERIFIED in r1 but MISCOMPUTED in r3 (integer-product argument without the required rounding-interval reachability check); SD-03 p_from_test_statistic MISCOMPUTED in both (two-tailed-only comparison against the both-tails rule for an unstated tail) — receipt-backed strict recompute recall 3/4 (r1) and 2/4 (r3), the honest baseline the #610 receipt-grammar change is meant to move; content-level per_defect detection is unaffected; class-wide statistical recall 5/5 per replicate (the four recompute rows plus MS02 SD-07 reporting_only). The #637 Markdown-decoration abort family did not recur — zero [SYNTHESIS-PARSE: found 0] events fleet-wide against 6/6 first-attempt failures in the attempt fleet — confirming the #638 tolerance fix; the conformance-abort rate drops 0.50 → 0.33, with the residual a different single-seat content-grammar family (domain.phase2 [ANCHOR-INVALID] absence anchor missing <where>; da.phase2 [DA-MAJOR-PARSE] table not outer-piped), both in non-retryable Phase 2 segments. The README run-history table gains the adjudicated baseline row, the pending corrected-cohort row retires, and the measurement preamble now points at the frozen baseline instead of "no v0.2 baseline exists yet".

  • Claims-evidence alignment becomes a platform-port condition (#640; PR #641). CONTRIBUTING §Platform ports gains a fifth maintainer-facing condition: every load-bearing, verifiable behavioral claim a port makes must ship with contributor-run evidence covering the claim's stated scope, or be narrowed to what the evidence covers; maintainer review checks that alignment plus ARS-principle conformance (human-in-the-loop, degraded-mode disclosure) and does not re-derive the target platform's runtime. Codifies the bar announced in the PR #636 second review round.

  • 2026-08-03 #610 v0.2 baseline-attempt cohort promoted (#637). Byte-for-byte promotion of all seven panels of the authorized #610 §7 step-2 attempt at frozen v0.2 SHA b97628f under the #608 harness (claude-opus-5 @ xhigh, isolated per-seat two-phase claude -p --bare dispatch, --tools ""): four score-eligible records (MS00 r1/r2, MS02 r2, smoke r99) in runs/, three blocked panels (MS01 r1/r2, MS02 r1) recorded — not replaced — in runs/blocked/ per the corrective-iteration rule, provenance valid on all seven with every retry and checker output preserved. check_e4_promotion.py PASS on every stem. The README run-history row documents the 0.50 conformance-abort rate whose root causes (#637) motivated the #638 tolerance fix, and the pending v0.2 baseline row now points at the post-#638 corrected cohort. The dispatch-harness pending-adjudication rule states both paths explicitly: scored-history records receive maintainer verdicts before commit, while attempt-documentation cohorts commit with adjudication.status: "pending" and the explanatory note intact — panels that will never enter baseline/gate scoring stay dispatch-fact records.

  • Bare /ars-* aliases on plugin installs (#633; PR #635). All 16 commands/ars-*.md files now declare an explicit frontmatter name equal to their filename stem. On Claude Code >= 2.1.216 the namespaced /academic-research-skills:ars-<mode> form stays canonical while the bare /ars-<mode> alias — the form the SessionStart announce has always advertised — also resolves whenever no other command claims the name (motivated by Discussion #626; community diagnosis by @Etogerman). Recorded decision: on pre-2.1.216 Claude Code the frontmatter name replaces the whole command name, so commands there surface only in bare form (still invocable; namespaced autocomplete is lost) — accepted, documented in SETUP en/zh-TW. New scripts/check_command_frontmatter_name.py (13 mutation tests, manifest-registered) pins the exact canonical name: <stem> spelling in the command-invariants workflow — any YAML-equivalent re-spelling of the key (quoted, spaced-colon, indented) fails closed, so a semantic duplicate cannot override the alias while CI stays green — keeping the alias surface locked to the announce list.

  • Statistical recomputation v0.2 measurement foundation (#610). Before changing reviewer behavior, the seeded-defect set now versions and pins four arithmetic procedure cases (GRIM, GRIMMER, df-to-N, and test-statistic-to-p) separately from reporting-only statistical defects. A bounded integer-scale oracle independently proves the prospective GRIMMER case and binds its inputs to the manuscript anchor, while a decision record freezes the prose-first arithmetic receipt, closed undecidable reasons, fresh #608-harness baseline order, receipt-backed procedure recall, numeric false-positive, receipt-correctness, and conformance-abort measures. Historical v0.1 results remain tied to their original suite commits; no external model fleet is launched by this slice.

  • Chinese-literature resolver client + API protocol reference (#595). api.crossref.org is one DOI registration agency, not the DOI system: real Chinese-literature DOIs are registered with ISTIC or CNKI, return 404 from the Crossref API, and resolve normally through doi.org — so the existing four resolvers reduce nearly every Chinese reference to unresolvable, the same state a fabricated one produces. New standalone scripts/chinese_literature_client.py closes the measurable part of that gap over four open, key-free upstreams with no CNKI/Wanfang/VIP scraping: doi.org/doiRA/<prefix> registration-agency triage, allowlisted-HTTPS-only doi.org content negotiation (which can verify original Chinese metadata when safely exposed), the Handle REST existence probe, and an ISSN → NLM-TA bridge feeding PubMed coverage confirmation plus the [ta]+[vi]+[pg] coordinate query. Interface discipline mirrors arxiv_client.py (typed ChineseLiteratureUnavailable, per-instance time.monotonic pacing on two independent host anchors, 429 backoff at the endpoint floor, no retry on 5xx, #331 non-expected-200-body guard) with three Chinese-specific departures: an applicability gate so non-Chinese citations are skipped rather than unmatched; a Chinese-aware exact-title-or-bust matcher, because the shared #431 normalizer measurably rejects fullwidth, CJK-punctuation and interior-space variants of one identical title — and one that excludes the shared fuzzy ratio outright, since on CJK titles that ratio scores an unrelated paper 0.510 against 0.577 for a fullwidth spelling of the identical one, making it neither sufficient nor safe as an added necessary condition (ANDing it in demoted a correct match to a P0 "refuted" row in live smoke testing); and a deliberate precision asymmetry — a refuted or title-mismatched identifier is ID-keyed unmatched (the C-V6(a) shape licensing false), but a resolved-yet-unverifiable identifier (the CNKI RA serves HTML, which we refuse to parse) and every PubMed coordinate miss stay title-keyed, i.e. unresolvable, with the signal strength expressed as a P0P3 human-confirmation priority instead of as a verdict. New deep-research/references/chinese_literature_api_protocol.md carries the hand-verified live examples (re-verified 2026-07-27), the rate-limit and degradation tables, the three-state mapping, and the legal boundary. 174 tests over fully synthetic checked-in transport bodies (scripts/fixtures/transport_bodies/chinese_literature/), zero live network in CI. Standalone by design: no verification_gate, citation_verification_summary, triangulation-matrix, schema or degradation-registry changes.

    Review hardening preserves scientifically meaningful title symbols, prevents supplied DOIs from falling through to DOI-less coordinates, validates Entrez literals and upstream payload shapes, closes redirect/body/exception-secret boundaries, makes DOI lookup evidence states explicit, and requires PubMed candidates to rebind through an exact Chinese DOI title without contradicting volume/page metadata. NCBI requests carry tool plus caller-provided email. Contributed by @ktao732084-arch in PR #600.

  • EQUATOR reference gains CARE / STARD 2015 / TRIPOD+AI condensed guidance and a study-design routing sequence (#594). deep-research/references/equator_reporting_guidelines.md carried substance for PRISMA / CONSORT / STROBE / COREQ / SQUIRE while the three study types a clinician-author most often writes first — case reports, diagnostic accuracy studies and prediction models — appeared in the mapping table as names only. Three new condensed sections (§7 CARE, §8 STARD 2015, §9 TRIPOD+AI) follow the file's existing shape: item-level guidance keyed to the official item ids, a citation to the source statement paper, a link to the official checklist, and Clinical Research Application Notes parallel to the existing Higher Education notes. Item wording is paraphrased throughout rather than transcribed — the licensing situation differs per statement, so each section now identifies the exact source artifact, its licence and the nature of this adaptation. A new §10 records the study-design → guideline routing sequence the mapping table previously assumed had already happened, returning a primary reporting guideline plus separately labelled appraisal tools, with ambiguous descriptions remaining unresolved until the scholar clarifies. Reference content only — no agent, schema, mode or file additions. Contributed by @ktao732084-arch in PR #601.

  • Deterministic venue-track contract coverage (#615). A test-only, offline contract oracle and 43-test pytest suite now exercise the accepted 15-target disclosure behaviour without adding a runtime schema, live policy lookup, or general submission engine. Coverage includes the existing seven-field/order checker plus deletion and ordering mutations; selector/user-surface drift; complete intake with citation checking and preserved OTHER / UNCLASSIFIED use; unknown, incompatible, prohibited, and uncurated-policy halts; all four disclosure outcomes; anchor-track isolation; distinct purpose-specific multi-placement blocks; conditional NOT_APPLICABLE children; and strict tool × task/run/artifact fact binding with a cross-record borrowing mutation. Field fixtures pin the final Chinese Nursing, JAMA, Nature containment, International Eye Science, Frontiers, Lancet/Elsevier, Western hard-prohibition, and ICMJE-alongside contracts, including conceptual-versus-data figures, non-LLM and LLM study rights/prompt branches, generated proportion and clinical de-identification, graphical abstracts, primary-versus-research-method images, protected subjects, cover-art permissions, and cover-art-only versus mixed outcomes. The suite is registered in the unified pytest manifest and makes no policy-content change.

  • Frontiers disclosure action-carrier closeout + evidence/audit provenance (#619). Frontiers factual-accuracy, plagiarism-free, and conditionally applicable figure accuracy-to-data checks move out of the Phase-2 render ledger into a labelled Phase-5 pre-submission checklist: created versus edited written/visual use and data-representing figures select the applicable actions, while false or unknown action state remains visibly outstanding without halting an otherwise complete disclosure or producing a false confirmation. AI authorship and editor/reviewer external upload remain separate hard prohibitions. The venue evidence row now reflects that distinction. JAMA retains its live current A Piece of My Mind and Poetry drafting prohibitions while explicitly recording that both clauses were verified live on 2026-08-01 and are absent from the cited 2026-07-01 exact-URL snapshot. The reusable external-contribution audit prompt, previously present only on the maintainer bakeoff branch, is brought onto main-line history with the exact 2026-08-01 PR #599 heads, model/effort, finding counts, first-party recheck, and closure outcome. A focused fail-closed checker and mutation suite cover these three closeout surfaces without adding a disclosure schema or a general submission-policy engine.

  • E4 promotion integrity and resume-from-bundle recovery (#616). The E4 dispatch harness now freezes a prompt-invisible recovery-state.json before record installation: invocation context plus the dispatch/retry/abort event ledger and a path/type/SHA-256 manifest, with no operator-supplied closed status fields. resume_e4_record.py re-emits through the original atomic record builder after a post-dispatch emission failure without constructing a model transport, retrying a call, or re-running a checker; it supports only the documented rolled-back <work-dir>/bundle and canonical installed-raw states and refuses changed, inserted, missing, redirected, ambiguous, inconsistent, or already-consumed evidence. check_e4_promotion.py independently verifies a manually promoted record/raw pair against its work-directory original — canonical scored/blocked layout, complete relative path/type set, SHA-256 identity for every file, and safe resolution of raw_bundle plus all *_location fields — without reinterpreting output or verdicts. Acceptance and mutation tests are wired into the unified pytest manifest; reviewer prompts, contracts, fixtures, checker verdicts, dispatch ordering, and the frozen 2026-07-27 NOT COMPUTABLE cohort remain unchanged.

  • Reviewer protocol text single-source, public role naming, and lightweight calibration tier (#611). The five sprint-reviewer Phase 1/2 prompt pairs and the synthesizer protocol now have one marked canonical source (reviewer_sprint_prompt_source.md) while every dispatched section remains fully inline for --bare --tools ""; a byte-exact render check plus explicit SHA-256 re-pin lock intentional edits without changing prompt semantics or dispatch behavior. The former public EIC seat is displayed consistently as Journal-Fit Reviewer, with eic_agent, contract_role: eic, serialized EIC/EIC-W<n> source IDs, frozen evidence, and real-journal Editor-in-Chief references preserved as compatibility boundaries; those tokens do not select Stage 3' agent files—the synthesizer emits first-round decisions, while contract-governed re-review uses three dedicated calls and a checker-derived outcome. Calibration keeps the existing panel engine and default 5-20-paper full tier (5 runs, 3-run budget override) while adding an explicit opt-in directional tier of exactly three gold papers (Minor, Major, and one Accept/Reject extreme), one fresh panel each, gold-label isolation, exact/raw Minor-Major boundary reporting, raw #215 severity-risk counts, and a hard prohibition on error-rate/profile claims. Its cross-model branch is a canonical non-sprint single-call Reviewer 2 transport with attempt-atomic fallback, so only a homogeneous substrate plan can feed metrics or disclosures; per-dimension score error remains NOT COMPUTABLE without adjudicated dimension-level gold scores, and every partially annotated dimension reports its own annotated_n/N plus missingness instead of implying gold-set-wide coverage. Three fail-closed lints and their mutation suites are wired into spec consistency and the unified pytest manifest.

  • Medical-publishing disclosure targets + fail-closed venue intake (#596). venue_disclosure_policies.md moves to v2 and one global alphabetical order, growing from 6 to 15 policy targets: the ICMJE umbrella recommendations, NEJM, The Lancet, JAMA, BMJ, PLOS, Frontiers, and the first two Chinese-language targets — the publisher-wide Chinese Nursing Journals Publishing House (中华护理杂志社) regulation and the journal International Eye Science (国际眼科杂志). Each addition is anchored to a browser-verifiable primary source: live official pages/PDFs, exact official-URL snapshots for bot-walled JAMA and current NEJM pages, and an exact Lancet author-information PDF capture plus Elsevier's current journal GenAI policy (the superseded February-2025 Lancet PDF is removed). The formatter's standalone disclosure branch now loads the venue protocol/database instead of substituting its normal full-pipeline statement. The venue renderer gains an applicability gate; complete logged-plus-author-reported intake with explicit citation-checking and OTHER / UNCLASSIFIED preservation; per-use closed operation/target records; a per-target required-fact ledger; explicit conditional child fields (including JAMA study prompts/sequence/revisions and extension applicability, detailed Lancet image/data-visualization facts, and International Eye Science generated proportion, data verification, clinical-data de-identification, and ethics/data-upload gates); KNOWN / NOT_APPLICABLE / UNKNOWN states; incompatible-value, prohibited-use, unclassified-use, uncurated-policy, and unknown-field halts; and separately tailored blocks for every required placement channel. The second review pass adds scientific/intellectual-contribution coverage beyond the Chinese Nursing policy's illustrative section list, operation-driven JAMA manuscript-class checks, canonical Nature copyediting plus fail-closed venue-image containment, a Frontiers GenAI scope discriminator, explicit ICMJE/NEJM/PLOS/Elsevier hard-prohibition predicates, closed PLOS/JAMA/Lancet conditional facts, and a non-merging ICMJE-alongside advisory. Policy corrections include BMJ's input/output/review summary wording, the full Chinese Nursing model statement and data/process/result prohibition, ICMJE advice-vs-prohibition and alongside-journal semantics, current NEJM review/edit obligations, JAMA extension numbers and prohibited content classes, International Eye Science's simultaneously applicable data policy and upstream national-standard-number caveat, and submission-timing-vs-location handling. A stdlib structural checker, wired into spec-consistency.yml, enforces exactly 15 unique headings, global case-insensitive ordering, and one complete seven-field set per venue; broader behavioural coverage remains tracked in #615. Venue-count/terminology surfaces are synced across the disclosure protocols, academic-paper/SKILL.md, /ars-disclosure, mode-selection guide, ARCHITECTURE, and READMEs ×5, with all four venue applicability outcomes, typed halt status, and the separate policy-anchor render identified. The Chinese Medical Association Publishing House GenAI regulation remains held back: its primary text was verified first-hand on the publisher's official WeChat channel, but no durable official URL exists to cite (session-signed links only; archive capture is captcha-blocked), and this database refuses third-party summaries. Contributed by @ktao732084-arch in PR #599.

  • Re-review persuasion-invariance paired controls (#576 Spec B §14, PR-B3 — evals). Final leg of the B1→B2→B3 chain, closing #576. New held-out set evals/heldout/re_review_persuasion_invariance/ — six paired-control scenarios in en + zh-TW (15 arms, 12 pairs, 43 (pair, observable, target) cells per language) that hold every Stage 3' input constant except one controlled factor and document, per observable, whether the contract requires the arms to agree or to diverge: P-1 identical revision under strong vs weak Response-Letter rhetoric (verdicts and decision must be identical; the two letters are gated to an equal claim set so rhetorical force is not confounded with claim content); P-2 one identical letter over a substantive vs cosmetic manuscript change; P-3 one and the same new issue as regression / previously_missed / indeterminate (the last with original_manuscript and apply_reports absent), whose sharpest cell is the §8 goalpost guard making the latter two decision-equivalent; P-4 an evidence-backed rebuttal vs an assertion-only one (only the former books a valid_rebuttal adjustment and reaches addressed_by_rebuttal, and the "deaf" direction is scored as well as the "persuaded" one); P-5 a fix at expected_change_surface vs an equivalent fix elsewhere vs a cosmetic edit AT the surface (SD-10: same verdict and different anchor for the first pair, rejection for the third); P-6 a qualifying research_integrity escalation approved vs a non-qualifying methodological new_standard vs the same qualifying exception the user REJECTS, pinning the identical pre-answer emission, the §6.4 closed class set, and the §6 Step-3 floor — and, because §3.1 makes Phase 1 revision-blind while §3.2 puts a NewStandardRecord's origin there, P-6 is the one scenario whose arms carry different Round-1 artifacts rather than a shared packet. Ground truth is documented per scenario with every cell carrying a rule_anchor back to the spec clause (#550 convention), and all six maintainer-judgment values are flagged as such in their scenario's ground truth, with the three that are load-bearing for a scored cell (P-3's new-issue severity, P-6's mechanical_decision_impact, and P-4's stated-argument reading) named as such. Scoring is pairwise-consistency at (pair, observable, target) cell granularity, with the honest asymmetry stated: identical cells are noise-robust because a shared judgment cancels, differs cells collapse to absolute correctness on the pair's observables. The set joins the #574 E4 harness per SD-11 (evidence contract, blocked-run separation, replicate discipline reused); E4's 2026-07-27 NOT COMPUTABLE cohort result is untouched. Apply reports ship placeholder hash tokens the dispatcher stamps at materialisation, so the §11 apply-chain witness cannot fail against a checked-in constant. New scripts/check_persuasion_invariance_fixtures.py structure-only gate (12 invariants incl. the pinned scenario/arm/pair/cell inventory, relation-vs-expected-value agreement, the P-1 claim-set equality, apply reports detected independently of their hash keys and required to declare report_format_version plus all three hash keys exactly once each with key-bound placeholder tokens, a pinned count of report-bearing files so a whole block cannot be deleted silently, one-hop pointer resolution, the held-out boundary checking every language's scripted checkpoint answer against every material file, exact packet/arm section sets, and multiset agreement on (arm-pair, observable, target) between each ground_truth.md's Pair-structure table and the index) with a 58-test mutation suite, wired into spec-consistency.yml and the unified pytest manifest. Two limits are stated rather than left to be discovered: invariant 7 pins the declared claim-set arrays, not the letter prose, and invariant 12 compares which cells exist, never what they expect. Cells whose correct value depends on a choice the spec leaves open carry conditional_on and go unscoreable rather than counting as a miss; where an observable can instead be defined TOTALLY over every conformant branch that is preferred, and P-6's arm-level escalation_path_entry is the worked example: quantified over the whole emission (entered iff ANY Phase-1 record requests escalation or ANY 2A exception exists) because §5.1 permits several standards per item and the exception record carries no item binding at all, and scoped to the entry ATTEMPT rather than its success, since a request that lapses back to advisory still crossed §3.2's boundary. Seed set with no baseline: it ships the fixtures, ground truth, and protocol, and makes no claim about any model's persuasion-invariance until measured.

  • Re-review three-gate contract becomes the Stage 3' default (#576 Spec B, PR-B2 — protocol + orchestration). Second leg of the B1→B2→B3 chain: the PR-B1 artifact layer gains its emitters and the contract turns ON. academic-paper-reviewer/references/re_review_mode_protocol.md is rewritten around the three-gate orchestration (Phase 1 criteria commitment revision-blind → Phase 2A evidence verdict persuasion-blind → Phase 2B claim matching with typed adjustment records), replacing the read-letter-first Traceability Rule; it now carries the full verdict vocabulary (+CANNOT_VERIFY), the §6 G0G2/B1B6/floors decision derivation with the mechanized p2_addressed_rate, the protocol-level generic P2 MADE_WORSE discriminator, dissent/new-issue/escalation/routing/input-manifest sections, and the ARS_RE_REVIEW_LEGACY=1 legacy boundary ([LEGACY-NO-CONTRACT] marker; flagless prerequisite failure → G0-class abort, never silent fallback). pipeline_orchestrator_agent.md gains the § Stage 3' Re-Review Contract Dispatch section (manifest emission before Phase 1, three sequential validated gates, the three post-2B passes in normative order, MANDATORY check_re_review_synthesis.py invocation before any outcome surfaces, deferral-loop re-persist/re-check cycle, fail-closed abort surfacing, producer obligations). The seven parallel Stage 4→3' transfer/input surfaces all gain the original (pre-revision) draft (orchestrator handoff row, state-tracker prerequisites, state-machine Normal Flow row + Verified-Paper-Draft matrix qualifier, pipeline SKILL Step-4 enumeration, two_stage_review_protocol, team_collaboration_protocol, reviewer SKILL + protocol input lists), and the four surfaces carrying the old single-report output_draft_hash check switch to the §11 ordered-chain rule (incl. revision_patch_protocol.md). New Stage 3'→4.5 handoff row + extended Stage 4/4'→4.5 row forward the sidecar's frozen previously_missed/indeterminate records; integrity_verification_agent.md consumes BOTH attributions as Stage 4.5 gate input; two §8 routing-fixture tests prove the records reach and are ingested on each path. Schema surfaces: Schema 11 gains the sidecar note + CANNOT_VERIFY in status + the legacy-scoped authors_claim flag consequence; Schema 7 consensus_level gains SINGLE-VERIFIER; Schema 6 judge_record gains optional precommitment_hash / routing_status (oneOf incl. the parameterized unmapped-labels pattern) / apply_chain_witness; the R<n>-follows-must_fix-order ordinal contract is pinned in editorial_decision_template.md + editorial_synthesizer_agent.md (R side only). reviewer_re_review leaves the Schema 13 enum (§5.4) with a rejection regression test; the §7 panel table, reviewer SKILL, and shared/contracts/README.md point at the dedicated family. The degradation registry (1.1.0) registers all 14 #576 mechanisms (MADE-WORSE-UNEVALUABLE, ESCALATION-UNSUBSTANTIATABLE, CHANGE-BASIS-ABSENT, ATTRIBUTION-INDETERMINATE, both CRITERIA-LAYER-ABSENT forms, ROUND1-FINDINGS-ABSENT, COMMITMENT-EVIDENCE-ABSENT, PATCH-BINDING-ABSENT, the three ROUTING-DEGRADED forms, LEGACY-NO-CONTRACT, the apply-chain not-run states) with fixture tests; check_pipeline_boundary_semantics.py CONTENT_LOCKS re-pinned for the four touched locked files in the same commit.

  • Re-review pre-commitment contract artifacts + synthesis checker (#576 Spec B, PR-B1). First leg of the strict B1→B2→B3 implementation chain for the three-gate evidence-before-persuasion re-review contract (design merged as PR #604). Four new schema documents under shared/contracts/re_review/precommitment (Phase 1 revision-blind criteria commitment: inherited-criterion binding with derived R<n> letter ordinals, P2 lighter form, §10-normalized source_reviewer_labels, NewStandardRecord with new_standard_id added at implementation so new_standard_ref has a stable target), verdict_record (Phase 2A persuasion-blind verdicts: CANNOT_VERIFY discriminated union, residual re-grading, dissent records with recomputable criterion_hash, pending escalation exceptions), traceability (Phase 2B sidecar: typed adjustment chains with closed bases, frozen new-issue copy, deferral-loop records — intents/resolutions/reapplications/rebuttal adjudications/G2(d) acceptances/pending rebuttal upgrades — DecisionInputs operands, emission-kind-scoped decision_state), and input_manifest (present-discriminated §11 unions, tagged passport:/path: refs, freshness fields). New stdlib-only scripts/check_re_review_synthesis.py recomputes the whole contract from the primary artifacts (#510 architecture class): JCS-sha256 hash chain, byte-exact criterion/letter binding with the §5.1 contiguity degradation, the apply-chain witness with patch_digest content-bound pairing and the [PATCH-BINDING-ABSENT] pre-1.2 policy, every §13 referential/cardinality invariant, the G0G2 gates incl. the deferral biconditional, and the B1B6 + floors decision derivation recomputed both from DecisionInputs and independently from the raw records — graded exit codes (0 pass; 1 synthesis_mismatch incl. all referential failures; 2 schema/manifest invalid with the matching §3.5 reason). scripts/ars_apply_revision_patch.py report format bumps 1.1→1.2, adding patch_digest (full sha256 over the exact patch bytes applied) per the 1.0→1.1 precedent. 181-test mutation suite (scripts/test_check_re_review_synthesis.py): three hand-pinned golden scenarios (Accept, cross-model-active Minor, G2(d) deferred/accepted), one violating fixture per invariant, the §10 card-normalization fixtures pinning both example files' Role lines verbatim (DA vocabulary via synthetic fixtures), a §6 derivation unit table, and jsonschema parity on the positive path; wired into the unified CI pytest manifest. No behavior change: nothing emits these artifacts until PR-B2 turns the contract on as the Stage 3' default. The three-track exact-head review round closed the §3.4 Direction-column enforcement (valid_rebuttal upgrades to FULLY_ADDRESSED only — the sole letter-anchor basis cannot move a verdict sideways; author_pointer_located_evidence is a strict upgrade to PARTIALLY/FULLY), the superseded-reapplication time anchor (a preserved failed attempt is never compared against the post-retry chain tail), the G2(d) acceptance↔adjustment one-to-one join, CrossModelResolution answer-membership, challenged-proposal booking exclusivity, the §11 no-original escalation/attribution degradations, verbatim source_reviewer binding, half-transported severity refusal, strict apply-report version/hash grammar, the relative-only path: ref grammar, and the §13 aborted-emission exemption scope.

  • Reviewer Schema 13.2 role-scoped scoring and canonical decision contract (#574 Spec A). The full and methodology-focus sprint contracts move to v2 with per-dimension eligible_roles / owner_role, mandatory-only paper-blind fatal triggers, a four-token decision enum, and an exhaustive Minor/Major/Reject/Accept condition ladder; full mode adds EIC-owned D6 venue fit and contribution. Phase 2 cards now score only eligible dimensions, use explicit structural/declared abstention, bind warn/block/fatal classifications to Phase 1 triggers, and leave one decision producer: the synthesizer. check_phase_conformance.py makes role binding, plan grammar, manuscript blindness, trigger binding, dissent/fatality, and Critical/Major anchor checks executable; check_panel_synthesis.py now applies per-dimension eligible-seat quantifiers (majority n=1 means owner decides), verifies dimension_verdicts, and enforces machine-addressable DA-CRITICAL adjudication with a terminal Accept escalation marker. The five delivered reviewer prompts gain per-finding band tests, anti-bundling, and singleton-Critical anchoring; decision authority and numeric-threshold residency are single-sourced and mutation-pinned.

  • Reviewer Spec-A E4 acceptance attempt recorded as BLOCKED, not scored (#574). A fresh claude-opus-5 xhigh cohort at suite 19bc872 launched the two clean-control replicates in parallel under the frozen isolated two-phase dispatch. r1 reached synthesis (editorial_decision=major_revision, one unscored maintainer observation) but is provenance-invalid because its first malformed methodology Phase 1 response was overwritten by the permitted structural retry; the final panel cannot independently prove that response's paper blindness or retry eligibility. r2 is independently provenance-invalid because its first malformed Methodology and Perspective Phase 1 responses were also overwritten by their permitted structural retries; exact checker outputs survive, but the rejected model responses do not. It then stopped fail-loud when the Perspective Phase 2 emitted an empty ## Scoring Plan Dissent section and check_phase_conformance.py returned [DISSENT-GRAMMAR: dissent section must name dimension_id]. The protocol does not permit a retry for this non-multi-dissent Phase 2 failure, so no DA, synthesis, MS01/MS02 panels, replacement draw, or imputation followed. The cohort has zero score-eligible runs, a provenance-invalid rate of 2/2 (1.00), and a conformance-abort rate of 1/2 (0.50), the latter against the Spec-A diagnostic expectation of approximately zero; the required 2 × 3 fleet, recall means, clean-control mean, severity agreement, DA-critical-versus-Accept monitor, and release gates are all BLOCKED / NOT COMPUTABLE. Both attempts live under blocked record/raw namespaces, preserving available evidence without presenting either as a valid run.

  • Reviewer E4 re-measurement on claude-opus-5: fresh two-condition fleet, behavior-batch gate verdicts (#574). The opus dispatch alias moved from claude-opus-4-8 to claude-opus-5 the day after the E4 baseline, so per the seeded-defect README's re-run-don't-reuse rule BOTH conditions were re-measured on the new model (12 blinded isolated runs: 2 replicates × 3 fixtures × {pre-#581 prompts at f7d9d07 via worktree, post-#581 at ad81b2e}, frozen dispatch shape reproduced over headless-CLI calls at effort xhigh). Gate verdicts for the #581 behavior batch against the same-model baseline: strict recall PASS and improved (MS01 0.95 → 1.00 with the critical band 0.875 → 1.00 — the SD-01 GRIM achievability recompute, never performed by opus-4-8, fires in both post replicates; MS02 1.00 → 1.00); clean-control false findings PASS and halved (4/2 → 2/1, the baseline's logical-foreclosure / inoculation / recruitment-channel-as-fact fabrications do not recur, and the post r1 run is the only one of twelve whose clean-control decision avoided reject_or_major_revision); severity agreement FAIL on the frozen highest-tagged-seat ladder (0.663 → 0.536) — with the recorded diagnostic decomposition showing DA-only agreement flat-to-up (0.621 → 0.644), letter-fallback cells 4 → 0, and non-DA per-finding tag coverage 0 → 100%, i.e. A3's severity transport is achieved and the frozen metric now exposes cross-seat critical-band inflation that was unmeasurable pre-A3; seat-level band anchoring is the named #574 B1 follow-up residual. All 12 run records + complete raw panel outputs committed under evals/heldout/reviewer_seeded_defects/runs/; README baseline table gains the two same-model rows and marks them (not the opus-4-8 row) as the operative gate baseline.

  • Reviewer behavior batch: quota removal + coverage receipts, typed evidence anchors, severity transport, decision symmetry (#574 A1/A2/A3/B1). The four behavior-changing reviewer-prompt reforms gated on the E4 baseline (measured 2026-07-24 at suite 307ef24: MS01 0.90 / MS02 1.00 strict recall, clean-control 0 false findings, severity agreement 0.625 — the batch's A3 half targets exactly that severity gap). (A1) The hard 3-5/2-3 finding quotas are removed from all four scoring seats (EIC + R1/R2/R3) and the peer-review template — findings now follow the evidence in both directions, and a report with an EMPTY Strengths or Weaknesses list must carry a Coverage Receipt (one row per examined dimension with the basis for "no finding"), so no-quota never becomes lazy non-review; the EIC's "still find 2-3 points" accept-caution edge case goes with it. (A2) Every finding carries ONE typed evidence anchor — text / table / figure / equation / dataset / absence — with the vocabulary canonical in the peer-review template: a verbatim quote + page is one type, not the universal requirement; omissions use absence with absence_scope + the surfaces actually checked; Critical/Major findings MUST carry an adequate, applicable anchor or they are not yet findings; the DA's issue tables trade the untyped Location column for Evidence Anchor (+ Confidence on CRITICAL/MAJOR). (A3) Schema 6's severity enum becomes the declared single source (the E4 severity-source ladder made mandatory): all four scoring seats emit an explicit per-finding Severity + Confidence (1-5) + competence basis, the DA's CRITICAL/MAJOR/MINOR bands map onto the enum with OBSERVATION excluded as a non-defect channel, the Weakness object gains optional evidence_anchor / confidence / competence_basis fields (additive, legacy cards stay valid), and the synthesizer TRANSPORTS severity/confidence from the cards — never re-derives — marking fallbacks [SEVERITY-SOURCE: letter-fallback] / [CONFIDENCE-SOURCE: report-level]; the decision standards' parallel Serious/Moderate vocabulary is folded into the enum. (B1) Replaces asymmetric strictness with symmetric evidence standards: the EIC's acceptance-rate rigor anchor (Q1 ~10-15%), the synthesizer's "Rare — most papers don't pass", and the standards' "< 5% of submissions" base-rate framings are all removed; a new Decision Symmetry and Register Independence section pins that Accept and Reject carry the same evidence burden, decisions follow venue criteria rather than target distributions, and tone rules govern wording only — severity never moves for politeness or rigor-signaling (the E4 DA band-instability finding gets the matching band-consistency rule). Also closes a #578 P0-3 residue: the skill-level "no duplicate criticisms" quality standard becomes independent-overlap-is-corroboration. Two new fail-closed lints pin all of it: scripts/check_reviewer_data_fences.py (the PR #578 <paper_content> + <phase1_output> fences verbatim across all five panel agents + the sprint-contract delivery side + the cross-model Reviewer 2 transport) and scripts/check_reviewer_finding_contract.py (quota regressions, receipt surfaces, anchor vocabulary — with Schema 6's machine-facing anchor_type enum and both DA inline spellings pinned to the same ANCHOR_TYPES tuple as the template rows, so the prose layer and the schema layer cannot silently desync — severity single-source witnesses, base-rate anchors), 96 mutation/inverse tests, both CI-wired via spec-consistency + the unified pytest manifest. Seventeen cross-model review rounds (finding trajectory 12→7→9→8→6→5→7→9→6→4→4→2→2→2→3→2→0, the final round returning an explicit zero-findings verdict — the convergence signal; three documented rejections carry compatibility bridges instead of removals) closed structural gaps the first cut missed: round 2's load-bearing find is that sprint runs inject ONLY the ### Phase 2 — Paper-visible review subsection as the reviewer's system prompt, so the Finding Contract now lives INSIDE that delivered subsection on all five seats (a file-level placement would never reach reviewer_full/methodology_focus calls — the structural explanation for the baseline's missing per-seat severity tags), with both lints parent-binding the subsection under ## v3.6.2 Sprint Contract Protocol (a decoy heading elsewhere no longer passes); the #215 ungroundable-field-norm disposition became canonical-enum Minor + [FIELD-NORM UNVERIFIED] (no off-enum "advisory" tier); the remaining directional priors (synthesizer divergence-weighting edge case, decision-template "conservative principle" rationale) and mandatory-strength mirrors (guided-mode opener, review-quality balance check) went direction-neutral/genuine-merit-conditional and are lint-pinned. Round 3 extended the contract to strength anchors inside the delivered block (A2 covers both polarities), unbounded the revision-response skeleton (W1..Wn — a W6 finding can no longer lose its author response), made the perspective seat's assumption-audit gate and the summary's polarity mentions conditional, re-phrased the "most authors overstate generalizability" heuristic as a criterion-only scope check, taught the shared section extractor CommonMark tilde fences, scoped the DA table check to ## Output Format, and widened the quota grammar again (worded ranges, at minimum, N+, not fewer than); the DA's no-consecutive-concessions ladder was deliberately KEPT (v3.0 anti-sycophancy: consecutive concessions are themselves accommodation-bias evidence) with an explicit B1-compatibility bridge — pressure-time procedure, never first-pass severity. Round 4 declared the statistical red-flag HIGH/MEDIUM/LOW labels detection TRIAGE (never a competing severity vocabulary — enum severity is assigned by decision impact, not copied), routed a lone Major recommendation through One-Outlier arbitration before it can escalate the decision, gave Schema 6 strengths a structured {description, evidence_anchor} form so strength anchors survive serialization, made the last affirm-first mirrors (hypercriticism guidance, guided-mode summary, response template's Strengths-Acknowledged block) genuine-merit-conditional, required full-line CommonMark fence closers in the shared section extractor (a ~~~not-a-close line no longer closes a block), and taught the quota grammar noun-first heading forms ("Weaknesses (at least 3)") plus per-mirror unbounded-response validation. Round 5 — by then finding only lint-mechanics, the convergence signal on the prompt layer — capped fence indentation at CommonMark's three spaces, bound the transmission witnesses to the ACTIVE delivery clauses (the protocol's numbered Phase-2 step; the Reviewer Track's When-active block), scoped the Schema 6 witnesses to the Schema 6 section (a row migrated into a neighbouring schema now fails), extended the quota grammar to imperative direct counts and maximum forms (both contradict no-minimum/no-maximum), and validated each response mirror separately with a widened bounded-range reject; both lint docstrings now state the threat model explicitly — accidental drift by well-intentioned edits, with adversarial repo editors out of scope since anyone who can craft a pathological decoy can equally edit the lint (the terminal defense there is review of lint-file diffs). Round 6 (down to one P1) pinned sub-claim severity INHERITANCE in the synthesizer's decomposition (all sub-claims of one parent share its transported severity — a differing severity means a different parent, never re-rating; the Step 1b example now shows two bundles), re-worded the EIC's weak-paper edge case to prioritize-never-truncate (independent evidence-backed defects all stay listed; only cascading symptoms of a listed root cause fold in), and fixed two realistic false-POSITIVE paths in the quota grammar (a non-finding "Questions (2-4 items)" count and "between major and minor issues" prose no longer fire; "up to N findings" maximums now do) — with inverse tests asserting the legitimate forms PASS. Round 7 reached four surfaces the earlier rounds had not opened: the cross-model DA's "find the 3 most serious weaknesses" prompt lost its fixed count (A1 reaches the #523 transport path too); the synthesizer's EMITTED package now carries the transported metadata (Severity columns on both roadmap tables, typed anchors in the Top Blocking cells on both mirror surfaces — the Step 1b working inventory is no longer where A2/A3 fields go to die); the DA-CRITICAL "cannot be Accept" iron rule became visible ADJUDICATION on all three surfaces (a validated or genuinely unresolved DA-CRITICAL still blocks Accept, but an EIC-adjudicated-and-rejected one is recorded with rationale rather than vetoing — an unvalidated negative claim carries the same evidence burden as a positive one); the DA's delivered Phase 2 contract is pinned verbatim (a weakened modality can no longer hide behind stronger undelivered sections); the three advertised examples were regenerated in the current finding format; and the quota grammar tolerates modifier words between count and noun while points left the finding-noun set (scoring language must not false-fire). Round 8 completed the A2/A3 emit chain end-to-end: EVERY roadmap row (both tables, synthesizer + decision template in lockstep) now carries transported Severity + typed Evidence Anchor + per-finding Confidence — not only the ≤3 Top Blocking rows — and Schema 7's RoadmapItem gains the same three optional fields for machine consumers (with type explicitly re-scoped as revision magnitude, distinct from finding severity); the last qualitative base-rate cue ("uncommon in practice") left the Accept scenario; the regenerated examples were corrected where they under-demonstrated the contract (explicit per-finding Severity fields, input cards carrying the severities the inventory transports, arbitration reading the per-finding Confidence rather than the report-level score); and the quota grammar gained limit/cap constructions, while the range pattern's precision-over-recall tradeoff (descriptive prose could false-fire on curated prompt surfaces; a visible CI failure beats a silent quota regression) is now documented in place rather than half-fixed. Round 9 finished the machine-transport tail: Schema 7 gained severity_source/confidence_source (an enum value alone cannot carry a fallback tag, so provenance gets its own fields), the lint pins all five Schema 7 transported-field rows scoped to the Schema 7 section, the DA's MINOR rows carry Confidence (a MINOR issue that becomes a Suggested Revision transports it like any other), the cross-model DA prompt block passes the full quota grammar (not just the retired literal), the between-range pattern tolerates modifiers, and both example files' Revision Roadmaps carry the transported metadata end-to-end. Round 10 — the first zero-P1 round — tightened four advisory residues: text-type strength anchors keep their short verbatim quote (prompt-compliant strengths can no longer be schema-invalid), the field analyst's low-maturity configuration makes developmental feedback a REGISTER while the recommendation stays evidence-based, the quota grammar catches top-N selection counts ("report the top 3 weaknesses"), and the Schema 7 witnesses pin transport semantics rather than row existence. Round 11 (also zero-P1) cleared the last advisory residues: selection-verb direct counts ("Select three weaknesses") joined the quota grammar, the sub-claim decomposition example's roadmap items and the remaining example text-anchors now conform to the transport contract end-to-end, and every sample severity cell in the decision template reads [transported] so no row invites re-derivation. Round 12 (zero-P1 again, two advisory residues) finished the tail: the template's last hard-coded severity sample became a transported placeholder, and the sub-claim example's omission finding carries a proper absence anchor from the input card through the roadmap. Round 13 (zero-P1, two advisory residues) split the one example roadmap row that merged two findings with heterogeneous metadata and made the fence mutation-test helper wrap-tolerant like the checker it exercises. Round 14 (zero-P1, two advisory residues) gave Schema 6's ReviewerReport an explicit reviewer_confidence (1-5) field — the legacy fallback's target, deliberately distinct from the 0-100 editorial confidence_score — and made the decomposition example's input cards schema-representable (anchors + competence bases, plus an atomicity note framing the bundle itself as the residual failure mode decomposition guards against). Round 15 (zero-P1, three advisory residues) completed the contract's edge semantics: ReviewerReport gained a conditional coverage_receipt object so a reviewed-empty list survives serialization distinct from a thin review; the Minor Issues block is now an explicitly declared NON-finding channel (copyedit-level, below the finding threshold, merged into Priority 3 as source_kind: "editorial" items); and RoadmapItem gained corroborating_sources + source_kind so multi-source consolidation drops nothing and no-source items are legitimate rather than legacy-ambiguous. Round 16 (zero-P1, two advisory residues) added competence_basis to Schema 7's driving and corroborating source shapes and put full typed anchors into the decomposition example's working inventory. The rounds also closed fail-open classes in the lints themselves (fence witnesses now scoped to the DELIVERED ### Phase 2 — Paper-visible review subsection / protocol / Reviewer-Track blocks, since a fence relocated out of the injected subsection is delivered nowhere; DA column checks parse the header row the shared footnote can no longer mask; quota patterns generalized beyond the retired literals; the Schema 6 confidence row pinned) and a family of kept-sentence contradictions the batch's new rules had left standing: the DA's CRITICAL band re-anchored to the canonical decision-impact bar (a fixable acceptance-blocker is Critical on every seat), the decision standards' cross-dimension table reframed as decision-impact-only (no second severity-assignment path, no off-enum values), unresolved-split handling made direction-neutral in both the synthesizer and the standards, the mandatory-merit lines ("they always exist", "for fairness") replaced with genuine-merits-only rules, the Coverage Receipt made polarity-aware (it states whether it covers Strengths, Weaknesses, or both), Minor findings' anchors made mandatory (section-level locator suffices), Schema 6's quote/absence_scope/check_performed made type-conditionally REQUIRED, the SKILL.md "fake diversity" anti-pattern row reframed as overlap-suppression (P0-3), and the #506 calibration leniency prior explicitly bridged as a measurement-reading prior that never shades a decision (B1). Post-change E4 re-measurement per the set's README is the acceptance step for the next release.

  • E4 baseline measurement recorded for the reviewer seeded-defect set (#574). First operative baseline on the v0.1 fixtures, executed under the protocol's now-frozen dispatch shape: per manuscript, a blinded neutral-filename copy outside the repo, a field-analyst call, then per seat a PHYSICALLY SEPARATE paper-blind Phase 1 call (contract + title/field/word_count only, manuscript reads forbidden — sprint_contract_protocol.md §2's load-bearing mechanism executed literally) and a paper-visible Phase 2 call, then the synthesizer over the five cards, with the protocol's §§4-5 structural lints enforced at dispatch (claude-opus-4-8, reasoning effort xhigh; 2 replicates × 3 manuscripts; maintainer-delegate adjudication under the README's frozen severity-source ladder). Isolation provenance, stated precisely: the manifests-opened-only-after-all-runs clause held for the superseded first attempt; by the operative rerun the ORCHESTRATING context had already adjudicated that attempt and so carried ground truth — contamination isolation for the operative runs therefore rests on the dispatch fence, not orchestrator ignorance: every reviewer/synthesis agent received only the neutral-named manuscript path, the reviewer skill files, the contract, and prior-phase outputs as delimited data, with all evals/ reads forbidden and no defect-related vocabulary in any prompt (the same fence every post-change run will rely on, since any post-baseline orchestrator is manifest-aware by construction — now recorded in the README protocol). Results at suite commit 307ef24: MS01 strict recall 0.90 in both replicates (critical band 0.75 — the sole non-detection in every MS01 run is SD-01, the GRIM-impossible mean: panels recompute t-vs-p and df-vs-N unprompted but never run the achievability check, cleanly isolating the A4 recompute gap to the granularity class); MS02 strict recall 1.00 in both replicates (critical band 1.00; both panels explicitly name the absent interview protocol); clean-control false findings 0 in both replicates (every asserted weakness verified as a true-absence observation or hedged advice; decisions were Minor Revision and "Major Revision gated on citation verification", the latter driven entirely by the excluded-by-design synthetic-DOI class — recorded as a v0.2 fixture-design consideration, not a measurand); severity agreement 0.625 mean (0.5000.722 across runs) — recall is stable while the losses split between DA band placement (the dominant share: the same defects swing a full tier across replicates and seats) and the letter-fallback rung (three 0.5-losses where NO detecting seat carried a tag, so the proxy governed) — both halves are the A3 severity-transport gap, one as band instability, one as missing per-seat tags. Two protocol events occurred and were recovered per protocol, both recorded for the operational monitors: a PANEL-SHRUNK abort (DA multi-dissent, §5 retry-from-Phase-1) and a voided-and-retried synthesis (§8.1 duplicate emission pair; the voided output is preserved under runs/raw/voided/ and the retried synthesis reproduces the same fired set and decision). Measurement provenance: the first same-day attempt ran each panel as a single in-context simulation; the cross-model review track showed its "paper-blind" Phase-1 plans pre-registering manuscript-specific facts (physical call separation is unachievable in one context), so it was superseded and fully re-run — the attempt is preserved under runs/superseded/2026-07-24-in-context-dispatch/ with near-identical headline numbers (MS01 0.90 same sole miss; clean-control 0; severity 0.599 vs 0.625), evidence the leak was not inflating recall. The dual-track trajectory (security three passes, all clean; cross-model ultra iterated to convergence: 2P1+2P2 → 3P1 → the dispatch-validity P1s that triggered the user-approved re-run → adjudication/grammar P1s closed by a mapping correction and the §8.1 synthesis retry → a final numeric-consistency pass) also drove the committed raw-output evidence base (runs/raw/, verbatim panel outputs making every verdict re-adjudicable), the frozen severity-source ladder, and the frozen dispatch-shape clause in the README protocol. The #574 behavior batch (A1/A2/A3/B1) is now unblocked, with A3 (severity transport) and A4 (recompute) carrying the clearest baseline-measured headroom.

  • Reviewer seeded-defect held-out set v0.1 (#574 E4). The acceptance instrument for reviewer-prompt changes: evals/heldout/reviewer_seeded_defects/ ships two synthetic defective manuscripts with ground-truthed manifests — MS01 (quantitative educational-technology study, 10 planted defects: GRIM-inconsistent mean, df/N contradiction, p-vs-statistic mismatch, causal overclaim from a cross-sectional design, citation-claim reversal, random-vs-convenience sampling contradiction, absent ethics/consent statement, subgroup-sum mismatch, abstract-vs-results value drift, unbounded generalization) and MS02 (qualitative/mixed higher-education-policy study, 9 planted defects incl. undescribed instrument, unoperationalized construct, n=12→sector generalization, abstract/methods count contradiction, suppressed disconfirming voices, re-identifiable "anonymized" participants, significance claim with no test, citation-claim mismatch, unqualified first-comprehensive claim) — plus MS00, a deliberately sound clean control for the false-positive side. Every manifest row carries a verbatim-unique anchor_quote, closed defect-class/severity/detector enums, and the arithmetic defects are independently re-computed in their descriptions. The README pins the measurement protocol (fresh session per manuscript, manifests are contamination — never in review context; maintainer-adjudicated DETECTED/PARTIAL/MISSED per defect; recall + clean-control false findings + severity agreement) and the honest scope line: a directional smoke tier, not FNR/FPR calibration (the #574-rescoped E5 decision). scripts/check_seeded_defect_fixtures.py (6 invariants incl. an expected-fixture inventory pin with duplicate-id rejection and an exact per-fixture defect-ID pin, 21 mutation tests, CI-wired into spec-consistency + the unified pytest manifest) gates fixture integrity only — the behavioral baseline is measured manually per the README and recorded in its baseline table BEFORE the #574 behavior batch (A1/A2/A3/B1) may land.

  • THIRD_PARTY.md "Upstream & related projects" section (#573). Acknowledges Yila-AI/sci-ssci-skills by @MissOrangePeel as the origin of the mechanism shape adapted in the #569/#570 revision-round claim-drift guards (PR #571), reciprocating their README listing of ARS under "Projects using or adapting this work".

  • revision_claim_drift post-guard re-measurement — first row under the #654 envelope (#652). The 2026-07-22 pre-guard baseline (2/8 claim-strength/hedge drift) finally gets its post-guard counterpart, in the issue's preferred two-arm concurrent design: 8 items × {unguarded, guarded} × 2 replicates in one session window on a frozen claude-fable-5 headless-CLI subject (neutral cwd; context probed free of guard content), where the guarded arm carries a guard block condensed from the shipped draft_writer_agent revision-mode ladder section (rules 1-3 near-verbatim + ladder scale; shipped rule 4's roster mechanism replaced by a token-conservation line - the row measures this prompt, not the shipped pipeline path). Result: unguarded 7/16 = 0.4375 vs guarded 1/16 = 0.0625 item-replicate drift; deterministic unauthorized numeric/citation deltas 4/16 unguarded (including the rp-06 citation consolidation the pressure aims at and a derived "24%" restating HR 0.76) vs 0/16 guarded; controls clean in both arms. The guard did not eliminate drift — the guarded residual (rp-03-G-r2) restated a null as an affirmative "showed no relation" (absence-of-evidence → evidence-of-absence), and the unguarded arm ran hotter than the 2026-07-22 row (4/8 items vs 2/8; descriptive temporal comparison only). Measurement discipline: heldout-measurement/1.0 envelope validated by the #654 checker (its first real-row engagement), adjudication rubric v1.0 committed + hashed before any judge output existed, legacy-comparability judge (codex gpt-5.6-sol xhigh, blind to arms/controls via seed-652 anonymization, judge_plan.exception: legacy_comparability per the contract), blinded fresh-context adjudication resolving 10 flagged runs (any verdict boolean true) to 8 confirmed drifted (both rp-01 runs rejected on the rubric's C3/C4, mirroring the baseline), raw subject/judge/adjudication outputs + superseded first-sample attempts retained under runs/2026-08-07/ (the run's two apparent call failures were later found to be in-flight reads - two agreeing samples each, corrected in RUN_NOTES items 9-11 along with a control-scoped criterion citation and the operator-attested status of the same-window claim). Judged rate remains a lower bound conditional on judge recall (rubric C7).

Changed

  • #569/#570 guard language tightened from "closes the residual" to "a guard now watches it" (#652, do-now half). The v3.19.0 release notes (CHANGELOG + README en/zh-TW + .claude/CLAUDE.md) described the revision-round claim-drift guards as closing the epistemic and token halves of the #390 honest-claim residual. The guards shipped after the only measurement row in evals/heldout/revision_claim_drift/ (the 2026-07-22 pre-guard 2/8 baseline), and no post-guard row exists yet, so the honest statement is that the failure mode is measured and a guard now watches for it — not that the guards reduce it. All four surfaces now say exactly that and point at the #652 post-guard re-measurement. Wording only; no mechanism, schema, or gate behavior changes.

Fixed

  • Conformance machine lines tolerate Markdown decoration; Severity self-correction is escalation-only (#637). The 2026-08-03 #610 baseline attempt aborted 3/6 panels on decoration alone: every synthesis first attempt (6/6) fenced the four mechanical audit lines, one retry re-emitted them as whole-line inline code spans, and one domain seat self-superseded its Severity declaration mid-card — all three shapes render the content verbatim and visibly, so by the #609 reader-visibility criterion these are false aborts, not leak guards. check_panel_synthesis.py gains audit_candidate_lines: the four mechanical audit-line grammars now also read fenced content (fence-marker lines dropped, the same open/close state machine as strip_fences) and the unwrapped bodies of whole-line inline code spans, normalized to their CommonMark-DISPLAYED form (padded-span one-space strip, indented-fence dedent); rejection rationales and the DA marker keep the plain-line source as a declared, test-pinned boundary. _one_body collapses same-value re-statements while two distinct parsed values stay a loud abort, so a disagreeing decoy can never be silently absorbed. check_phase_conformance.py accepts a multi-declaration Severity chain only when it strictly escalates (Minor < Major < Critical — the observed self-correction direction, ms01_quant r1's Major -> Critical) with a [SEVERITY-SUPERSEDED] advisory trail in the gate log; de-escalation (which could waive the Critical/Major Evidence-Anchor hard gate with one appended weaker line), non-monotone or repeated-value chains, and same-line pipe pairs all keep the abort. Dual-track review (security opus + codex gpt-5.6-sol xhigh) surfaced 8 advisory findings, all first-party verified and closed; round 2 converged clean on both tracks, including a differential proof that the post-fix abort set is a strict superset of the pre-fix one. 16 new pinning tests cover fence-state decoys after malformed/short/mismatched/Unicode-separator closers, CRLF, the operative-value anchor pin, and the padded-span conflict abort.

  • Monthly harness-retirement scope and debt wording no longer drift (#617). The scheduled generator now checks out the repository and renders its issue body from scripts/ars_phase_scope_manifest.json, so the Bucket A inventory, per-skill counts, and agent names stay aligned with the authoritative manifest (currently 23 agents, including timeline_extraction_agent). The generated debt categories are model-agnostic, and regression tests cover manifest growth, deterministic CLI output, and removal of the stale inline workflow list.

  • E4 dispatch harness: the evidence contract becomes structural (#608). reviewer-e4/2026-07-27 requires every checker-rejected response and its checker output to survive a retry, and the 2026-07-27 Spec-A fleet lost that on both launched panels (observed_provenance_invalid_rate: 1.00, gate verdict NOT_COMPUTABLE) because a retry wrote over the response it was retrying. That is not an operator mistake to correct by trying harder: the preservation step sat at the moment the operator was trying to get the run to proceed. New scripts/dispatch_e4_panel.py inverts the order — a response is written to an attempt-numbered path with O_EXCL before any checker may judge it, so preservation precedes the decision to retry instead of depending on it, and each checker invocation's own bytes are stored beside the response it judged. Checkers run from inside the bundle with relative paths, so nothing has to be stripped and every stored diagnostic is verbatim. Paper-blind and paper-visible calls now get separate whitelisted sandboxes and the blind one does not contain the manuscript at all, making blindness a filesystem fact rather than the seat's restraint (hand dispatch put every artifact in one directory); evals/ is outside both, and the contamination fence is a PATH allowlist rather than a word denylist: the harness may read only the contract, the seven agent files, and the three manuscripts, so a manifest is unreadable and a future held-out artifact is unreadable by default. A word denylist was written first and measured to be worse than the failure it guarded against — manifest and seeded are ordinary review vocabulary and 5 of the 18 committed real panels of this set contain one, so gating assembled prompts would have aborted roughly a quarter of panels after all five cards existed, with no replacement draw permitted, which is a false-abort channel of the exact kind #609 was raised to remove. Ground-truth tokens in model output are now an advisory leak_canary_hits field, never a gate. The seat set is derived from the contract with panel_size asserted rather than hard-coded a third time; only a reviewer-conformance exit is retried, because §11 routes every exit-2 class to abort-no-retry and retrying one would file a phase1_retries event the evidence contract does not classify as a retry; and the one permitted Phase 2 recovery is recognised from the checker's own [PROTOCOL-VIOLATION: multi_dissent=true] line, now pinned by the checker's tests so a reword fails CI instead of silently killing a fleet. The four closed status fields are derived, with provenance_status computed by re-checking that each named location still resolves rather than by trusting the write path. The work directory mirrors the set's own tree — runs/<stem>.json beside runs/raw/<stem>/, or the blocked namespace for an aborted panel — with every *_location already record-relative, so promoting a run is a copy and nothing is rewritten at commit time; that rewrite is what previously turned a verbatim diagnostic into a paraphrase. A completed panel carries adjudication.status: "pending" because the harness cannot fill per_defect without the held-out manifest and does not guess. A work directory inside the repository is refused with nothing written, since writing a record there is the act being refused, and an internal preservation fault produces a blocked record rather than a traceback: losing the record is the one failure mode this mechanism cannot afford. Cross-model review then found four more places where the harness was not yet dispatching the frozen shape or not yet durable, all fixed here: the field-analysis call sent a one-line placeholder instead of field_analyst_agent.md; the synthesizer received only its sprint-contract arithmetic subsection, which stops above its Editorial Decision Letter and Revision Roadmap instructions, so a panel could be score-eligible with neither artifact while the arithmetic checker still passed; a synthesis-layer failure (checker exit 1) aborted instead of being voided and re-run once with the diagnostics as delimited data, as §8.1 requires, so ordinary stochastic formatting blocked valid panels; and a transport timeout or missing binary escaped as a traceback with no record at all, while a nonzero exit truncated stderr to 400 characters inside a field stamped verbatim. A no-response event now writes its exact bytes and blocks the run without filing a retry event, which is what the contract says a re-dispatch after no response is. --date and --fixture are validated before they become path components (one separator relocated the evidence bundle, filed a blocked run under the scored namespace, or lost the record entirely); read_prompt_material refuses a path that reaches its target through a link, since an allowlist over names would otherwise authorize whatever the name points at; a record's diagnostic field is stripped of absolute local paths before a blocked record is committed to a public repo; and seats_for runs inside the handler so contract drift leaves a blocked record rather than a traceback. Two further review rounds found nine more, in the same two classes. What the panel was asked: the five Phase 2 calls never received the Phase 0 reviewer configuration cards, so a generic seat was dispatched where full mode defines a configured identity (the cards go to Phase 2 only, being paper-derived); the one permitted Phase 1 retry re-sent the identical prompt, and since each claude -p is a fresh conversation it could not learn the lint gap §4 requires be hinted; and the CLI was launched without --bare, so it auto-discovered the maintainer's user-level CLAUDE.md, hooks, plugins and auto-memory -- context the allowlist never authorized, reaching a paper-blind prompt -- while the record already attested "no session persistence" (--bare requires ANTHROPIC_API_KEY, an operational cost taken so the fence is true). What the record attests: suite_commit was read AFTER a tens-of-minutes panel and ignored a dirty tree, so it could name a commit the dispatched bytes never came from; it is now read before dispatch and paired with suite_commit_reproducible. Durability: repeating a blocked panel into the same work directory made emit's bundle rename raise ENOTEMPTY, losing the record entirely while the traceback's exit 1 reads as this harness's EXIT_BLOCKED -- a fleet driver would file a blocked panel that has no record; relocation is now non-destructive and non-fatal and both emit call sites are wrapped. Honesty of attestation: diagnostic_form is derived from whether the text was rewritten instead of being stamped verbatim unconditionally, and the path scrubber's general strip-anything-path-shaped pass was removed after it was measured to corrupt ordinary diagnostics (title/field/word_count became titleword_count, a DOI URL lost its host). And the symlink refusal added one round earlier was itself a false-abort: comparing resolve() against absolute() refused every set root under a symlinked ancestor, which on darwin means every /tmp path and every pytest tmpdir, with a message blaming the manuscript; it is now scoped to components at or below the declared root, and its pin was rebuilt because the original passed on darwin with the fix removed. A third round then caught the previous round's own fix converting a loud crash into a silent one: making the bundle relocation non-fatal meant a repeated panel identity overwrote the first attempt's record and left it stamped provenance_status: valid while pointing at the first attempt's bundle -- a retry destroying the account of what it replaced, which is the failure this harness exists to eliminate, one layer above where it was fixed. A repeat is now refused before anything moves or is written, the location prefix is computed from where the bundle actually is, the record is written O_EXCL like the artifacts, and locations_resolve_from is enforced in emit rather than only in tests so a prefix mistake downgrades the attestation. The indirection refusal was extended from the set root to every allowlist family from its own base's parent (agent files are now sent whole, so a redirected one would deliver its entire target into a prompt, and a manuscripts/ -> manifests/ directory link would otherwise BE the base and never be walked). Absolute paths are removed at the source rather than by a scrubber, since dropping the over-broad pattern had re-opened exposure for work directories under neither REPO nor $HOME -- the README's own /tmp/... shape. And a fail-closed preflight refuses before dispatch when neither ANTHROPIC_API_KEY nor an apiKeyHelper is visible, since --bare reads neither OAuth nor the keychain and discovering that as a transport abort costs the first call of a fleet. A fourth round found the two deliverables no checker looks at. check_panel_synthesis.py validates the audit lines and the arithmetic and exits 0 on a synthesis carrying no Editorial Decision Letter and no Revision Roadmap, and nothing validates the field analyst at all -- the harness's own clean-path fixture was the placeholder string field analysis and stayed score-eligible, which is a demonstration that an unconfigured panel could be measured. Both are now required by the harness, since it is the only place an absent deliverable can be caught, and the fixtures carry the real sections. The synthesis call is paper-visible again, as the committed 2026-07-25 artifact records it: a blind synthesizer cannot check a disputed reviewer claim against the paper, which is most of what arbitration is. A nonzero exit or a timeout that nonetheless produced output now preserves that output as a partial-response artifact and says so, because the contract's no-response carve-out applies only when there is no response. The auth preflight passes --settings when an apiKeyHelper is the credential path, which it previously accepted without ever supplying. And the run's own work directory and set root join REPO and $HOME as scrubbed prefixes, exact rather than heuristic, since the README's own /tmp/... shape is under neither. One more, found by walking §2 step by step rather than by waiting for a reviewer: step 1 requires the contract be prepared and validated before dispatch, and the harness was sending the on-disk template verbatim. The template carries no generated_at; the committed 2026-07-27 bundle's contract does, because the real dispatch populated it, so the seats were receiving a different contract from the one the frozen shape sends -- and step 1's abort-on-error gate was absent entirely, so a malformed contract would have reached all five seats unchecked. prepare_contract stamps generated_at (--generated-at, defaulting to now in UTC) and validate_contract runs check_sprint_contract.py on the prepared object, refusing before the first call. A fifth round found five more, three of them created by the fixes above. Every seat was receiving all five reviewer configuration cards rather than its own, which SKILL.md Iron Rule #2 forbids and whose anti-pattern table names overlap suppression as "unexecutable under blindness" -- shipping the panel's angles to each seat is what would make it executable, and a suppressed finding is a MISSED in strict recall, so the fix for one instance would have depressed the metric the fleet exists to produce; each seat now gets only its own card, and a seat with none is told so rather than handed its peers'. The contract validator staged its file inside the work directory and ran before the auth preflight, so a refusal printing "nothing was written" wrote anyway, poisoned the emptiness precondition, and turned the documented fix-and-retry into a blocked record for a panel that never dispatched, consuming the panel identity; it now stages in a scratch directory after the preflight. The synthesis call passed diagnostics into the manuscript parameter, so every first attempt received <paper_content>None</paper_content> and every retry received the checker transcript as the paper -- caught only because a reviewer read the call site, since the existing test exercised the builder rather than the wiring. Instructions and data are now sent as the system and user halves §2 names rather than concatenated into one user message; a malformed contract leaves a blocked record instead of an uncaught exit; the <reviewer_configuration> block carries the Iron Rule #7 data-boundary sentence its two siblings already had; and a filesystem-root work directory can no longer reduce a scrub prefix to / and delete every slash in a diagnostic. A sixth round found six more. A seat that follows its own instruction -- on two or more dissents, abort with [PROTOCOL-VIOLATION: multi_dissent=true] INSTEAD of drafting a card -- failed the checker at parse_report, whose output then never carries the token, so a compliant seat lost the one §5 recovery; the token-only shape (strictly one non-blank line, so it cannot be smuggled inside a real card) is now recognised from the response itself. Card #4's slice ran to end-of-analysis and swallowed ## Review Strategy Recommendations, whose reviewer-complementarity notes are panel-wide Iron Rule #2 material; a card now ends at the next card or the next section heading. A field analysis missing any of the four required cards (the analyst's own quality gate) dispatched a generic seat while staying score-eligible; it now aborts at field analysis, and only the DA -- seat five, cardless by design -- gets the no-card notice. The field-analysis and synthesis calls gained the Iron Rule #7 data-boundary sentence their agent files do not carry (under --bare --system-prompt nothing else would say it). A [DELIVERABLE-MISSING] diagnostic is now written to the artifact the record names as authoritative rather than pointing at a model response or an already-PASS gate log. And per §6's independent cycles, one seat's conformance failure no longer stops the remaining seats: every seat runs, every attempt is preserved, and the panel then aborts with the first failure -- a transport fault stays panel-fatal. check_phase_conformance.py gains --phase1-only (mutually exclusive with --phase2, mirroring the existing --synthesis / --layer1-only split) because the retry decision is taken while Phase 2 has not been requested yet, and a reimplemented gate would not be the checker's own output. A seventh round — codex r6 plus a closing security pass that drove the whole contract on the scripted transport and swept every assembled prompt — found six more. The deliverable gate was a literal ## -substring test and failed in both directions: the committed synthesizer output varies its heading level (# Part 1: Editorial Decision Letter in two acceptance-cohort panels) and sometimes drops the "Part N: " wrapper, so 3 of the 18 committed real panels would have aborted AFTER the full panel had burned with no replacement draw permitted — the #609 false-abort channel reopened at the synthesis step — while a synthesis that merely mentioned the heading in prose, or emitted it empty, or inside a fence, passed. Sections are now resolved as real nonblank heading sections via the repo's shared heading_section (H1-H3, "Part N: " wrapper optional); the one 2026-07-24 panel titling its letter # Editorial Decision stays outside the gate as a documented accepted miss, because accepting that bare name would also accept a seat's own ## Editorial Decision section pasted into a synthesis. The Phase 1 retry hint was delivered as user content where §4 says "hinted in the system prompt" — role placement is part of the registered condition — and now rides the system half. A precondition refusal into a work directory holding an interrupted attempt's bundle reopened that bundle, appended the new abort to its journal, and renamed the whole directory — stale responses included — under the new blocked stem with the record attesting provenance_status: valid over evidence it never produced; an existing bundle is now refused untouched, with no record written, because a duplicate refusal record costs nothing and the earlier evidence has no other copy. A mid-edit contract template escaped main as a JSONDecodeError traceback with no record (its exit 1 reads as EXIT_BLOCKED to a fleet driver); it is now a precondition failure with a blocked record. Agent files were read lazily per call, so a checkout change mid-panel could deliver different bytes to later seats while the record still named the pre-dispatch suite_commit as reproducible; the seven files are snapshotted at builder construction. And card_for scanned the whole analysis for the first Card #N mention, so a card number named in an earlier inconsistency note would hand that seat the surrounding prose as its configuration with the missing-cards gate none the wiser; only the cards section is searched now, found by the same H1-H3 variants the deliverable gate accepts, so a variant analysis cannot pass the gate and then dispatch a cardless panel. A seat that decorates the multi-dissent token with prose or a fence still loses the §5 recovery — pinned as a documented accepted miss, since the strictly-one-line shape is what keeps the token from being smuggled out of a real card. The set README gains the harness bundle-directory layout in §6 and a comparability rule: harness cohorts change the dispatched condition (--bare, per-seat cards, split system/user halves, separate sandboxes, stamped contract), so they are never compared against the hand-dispatched rows and both conditions re-run under the harness. An eighth round — codex r7 in parallel with a second closing security pass — then caught the seventh round's own deliverable fix repeating the class it fixed, plus five more. The heading-variant rule judged emptiness by heading_section's same-or-higher-level body, and the committed synthesizer output organises a letter's content as sibling H2 sections as often as child H3 ones, so two real panels (one in the operative 2026-07-25 acceptance cohort) read as deliverable-less — the net false-abort count was unchanged at 3/18 with two panels swapped, and no test guarded them. The gate now measures the deliverable's INTERVAL — from its heading to the next required-deliverable heading — via a position-aware _heading_lines whose fence rules are copied from heading_section and pinned to it by test; measured against all 18 committed panels the one abort left is the documented accepted miss, and the interval rule also closes two shapes the seventh-round spelling passed (an empty letter borrowing a contentful roadmap's body, in either heading-level direction). Builder construction had ALSO moved the agent-file read outside dispatch_panel's handler, so a symlinked or missing agent file escaped as a traceback after the bundle was on disk — no record, with exit 1 reading as EXIT_BLOCKED; construction is back inside the handler and an unreadable prompt file is a PreconditionFailure. The synthesis boundary sentence now covers every delimited block, because a manuscript directive can be echoed into a reviewer card or the field analysis and the synthesizer's agent file carries no untrusted-material rule; the Phase 1 metadata envelope — whose title is quoted from the manuscript's H1 — gains the same data-only sentence. A claude exit 0 with empty stdout is a transport event rather than a conformance failure that consumes the one permitted Phase 1 retry. And git provenance failure (outside a worktree both commands fail while status prints nothing) no longer reads as a clean tree at commit "unknown": unknown provenance is declared dirty, so suite_commit_reproducible cannot be true for a commit that does not exist. Two shapes are documented as accepted misses rather than gated: a field analysis whose four cards are bare heading shells (validating card substance across the committed card shapes — inline single-line cards and heading cards — risks the false-abort class this round just removed twice, the analyst's own quality gate owns card content, and all 19 committed analyses carry substantive cards), and a deliverable heading hidden inside an HTML comment (_heading_lines mirrors heading_section in tracking fences but not comments; the comment-visibility channel is #613's scope). A ninth round — codex r8 in parallel with a third closing security pass (which confirmed the interval rule at 1/18 aborts, both prior regressions closed, and found nothing blocking) — added five more. The CLI's own built-in tools are now denied per call (--disallowedTools): --bare cuts customization and --strict-mcp-config cuts MCP, but neither disables Read/Bash/WebSearch, and the checkout is public, so a paper-blind seat could otherwise retrieve a manuscript's held-out siblings mid-call with no tool-use audit trail in a text response — the seats' task is pure text and needs no tool; a NEW tool name shipping in a later CLI is a declared residual. §6's independent cycles are confined to reviewer-conformance failures: a checker infra exit (2) or crash (1) inside the seat loop now aborts the round immediately instead of re-running the same global fault once per seat. A mid-panel checkout change downgrades suite_commit_reproducible (the checkers and their imports load from REPO afresh at each gate, so the pre-dispatch snapshot alone could attest a reproducible commit for a run that mixed checker versions). A regular file passed as --work-dir is a stated precondition refusal instead of a NotADirectoryError traceback with no record. And --date must be a real calendar day in canonical ISO form — the shape regex alone passed 2026-02-31, which would have consumed a full panel and been committed as invalid provenance. One more shape is documented as an accepted miss: heading lines count as interval content (excluding them would reject the committed pattern that writes the decision in a heading line, a real-corpus shape, to close one with zero corpus occurrences). A tenth round — codex r9 in parallel with a fourth closing security pass — caught the ninth round's own tool fix being the wrong polarity, plus five more. The security pass measured the --disallowedTools deny list incomplete against the installed CLI the day it was written (13 built-in names beyond its 14) and found its completeness sentence to be a false attestation; the CLI's own whitelist spelling --tools "" now does the shutoff — under an emptied whitelist a tool added by a later CLI is closed by default, the property a deny list can never have, and the argument that replaced this harness's word denylist with a path allowlist — with the deny list riding behind as depth only. The auth path stopped smuggling context past --bare: --settings loads the whole file it names, and the user's own settings may carry env, hooks and plugin configuration, so the apiKeyHelper now travels alone in a staged one-key file, and nothing is passed at all when ANTHROPIC_API_KEY is set. A synthesis whose checker passes but whose Editorial Decision Letter or Revision Roadmap is absent is voided and re-run once per §8.1 instead of aborting a completed panel on an ordinary stochastic omission after all twelve calls had burned. An external --set-root no longer reads as reproducible provenance (its manuscript bytes are not covered by the repo commit). A sandbox-creation or initial-write failure — read-only parent, full storage, two processes racing on one work directory — is a stated setup result instead of a traceback whose exit 1 reads as EXIT_BLOCKED with no record. And stem_for applies the same calendar-day validation as the CLI, so the library-level guard is not weaker than the entry point's. An eleventh round — codex r10 in parallel with the fifth closing security pass — added seven more, three behavioral and four hygiene. The hard-coded Phase 1 metadata field mislabeled the MS02 quality-assurance manuscript as educational technology for every paper-blind call, biasing that fixture's measurement before any seat saw its card; the field now follows the fixture (the ms00 value matches the committed 2026-07-27 bundle's metadata). card_for's catch-all line regex minted a marker from prose like "covered by Card #3", truncating the current card and letting the named card resolve to the mention line itself; only heading lines open a card now, measured against all 26 committed field analyses (every card in the corpus opens with a heading, every cross-mention sits in prose). A transport timeout's summary no longer serializes str(TimeoutExpired) — the whole argv, system prompt and staged paths included — into a log meant for public commit. Hygiene from the security pass: the apiKeyHelper staging happens once per transport instead of once per call (a six-panel fleet would have strewn ~96 copies of the helper command across the temp tree); an EMPTY leftover bundle/ no longer blocks the refusal record (it holds no evidence to protect, and the message sent the operator to move aside evidence that did not exist); _bare_auth_available returns an actual bool; and the README names the pre-fleet single-panel smoke test for the never-live-called --tools "" and --bare/xhigh/thinking interactions. A twelfth round — codex r11 in parallel with the sixth closing security pass (which measured the heading-anchored card slicing against the full corpus: 110 slices, zero deltas, confirming the old regex had never actually truncated a committed card) — added two corrections and pinned one rebuttal. A shrunk panel now aborts with §6's own [PANEL-SHRUNK: usable=<k>, panel_size=<N>] cardinality marker ahead of the first seat's diagnostic, so the operational monitor can count these rounds. The one console refusal assembled from a raw exception is scrubbed (an OSError spells out absolute paths; console-only, never a committed record). A claimed missing §8.1 terminal marker was REFUTED by measurement and pinned: the synthesis checker's exit-1 output opens with its own [PANEL-SYNTHESIS-MISMATCH...] line and the abort diagnostic is exactly that line. The corpus-size figure in the card-slicing rationale is corrected from 30 to 26 (three independent counts agree; the conclusion it supported was re-verified on all 26). A thirteenth round — codex r12 in parallel with the seventh closing security pass — added six more. The security pass caught a stray .coverage build artifact (a SQLite file carrying the operator's absolute paths) that a bulk git add had swept into the amended commit; it is removed and ignored (.coverage, .coverage.*, htmlcov/). It also proved the path scrubber's resolved-only prefixes never matched an OSError's caller-spelled paths — on darwin /tmp resolves to /private/tmp, so a setup failure under the README's own --work-dir /tmp/... example put an absolute path into a committed record, refuting the README's no-absolute-path sentence; RUN_ROOTS now registers both spellings (absolute only, since a short relative spelling would corrupt ordinary prose as a substring). Sandbox inputs are exclusive-create, so two panels accidentally given the same empty work directory can no longer silently cross-contaminate — the loser's writes previously overwrote the winner's contract, metadata and manuscript before the bundle's O_EXCL collision, letting gates check Phase 1 against the other fixture's paper. A mid-panel OSError (a gate-log write, a checker launch) leaves a blocked [IO-FAULT: ...] record instead of escaping as a traceback. An abort diagnostic now exists byte-for-byte in the artifact its record names authoritative: the panel-shrunk composite goes to its own panel-shrunk.log, and the transport response-status suffix is composed before the log is written. And --replicate is validated up front (1-99, in stem_for too): a nonpositive value minted normal-looking run ids, and a long one raised ENAMETOOLONG only in emit after the full panel had burned. A fourteenth round — codex r13 in parallel with the eighth closing security pass (which verified the diagnostic-artifact equivalence across all twelve abort shapes and the /tmp scrub end-to-end) — added four more. A [PHASE1-MANUSCRIPT-LEAK] verdict is never granted the Phase 1 retry: the leak shares exit 3 with the structural lints, so the harness retried it and a clean second attempt made the contaminated panel score-eligible, although the checker's own comment names blindness "the half a retry must not be granted in spite of". The Phase 2 card wrapper no longer forbids what the card exists to do: it said the card "may not alter your identity" while the card IS where full mode's reviewer identity comes from, so an obedient seat would refuse its own configuration and the harness would measure generic reviewers — identity adoption is now authorized explicitly while Phase 1 commitments, scoring procedure and output format stay fenced. Allowlist keys bind to the declared LEXICAL paths, never resolved ones: resolving a manuscript name that is itself a symlink to a held-out manifest inserted the manifest's real path as an allowed key, and a read via the target's own spelling then passed both membership and the symlink walk. And main's precondition stage catches OSError too — validate_contract stages in a temp directory, and a full TMPDIR escaped as a traceback with no record while the work directory could be on a different, writable filesystem (_git_state is guarded the same way, and the temp root joins the scrub prefixes). A fifteenth round — codex r14 in parallel with the ninth closing security pass — hardened the last second-order failure paths. The post-panel provenance re-probe is guarded (a git spawn failure AFTER a completed, expensive panel raised before _emit_or_explain and cost the record; the initial probe already tolerated the same OSError). Every abort handler's own journal and artifact writes are best-effort — an exception raised inside an except block is not routed to a later sibling, so a journal device failing mid-abort escaped WITH the blocked result it was recording; a missing artifact is caught by locations_resolve_from, which downgrades the attestation instead of losing the record. A setup failure after the bundle was created returns that bundle rather than None, so the refusal path no longer mistakes this invocation's own half-written bundle for an earlier attempt's evidence. emit refuses a repeated identity across BOTH record namespaces — a successful run repeated produced a blocked record beside the normal one under the identical identity. And the two OSError-derived diagnostics declare their rewrite honestly: they were scrubbed before composition, so scrub found nothing left to change and stamped verbatim on strings whose paths were in fact removed; PanelAborted now carries an explicit form set at composition. A sixteenth round — codex r15 in parallel with the tenth closing security pass — closed the race and custody residue. The work directory is claimed atomically (.claimed, O_EXCL) before anything else: two processes racing past the emptiness check both built state in one directory, and the loser could consume the run identity with a blocked record, leaving the winner's finished panel unable to emit — the loser of the claim now dies having written nothing. A setup failure journals its diagnostic so the record's authoritative artifact actually holds the line, and a bundle that merely OPENED a directory already holding content is never handed back by the setup handler (the stale branch keeps custody — the same F3 class, reopened by the previous round's own fix and caught by the security pass). The staged apiKeyHelper copy is removed at process exit. One provenance bound is declared rather than detected, in the README: an edit made during a gate and reverted before the end probe is not caught — prompt material is snapshotted and immune, the checkers load per gate, so the checkout must not move while a panel runs. A seventeenth round — codex r16 in parallel with the eleventh closing security pass — polished the remaining edges. A claim-time OSError (unwritable parent, full disk) is a stated precondition refusal instead of an uncaught exit 1. Record locations are serialized with POSIX separators regardless of platform, so a record produced on Windows still resolves when committed and read on POSIX. A settings file whose valid JSON has a non-object top level carries no helper instead of crashing the auth preflight. The README's normalized definition now describes the scrubber's actual (and intended) semantics — registered run roots are removed wherever they appear in a harness-assembled diagnostic, since an OSError spells its path mid-sentence, not only leading. And the claim-refusal wording credits an earlier or concurrent invocation rather than always another one. An eighteenth round — codex r17 in parallel with the twelfth closing security pass (which delivered the final residual inventory) — closed the last escape hatches. The blindness check runs BEFORE structural parsing under --phase1-only: a response both malformed and carrying a manuscript shingle reported only the grammar failure, so the dispatcher granted the retry a proven leak must never receive. locations_resolve_from covers synthesis_retries (the bundle-side check already covered every retry event; the record-side predicate now matches). Undecodable UTF-8 in prompt material or in the settings file is a stated refusal, not a UnicodeDecodeError traceback — it is a ValueError, caught by neither precondition handler on its own. And transport construction (which re-reads settings and stages the helper file, after .claimed exists) routes its failures through the record path. A nineteenth round — codex r18 in parallel with the thirteenth closing security pass (which proved the checker-order asymmetry unreachable: a leaking seat's Phase 2 is never dispatched) — rejected the structurally ambiguous shapes. A synthesis carrying TWO letters or two roadmaps was score-eligible on the first interval; a duplicate is now rejected like an absence, riding the same §8.1 rerun. A duplicated configuration-card number let card_for hand the seat the first copy while synthesis received the whole conflicting analysis; the panel now aborts at field analysis naming the duplicated card. And the transport-construction handler declares its diagnostic rewrite (normalized) like its two sibling handlers instead of stamping verbatim on a pre-scrubbed string. A twentieth round — codex r19 in parallel with the fourteenth closing security pass — held the measured condition steady on three more edges and pinned one rebuttal. An unexpected Card #5 (six superseded-namespace analyses emit one spontaneously; none of the 18 scored panels do) never reaches the DA, whose seat is cardless by design. The work directory is made absolute at entry: a relative --work-dir left the sandbox relative, and the subprocess cd's INTO the sandbox before the CLI re-resolves the same relative --add-dir from its new cwd. The helper value is read in one defensive snapshot, since a settings change between the availability probe and the second read raised KeyError past the transport-construction handler. And the claimed late-provenance routing gap was REFUTED by measurement and pinned: the reachable shape (an artifact vanishing before emission) is routed to the blocked namespaces by the bundle-side re-check that runs before the destination is chosen — the record-relative predicate downstream is a defensive invariant over emit's own arithmetic. A twenty-first round — codex r20 in parallel with the fifteenth closing security pass — tightened the last preflight edge and pinned two boundaries. A truthy non-string or whitespace-only apiKeyHelper no longer passes the preflight and stages an unusable credential (which would have converted an operator precondition into a dispatched blocked run — the exact cost the preflight exists to prevent). A claimed final-artifact completeness gap was resolved AGAINST widening provenance_status: the README scopes that closed two-value field to retry-evidence completeness alone and states verbatim that valid does not attest panel completeness — an accepted artifact deleted by an outside force after dispatch is the maintainer's commit-time concern, with the raw bundle beside the record; the scoping is pinned by a test quoting the contract. And the Card #5 guard's rationale comment counts the corpus correctly (six superseded-namespace analyses, none scored). A twenty-second round — codex r21 in parallel with the sixteenth closing security pass — sealed the delimiter boundary. Every untrusted block travels through one _delimited helper that refuses text carrying its own closing delimiter — a manuscript containing </paper_content> (or a card containing </reviewer_configuration>, and so on for every block) would close the declared data early and let the remainder read as instructions; refusal keeps the dispatched bytes untouched, where an escape scheme would change what the model receives. The retry hint's checker transcript — which rides the system half per §4 and can echo model-controlled text — is now explicitly fenced as checker-output DATA and delimiter-checked like every other block. A symlink-loop work or set root is a stated precondition refusal instead of a RuntimeError traceback. And a whitespace-only ANTHROPIC_API_KEY fails the preflight instead of suppressing a valid helper and failing the first live call. A twenty-third round — codex r22 (down to a single finding) in parallel with the seventeenth closing security pass (which byte-audited the delimiter refactor: exactly one intended line changed across all 32 prompt halves, zero corpus hits for any closer token) — worked the token-only audit classification to its honest resting place — in two steps, the second correcting the first. The first spelling replaced the retry event's diagnostic with the response's token line; the next codex round refuted it against the contract (verbatim is byte-for-byte CHECKER output, and the named gate log did not hold the replacement). The retry event therefore keeps the checker's own line — its eligibility was already machine-readable as stage: phase2_multi_dissent, with the bare token verbatim in the named rejected response — and §11's exhausted marker rides the TERMINAL record only, in a harness-written artifact that is byte-equal to the recorded diagnostic, with the checker's gate log untouched beside it. A twenty-fifth round — codex r24 in parallel with the nineteenth closing security pass — hardened the delimiter refusal to whitespace-variant closers (</paper_content > is as valid an end tag as the exact spelling) and made an operator interrupt durable: Ctrl-C mid-call escaped everything, stranding a .claimed directory with a partial bundle and no record, which a rerun then refused — the interrupt now leaves a blocked record with its own byte-equal artifact, every completed attempt already preserved by write-once. A twenty-sixth round — codex r25, again a single finding — restored the restarted Phase 1's own §4 retry: the §5 multi-dissent recovery restarts at Phase 1, and every Phase 1 pass carries one structural retry, but the restart was dispatched with a single-attempt budget, so an ordinary formatting slip on the replacement blocked an otherwise recoverable panel. A twenty-seventh round — codex r26 in parallel with the twenty-first closing security pass — finished the fence equivalences. Closer detection is case-insensitive and covers the self-closing spelling (HTML reads tag names case-insensitively and a model may too). Card discovery and duplicate counting are fence-aware like the deliverable gate, so a fenced template inside the cards section cannot configure a seat from example text. The bare-root scrub pass requires a path boundary (/tmp registered no longer eats the /tmp inside a sibling /tmp2/file). And a helper that dies between preflight and staging is a loud precondition instead of an empty flag list that would launch --bare uncredentialed and burn the first live call. A twenty-eighth round — the twenty-second closing security pass, with codex r27 returning ZERO findings for the first time — caught the previous round's helper guard sitting behind the tolerant probe's short-circuit, unreachable for the very scenario it named; auth_flags now classifies the settings file itself in one read, and reaching it without any usable credential — never configured, deleted, rewritten or broken since the preflight — refuses loudly instead of launching --bare uncredentialed. A twenty-ninth round — codex r28 in parallel with the twenty-third closing security pass — carried the §4 grammar into the checker itself and stopped mutating refused directories. parse_phase1 validated only the Scoring Plan, so a response missing its Contract Paraphrase and terminal [CONTRACT-ACKNOWLEDGED] passed --phase1-only and the dispatcher could retry — or accept — a protocol-invalid Phase 1; the checker now enforces all three §4 requirements, on both its modes. And the work-directory claim looks before it plants: an accidental --work-dir /tmp gets its refusal record without a stray .claimed first, while the post-claim emptiness check stays for the race. A thirtieth round — codex r29 in parallel with the twenty-fourth closing security pass (which replayed the four real dispatch Phase 1 outputs against the stricter grammar: all PASS, zero false aborts) — gave the paraphrase check its floor: a bare heading over one line passed although the contract's paraphrase_minimum_dimensions is "all", so the paragraph count is now the machine-checkable lower bound (the real outputs carry exactly one paragraph per dimension, and the test fixture was raised to that valid shape); whether each paragraph is TIED to its dimension stays with the seat's own §4 preflight. A second interrupt window (after dispatch, before the record) was resolved AGAINST further handling, with the security pass's standing measurement: the window is sub-second, the bundle already sits preserved on disk, and re-entering a half-finished emission would add real complexity for a recovery the write-once layout already provides by hand. A thirty-first round — codex r30 in parallel with the twenty-fifth closing security pass — made the transport abort path tell the whole truth. A partial-response write that itself fails is no longer claimed "preserved" (the summary now says it could NOT be), and the transport summary and stderr are scrubbed at the source so the committed log matches the record byte-for-byte — the partial response itself is evidence and is never rewritten. A thirty-second round — codex r31, a single finding — routed SIGTERM into the same durable abort path as Ctrl-C: a fleet runner cancels with SIGTERM, which does not raise KeyboardInterrupt on its own, so the process exited with no blocked record and a stranded .claimed marker. A thirty-third round — codex r32 — installed the record atomically (staged .tmp plus os.replace; an ENOSPC mid-write left a truncated JSON at the final path that later runs refused, unrecoverable normally) and rerouted a late record-side predicate failure to the blocked namespaces — the third reviewer angle on this seam; the second rename cannot collide because the identity check already covered both namespaces. A thirty-fourth round — codex r33 — completed the §4 grammar (the H2 sequence must be exactly ## Contract Paraphrase then ## Scoring Plan, in order and nothing else at H2; presence alone let a reordered or extra-sectioned precommitment pass, and all four real dispatch outputs carry exactly the required sequence) and wrapped the whole claim-to-record span against interrupts: an interrupt outside the dispatch loop — preflight, contract staging, transport setup, emission — now exits with a stated refusal instead of a traceback, everything preserved in place. A thirty-fifth round — codex r33's successor — aligned the Phase 1 envelope with the seat files' own promise (all five state the contract arrives "under ## Contract" and the metadata "under ## Paper Metadata"; the plain labels were a different envelope from the registered instructions) and made the terminal acknowledgement check fence-aware, so an acknowledgement that exists only inside an unclosed code fence no longer passes. A thirty-sixth round — codex r34's successor — carried the §11 cardinality marker onto the synthesis-stage seat failure (§8.1 classifies that exit 3 as an unusable reviewer; the count is honestly unknown, the checker's own finding rides in the diagnostic) and scrubbed checker crash tracebacks at the run_checker outlet — a crashing checker's stderr spells absolute script and module paths straight into committed gate logs, while normal checker output is relative-path-only so the scrub is a no-op there and verbatim stays honest. A thirty-seventh round — codex r35's successor — closed the two second-order effects of the previous fixes. The acknowledgement check now requires BOTH tails: the raw tail catches output trailing after the marker (a fenced block vanished from the structural view before the tail was computed), the fence-aware tail catches a marker that exists only as fenced code. And the checker-output normalization state travels with the output — run_checker returns a third element, carried through the attempt chain into PanelAborted and RetryEvent, so a scrubbed crash diagnostic is stamped normalized, never verbatim. A thirty-eighth round — codex r36's successor — closed the last race seam and the last acknowledgement spelling. .claimed existing refuses OUTRIGHT, before the occupied pre-check: an occupied-looking directory may be the owner's live run (sandboxes built, bundle still empty), and skipping the claim check there let a loser claim the owner's empty bundle through the stale branch and consume the identity. And the acknowledgement is compared unstripped bar the line ending — an indented marker renders as a code block, and stripping before comparison let it pass the exact terminal-line requirement. A thirty-ninth round — codex r37's successor — widened the closer refusal to attributed end tags: </paper_content data-x=1> is invalid HTML, but tolerant parsers accept it, and the boundary must not depend on the model being a strict parser — while the word boundary keeps a longer tag name (</paper_contents>) out. A fortieth round — codex r38's successor — bounded the last card at any non-card heading of its own level or higher (a literal ## check would let an H3-headed strategy section ride inside an H3-headed Card #4) and gave the exhausted synthesis retry its §11 [SYNTHESIS-MISMATCH] terminal marker, riding its own byte-equal artifact with the gate log untouched beside it. A forty-first round — codex r40's successor — closed the last two verbatim false attestations in the abort handlers: the transport arm derived the form from the summary alone (a scrubbed stderr left the named log rewritten under a verbatim stamp), and the two preflight failure paths dropped the fact of normalization entirely — validate_contract discarded the checker's own form and the OSError arm pre-stripped the path so the record's scrub fallback saw clean text; PreconditionFailure now carries the form to the record. A forty-second round — codex r41's successor — closed three install-side gaps: the §4 paragraph floor counted six bare ### Dn headings as six paragraphs (a heading now separates paragraphs and never counts as one; all four committed Phase 1 outputs still PASS), the staged record file became the identity lock — one path per stem across BOTH namespaces, re-checked inside the lock — so a concurrent emission of the same identity can no longer leave a scored record beside a blocked one, and a staged-write failure after the raw bundle moved into runs/raw/ now rolls the bundle back so the identity is not consumed by a failure that wrote no record. A forty-third round — codex r42's successor — extended the §4 separator set to the other zero-content lines (thematic breaks, single-line HTML comments); the list is closed by design, not a CommonMark block classifier — a bulleted paraphrase still counts as content, because refusing real prose over formatting is the false-abort channel #609 exists to remove. A forty-fourth round — codex r43's successor — kept the abort classifiers honest on three edges: multi-line HTML comment blocks join the zero-content set via a conservative line-leading-opener state (prose mentioning <!-- mid-line still counts), the §11 [SYNTHESIS-MISMATCH] marker now requires the retry-attempt checker to have actually spoken (a checker crash on attempt 2 keeps its own infra diagnostic), and the exhausted multi-dissent marker fires only on a conformance exit that survived the one retry — a token-only response over an infra exit on attempt 1 is no longer dressed as exhausted. A forty-fifth round — codex r44's successor — answered the emission-failure recovery question at the documentation layer: the explain message now names the preserved state, and the set README documents the two recoverable post-panel failure states (any staged-write or install failure rolls back with the identity unconsumed; a rollback failure on top leaves the bundle in place, named on the console) — resume-from-bundle re-emission stays a declared bound of this version, not a CLI feature quietly half-built. A forty-sixth round — codex r45's successor — widened the emission rollback guard to everything after the raw rename (an ENOSPC on the record directory used to strand the bundle in the runs tree, exactly as the recovery notes said it would not) and made the diagnostic scrub process its roots longest-first with deduplication (macOS aliases /tmp onto /private/tmp, and the shorter spelling processed first corrupted the longer one's occurrences mid-path); a third finding — container-prefixed comment shells like - <!-- … --> satisfying the paragraph floor — is declined under the declared closed-separator-list bound: recognizing comment openers behind arbitrary CommonMark container prefixes is the renderer-rewrite slope both this round and #609 exist to refuse, and §4's TIED judgment stays with the seat's preflight. A forty-seventh round — codex r46's successor — admitted lone list markers (-/*/+ with no item text) into the closed separator list (zero parse slope, zero false-abort surface — a marker WITH text still counts), and honored the record contract's MUST at emission: a terminal abort whose named artifact fails to resolve is rewritten once from its own diagnostic (the bytes ARE the diagnostic, so equality holds by construction) before the downgrade fires, and the README now states plainly that the downgrade is the terminal fallback for any unresolvable named location, not an attestation that a retry occurred. A forty-eighth round — codex r47's successor — closed the zero-content axis with an explicit termination bound written into the counter itself: variants beyond the closed list (container-prefixed comments, malformed comments like <!-->, entity/whitespace tricks) are declared out of scope — the variant space is unbounded, the observed base rate in committed panels is zero, and §4's substantive judgment sits with the seat's preflight, not this floor. 259 new cases (239 harness + 20 conformance) run the whole contract on a scripted transport with no model call, reusing the two checkers' own valid fixtures so a card that stops satisfying a real gate stops satisfying the harness tests too. Measurement-side only: no reviewer prompt, contract, template, or fixture changed.

  • An empty-but-present dissent section no longer destroys the panel through an unretryable Phase 2 abort (#609). scripts/check_phase_conformance.py treated a ## Scoring Plan Dissent heading carrying no canonical dimension_id: line as a hard conformance failure; because Phase 2 permits no retry, that seat's abort took the DA and the synthesizer with it. On the 2026-07-27 Spec-A fleet it fired on 1 of 2 launched panels, an observed conformance-abort rate of 0.50 against the Spec-A diagnostic expectation of approximately zero, so every measurement of the review stage ran through a gate whose completion rate was bounded by how often four or five seats all avoided one formatting slip. The prompt layer was not the cause and was not changed again: both prompt-side preflight rounds (#591, #592) were already ancestors of the suite root the fleet ran on, every seat agent states the omit-the-whole-section rule twice, and the observed Perspective card restated the rule in its own output and still left the heading standing. A section that spells no dissent field at all is now read exactly as an absent section — both inputs mean no dissent — and the run record carries the visible [DISSENT-EMPTY-SECTION: …] diagnostic, including the section's non-blank line count, so the occurrence stays auditable rather than silently absorbed. The tolerance is keyed on field shape, not on canonical spelling, and the shape test is decoration-agnostic by construction: a line is a claimed dissent when the letters preceding its first colon spell exactly a field name, whatever non-letters surround them (NFKC-folded first, so a fullwidth re-spelling cannot present itself as prose), and a claimed dissent aborts unless it is canonical. Enumerating Markdown wrappers instead would leave the next unenumerated one reading as prose — successive cross-model review rounds against a character-class version produced - dimension_id:, then - [ ] dimension_id: and | dimension_id: |, each absorbed as no-dissent; the inverted rule covers those plus link labels, HTML comments, indentation, and hyphen/space spellings without naming any of them. Letters are collected with str.isalpha, so CJK prose that happens to name a field keeps its own letters and stays prose instead of collapsing onto the field name and aborting a panel it should tolerate (zh-TW, ja, and ko prose shapes are pinned); markup spans carrying letters of their own — HTML tags, reference targets, task-list markers, and link destinations up to one nesting level deep — are dropped before the colon is located rather than after, so an absolute link target's own https: cannot win the partition, and decoration beyond those (a deeper nested destination, a quoted HTML attribute containing >, an entity-encoded colon, exotic custom syntax) is deliberately out of scope, with a test pinning that declared limit so it cannot move by accident, because absorbing it costs one advisory-flagged record while a broader rule costs the false aborts this tolerance exists to remove. The scan runs on the section's raw span, not on the sanitized body, so a field the sanitizers delete (fenced block, HTML comment) or relocate (a field spelled as its own ## dimension_id: D1 heading, which split_sections moves out of the body entirely) cannot reach the tolerance branch as an empty section; a field line the canonical parse never saw is a dissent the seat cannot be credited with, so it fails whether or not it is canonically spelled, counted by occurrence rather than matched by value so a hidden copy of a canonical field cannot ride in on its twin's identity. That scan also closes a pre-existing hole it sits on top of: because strip_fences leaves HTML comments in place, canonical fields written inside <!-- … --> parsed as a genuine dissent on main and collected the trigger-binding exemption while the visible card claimed nothing, so a drifting trigger passed; a commented-out field is now struck from the canonical parse and fails as an unparsed occurrence, with the delimiter-opening confined to lines a comment actually covers so a canonical rationale: merely mentioning <!-- in its unrestricted text is not rewritten out of agreement with the canonical parse, while a fence or comment carrying no field stays tolerated. Comment state is resolved by delimiter order rather than by presence: a line that closes one comment and opens another (<!-- an aside --> <!--) leaves the fields below it hidden instead of handing the exemption back, while a balanced aside on a single line hides nothing after it. The closer may reuse the opener's own last two dashes, as CommonMark closes <!--> and <!--->, so ordering the scan does not turn those into unterminated openers that abort a card a presence test had passed; the state machine is pinned line by line in both directions. Only an unfenced, uncommented heading delimits the span — fence state is tracked exactly, mirroring panel.strip_fences under a test that pins the two to agree line-for-line across backtick, tilde, longer-closer, indented, unterminated and CRLF shapes — so a heading written inside a fence or an HTML comment cannot end the span early and hide the fields after it, while a comment opener counts only as a block opener — at most three spaces of indentation, never inside a fence — list and blockquote markers included, since CommonMark opens raw HTML for - <!-- exactly as it does for a bare <!-- and the bullet form needs no closer at all. Indentation is read as columns, not characters: the outer and inner allowances may not add to four (which would make the line indented code), and a tab advances to the next four-column stop, a distinction that put > \t<!-- and - \t<!-- on opposite wrong sides while characters were being counted. Two shapes stay out: a marker following text on its own line, and one indented as a lazy paragraph continuation. Both do form a comment, so the miss is stated at full price rather than discounted: it grants a trigger-binding exemption for a dissent the rendered page does not show, which omitting the section would not have granted. It is refused anyway because closing it deterministically means reading a bare <!-- inside unrestricted rationale: text as an opener, which aborts a valid card on a phase that permits no retry; the card that would die is pinned as a test, as is each limit, and the deterministic closure belongs in the reviewer output grammar rather than in this parser, tracked as #613. Paragraph state is tracked, because it decides which markers may open anything, and each of its rules is state-dependent in a way that cost a defect to learn: only an ordered list starting at 1 interrupts an open paragraph; an ATX heading of any level, a thematic break and a lone - end a paragraph from either state; a = run or two-or-more hyphens is a setext underline only when there IS a paragraph to underline, and is ordinary paragraph text otherwise; an empty list item cannot interrupt a paragraph at all, so a lone *, + or 2. only holds the state down where none is open. Blankness follows CommonMark rather than Python, counting only spaces and tabs, so a line holding an ideographic space stays a paragraph instead of aborting a valid zh-TW card. The opener pattern is matched against a tab-expanded line with one unambiguous run per gap; the earlier spelling let a single space be claimed by either of two adjacent optional groups, doubling the work per nesting level (0.17 s at 20 levels, 2.7 s at 24, a stall by 30) instead of returning a verdict. Measured rather than asserted, against a real CommonMark render over a grid of 511 opener spellings x 14 block contexts x closed or unclosed comment, deduplicated to 14308 distinct shapes; every count below is relative to that grid. Shapes hidden from a reader but credited, and shapes visible to a reader but refused: main 8139 / 0, then 2372 / 337 with no paragraph state at all, 3560 / 61 with paragraph state guessed from mere non-blankness, 3133 / 122 with the setext rule applied unconditionally, 3133 / 0 once each rule was stated per state, and 2889 / 0 once a comment block stopped counting as a paragraph. Read the middle rows rather than the last one: the first interim spelling had no state and its false aborts argue for having some, while each of the next two had state and guessed at it, and every guess cost false aborts until the rules were separated by which state they hold in. The step that cleared the last 122 left the miss count untouched at 3133, with 122 shapes entering the miss set as 122 left it: == and -- lines had been refused for the wrong reason, and correcting the model makes them correctly not-a-list-opener, hence correctly credited-though-hidden. Every surviving miss is a declared shape: the delimiter follows text on its own line (a zero-gap marker, or an ordered marker other than 1 under an open paragraph whose later closer forms an inline comment), a lazy paragraph continuation, or an opener inside an already-open list item or block quote where the container shifts the column at which a block starts. Resolving the last two means growing a container parser. Those, and the wider channel this visibility model does not cover at all (raw HTML that is not a comment, such as a <script> or <template> block, whose contents no renderer displays), are pinned by tests and tracked in #613 rather than left implicit. A commented heading still delimits the span, agreeing with split_sections rather than second-guessing it: disagreeing cost four false aborts across review rounds and bought only a miss that credits the seat nothing, while agreeing stops a comment opened above the heading from laundering the fields below it, not even one repeating a title that exists structurally elsewhere in the card. Only a field-shaped heading inside the span counts — a ## Rationale: … section elsewhere in the card is an ordinary extra section the report grammar permits, not a dissent. The diagnostic's line count is taken on the raw span for the same reason: fenced placeholder prose is archived content and would otherwise be reported as zero. One consequence of the same rule closes a pre-existing hole: a canonical dissent plus a decorated second one had been slipping past the multi-dissent cardinality gate. Every other claimed-dissent abort is unchanged and separately pinned: a rationale: without its dimension_id:, a dimension_id: without its rationale:, duplicate ids, a claimed dissent placed after ## Dimension Scores, and duplicate ## Scoring Plan Dissent headings. The empty case short-circuits ahead of the section-order check, since an absent section has no position to violate. What the tolerance costs is stated rather than papered over: a seat that narrates a deviation in free prose without spelling any field is read as no dissent. Its Phase 1 trigger binding is still enforced in full — no dissent claimed means no per-dimension exemption, so a deviating trigger still fails at [TRIGGER-DRIFT] — but a deviation whose trigger text stays inside the Phase 1 commitment is recorded only as the advisory line plus the archived raw response. No deterministic rule closes that residual without re-breaking the observed case, which was itself free prose; the same deviation is equally invisible when a seat simply omits the section, so the tolerance does not open a channel that being silent did not already offer. Parser-side only: no reviewer prompt, contract, template, or fixture changed, and scoring, decision, severity, fatality, dissent-cardinality, and retry semantics are untouched, so the change cannot move a review-side metric. 205 new cases (178 → 383), including the verbatim 2026-07-27 placeholder-retraction shape, twenty-four decorated field-shape spellings, three sanitizer-laundered shapes and their multi-dissent variants, every other multi-dissent bypass, nine tolerated shapes carrying a colon in four languages plus field-less fences and comments, and a CLI pass asserting the diagnostic reaches stdout. The coupled Phase 1 canonical-line first-attempt miss rate stays open until #608 preserves the rejected responses needed to characterise it.

  • Reviewer E4 retry-evidence contract versioned without weakening older blocked-run safeguards (#574; PR #603). Only the 2026-07-27 requirement to preserve every checker-rejected response followed by a retry and its checker output has a prospective boundary; a disclosed transport/session re-dispatch that produced no response has no such artifact to preserve. Panel completeness, exclusion from means, no imputation, and no replacement draw that conceals an abort apply regardless of date; the grandfathered set is closed to the already-committed 2026-07-24/25 scored records and their accepted final panels, while superseded artifacts stay frozen without a current-contract label, so no other historical root can be promoted. New records name reviewer-e4/2026-07-27; both blocked Spec-A records now use one machine-readable provenance schema, and r2 records both overwritten Phase 1 responses, making the clean cohort's provenance-invalid count 2/2. This corrects the earlier Unreleased Added summary's conformance-only description of r2. It also replaces r2's previously committed paraphrased methodology diagnostic with the dispatch log's path-stripped verbatim checker output and records that correction in the run notes. The Unreleased 0.663 → 0.536 gate claim remains governed by the pre-2026-07-27 evidence contract and does not attest that rejected retry responses can be re-adjudicated; the README records the non-gating sensitivity of all three affected baseline panels.

  • Sprint DA table parsing now has one terminal-suffix invariant exposed by formal E4 (#574; PR #598). The fresh post-#597 clean-control cohort reached both DA Phase 2 calls, but both cards placed ordinary Review Body prose after the #### MAJOR table. The shared parser skipped the separating blank line and misread that prose as another table data row, so both panels failed conformance before synthesis (2/2 aborts, 100% versus the expected near-zero operational monitor); no Phase 2 output was retried, no replacement replicate was drawn, and the quant/qual fixtures were not started. The delivered DA prompt and protocol now require every prose paragraph before #### CRITICAL; the exact CRITICAL and MAJOR tables form the terminal suffix of ## Review Body, with only blank lines between/after them, and DA reports contain no HTML comments. The shared conformance/synthesis helper enforces that single structural invariant, rejects hidden or late payload without enumerating Markdown container shapes, preserves exact missing-heading diagnostics, and keeps malformed contiguous rows fail-closed. Raw HTML-comment openers are checked before fence normalization, fenced blocks remain opaque nonblank sentinels for DA structure, and a bare --> remains valid report text rather than being misclassified as a comment. Positive pre-table prose plus symmetric CRITICAL/MAJOR-tail, outer-pipe, pipe-less, list, blockquote, heading, multiline, shadow-table, fenced-payload, and HTML-comment regressions cover both checker paths. The failed root remains frozen and E4 restarts from a new root after this corrective merge.

  • Sprint reviewer retry diagnostics and text: anchor preflight now match the deterministic gates (#574). The first fresh E4 root after #592 stopped the fleet at the clean-control pair: one methodology Phase 1 plan still omitted a mandatory fatal trigger after its single allowed retry, because the checker diagnostic named the missing field but not dimension D3; the other panel reached its fifth Phase 2 seat but the DA emitted a text: anchor with no quoted excerpt, which the shared parser correctly rejected. Both panels aborted before synthesis, no malformed Phase 2 output was retried, quant/qual fixtures were not started, and the root remains frozen. Phase 1 field-cardinality diagnostics now name the exact dimension, and duplicate-subsection diagnostics preserve the sorted duplicate headings, so the one retry receives the protocol's required specific lint gap across that grammar family. All five delivered Phase 2 prompts, the sprint protocol, and the report template now state that every text: anchor contains at least one balanced quoted verbatim excerpt as well as enforcing the existing 25-word per-excerpt ceiling; lockstep lint mutations pin both detailed and terminal wording. Runtime acceptance, anchor parsing, scoring, decision, fatality, and retry semantics are unchanged; formal E4 restarts from a new root after this corrective merge.

  • Sprint reviewer terminal preflight now closes the whole structural family instead of one observed token form (#574; PR #588). The next fresh E4 root confirmed that the empty-dissent placeholder fixed by #591 no longer recurred, but stopped again before a complete panel: one emitted EIC card paraphrased its D6 block trigger instead of copying a Phase 1 substring and failed [TRIGGER-DRIFT]; the other EIC paper-visible call exited with no output, while one paper-blind plan still needed the allowed retry after emitting a forbidden D5 fatal sentinel. No malformed Phase 2 card was retried and the root remains frozen. The five delivered Phase 1 prompts now end with a complete structural audit over required section order and paraphrase cardinality, exact headings and field multiplicity, fatal-key cardinality, forbidden paper-visible output, acknowledgement placement, and manuscript blindness. The five delivered Phase 2 prompts now end with one closed-family audit over required/retired sections, dissent cardinality, role/dimension and abstention shape, character-for-character trigger binding, fatal/block-class scope, scoring-seat findings and receipts, exact DA table grammar and dense IDs, typed anchors, and B1 bands; the protocol mirrors both and lint relocation/mutation checks pin terminal delivery. This changes no runtime checker, scoring, decision, severity, dissent, fatality, or retry semantics; E4 restarts from a new root after merge.

  • Sprint reviewer terminal preflights now make omission rules executable at generation time (#574). The first fresh formal E4 root after #590 aborted both clean-control panels before synthesis: two independent paper-visible cards emitted an empty ## Scoring Plan Dissent heading with an omitted placeholder despite the delivered omission rule, and one paper-blind EIC plan first emitted a forbidden non-mandatory fatal sentinel before the one allowed Phase 1 retry corrected it. The deterministic gates rejected both malformed Phase 2 cards, no Phase 2 call was retried, and the failed root remains frozen as operational evidence. All five delivered Phase 1 system prompts now require a terminal mandatory/non-mandatory fatal-key count, while all five delivered Phase 2 prompts require a terminal heading/content check that deletes empty dissent placeholders; the sprint protocol mirrors both and role-scoped lint mutations pin every delivered surface. No checker, scoring, decision, severity, dissent, or fatality semantic changes; formal E4 restarts from another fresh root after this corrective merge.

  • Sprint reviewer live grammar instructions now close the E4 delivery gaps exposed after #589 (#574). The first post-merge isolated Opus 5 pre-measurement attempts aborted five of seven panels: one Phase 1 reviewer exhausted its retry after emitting a forbidden what_triggers_fatal: NOT_APPLICABLE line on a non-mandatory dimension, while four paper-visible cards independently failed on a duplicate report-level contract_role, an empty ## Scoring Plan Dissent section, Severity: Strength under strength headings, or an over-25-word text: anchor excerpt. The deterministic checkers rejected every malformed card as designed, and no Phase 2 output was retried. All five delivered Phase 1/Phase 2 system-prompt subsections now spell out the corresponding omission/placement rules, exact contract dimension names, weakness-only Severity, and the pre-output quote-length check; the sprint protocol and report template mirror them, while role-scoped lint witnesses and mutation tests pin the lockstep surfaces. This clarification changes no checker, decision, scoring, severity, or anchor semantics; E4 restarts from a fresh output root after the corrective merge.

  • Sprint reviewer typed-anchor grammar made self-contained at the delivered Phase 2 boundary (#574). E4 preflight exposed that the checker correctly required <type>: <locator>, while the isolated sprint call receives only each agent's ### Phase 2 — Paper-visible review subsection and that delivered text described wrapping variants without spelling out the colon grammar. All five delivered reviewer subsections now state the exact value grammar, demonstrate whole-value wrapping, reject both observed type-only wrapping forms, require matched and unpadded outer wrappers plus balanced straight/curly quote pairs, and define the executable absence: <where> — expected <item>; checked <surfaces> subgrammar with each reserved separator appearing exactly once. The peer-review template and sprint protocol mirror the rule; template replacement instructions sit on a separate non-value line, while the shared phase/synthesis validator uses total deterministic delimiter parsing and a type-aware quote stack so balanced nested quotations remain valid and rejects reversed or backtracking separators, padded or unbalanced wrapper debris, crossed quote pairs, and incomplete absence anchors without mistaking balanced locator content such as Eq. [3] or inline code for an outer wrapper. check_role_scoped_contract.py mutation-tests every seat, both mirrors, all three shipped examples, the template's ordered canonical text/table/absence examples, every nested quote pair, square-bracket ordering, exact wrapper assignments, and each executable parser witness.

  • Reviewer contradiction batch: C1/C2/C4/C6 + manuscript data-fencing + panel-reference no-invention rule + Top Blocking Issues section + overlap-prohibition removal (#574 PR 1). Seven prompt-level defects from the #574 audit, none changing the review's behavioral direction (the behavior batch — quota removal, typed anchors, severity transport — waits on the E4 baseline). (C1) eic_agent frontmatter claimed the EIC "delivers the final editorial decision" while the body forbids exactly that — the description now names the review-card role and the synthesizer's decision ownership. (C2) The "Recommendation to Peer Reviewers" output field is retired: reviewers run independently in parallel (Iron Rule #2), so no channel exists to deliver it — dead output at best, an independence leak at worst; the SKILL.md flow line "Sets the review tone" goes with it. (C4) The Roadmap's Priority-2 rule cited [CONSENSUS-2], a label the same file's taxonomy explicitly says does not exist — now "corroborated findings (agree = 2, conflict = 0)". (C6) The EIC's "first impression score (1-10)" fed no output field and is retired. (A6) All five reviewer agents now receive the manuscript wrapped in a <paper_content>...</paper_content> data delimiter with an explicit data-not-instructions rule (SKILL.md Iron Rule #7 operationalized at the call boundary; injection attempts are themselves a reportable weakness); sprint_contract_protocol.md §2 step 4 specifies the delivery side in lockstep, and the re-review protocol marks both the revised manuscript and the Response to Reviewers as untrusted author-authored inputs. (A5) Panel-suggested references gain the no-invention rule in domain_reviewer + perspective_reviewer: the v3.11 citation gate covers the author's citations, not the panel's suggestions, so recommendations must be attestable or carry [UNVERIFIED] as search leads; the perspective reviewer's "3-5 references" quota is removed (zero allowed). (E7) The Editorial Decision Letter gains a mandatory "Top Blocking Issues (03, ranked)" section — each row with source reviewer(s), evidence anchor, and resolving roadmap item; zero rows is valid for a genuine Accept (template + synthesizer letter format in lockstep, replacing the synthesizer's unanchored "Summary of Key Issues"). (P0-3) The DA's "No repeating other reviewers" discipline — unexecutable under Iron Rule #2 blindness and a suppression incentive against legitimate corroboration — is reframed: report what you find, deduplication is synthesis work.

  • Re-review plumbing: yardstick continuity + Stage 4→3' handoff completeness + apply-report output binding (#574/#576 pre-work). Three bug-class gaps surfaced by the 2026-07-24 reviewer-stage audit, fixed ahead of the #576 contract design because each is wrong today independent of it. (1) Yardstick continuity: re-review re-ran field_analyst_agent over the revised manuscript (academic-paper-reviewer/SKILL.md mode table), regenerating the reviewer configuration mid-loop so Round-2 verdicts could be judged against a different yardstick than the one that produced the Roadmap being verified. Re-review now reuses the Round-1 Reviewer Configuration Cards (new § Yardstick Continuity in re_review_mode_protocol.md; target-venue freeze included); the standalone-invocation fallback regenerates visibly with a [YARDSTICK-REGENERATED: <original|revised> manuscript — <reason>] marker recorded on a new Judge Record "Reviewer configuration" line — advisory, never a block. (2) Handoff completeness: the orchestrator's Stage 4 → 3' transfer row omitted the Revision Roadmap and the #390 apply report even though both are declared required re-review inputs (revision_patch_protocol.md, academic-paper/SKILL.md); the row now carries both plus the Round-1 configuration cards, and the re-review protocol's own input list gains the same two entries. The #528 orchestrator content lock was re-pinned per its documented procedure. (3) Apply-report output binding: the apply report recorded base_draft_hash but no hash of the revised draft it describes, so a post-apply rewrite (finalizer pass, manual edit) could silently invalidate the untouched-block evidence while the report stayed paired with the draft. Report format 1.1 adds output_draft_hash (same 12-hex format, hashed over the exact written bytes); revision_patch_protocol.md and the re-review protocol now instruct consumers to check it before relying on the report, and the Stage 4→3' row names the check. 3 new tests (TestReportOutputHash) pin the binding; check_390 / check_268 / boundary-semantics / spec-consistency lints all green. The 8-round cross-model review trajectory (13 P1 + 8 P2 closed, final round 0 findings; /security-review 0 findings) drove the fixes across every operative mirror: all six Stage 4→3' authority surfaces carry the new inputs in lockstep (SKILL.md, state machine transition + artifact lineage, team collaboration, state tracker, reviewer SKILL.md, two-stage protocol), each gated on the re-review-default vs user-requested fresh-full-review split (mid-entry quick→full path) so the legitimate no-Roadmap path is never rejected; shared/model_tiering.md's Stage 3' caching roster no longer re-dispatches field_analyst; Schema 6 judge_record gains the optional reviewer_configuration member (round1_cards_reused / verbatim [YARDSTICK-REGENERATED ...]); the #390 spec records format 1.1 in a §0.1 amendment; and the three registered examples execute the new contract (EIC-only verification, reused cards, apply-report retention).

[3.19.0] - 2026-07-22 — Revision-round claim-drift guards, PDF read-integrity preflight, read-scope attestation

Added

  • PDF read-integrity preflight for locally-extracted page anchors (#512). Closes the local-extraction-channel gap between the v3.7.3 locator-presence rules and the #182 existence gate: PDF readers silently truncate documents with malformed cross-reference tables, so a real, correctly-cited source could acquire an apparently valid page anchor from a truncated or mispaginated read and pass every existing gate. New scripts/pdf_read_preflight.py (pypdf-backed with the verify_submission_package.py ImportError-degradation precedent; not a "grep the first /Count" check — xref streams, /Prev chains, and object streams ride pypdf's machinery) compares three independent page-count signals — the raw root page-tree /Count, the script's own cycle-guarded /Kids-walk leaf count, and pypdf's flattened page list — and emits a JSON sidecar (pdf_read_preflight/1: verdict + file sha256 + the three counts + captured parser-repair warnings). PASS requires all three to agree with zero parser warnings; count disagreement is FAIL (the truncation/mispagination signal itself); everything the preflight cannot vouch for — encryption, cycles, repair chatter even with agreeing counts, missing pypdf — is UNAVAILABLE. Enforcement sits upstream of the writers per the issue: the three v3.7.3 emitters gain firm rule R-L3-1-D (a locally-read PDF's page anchor requires a PASS sidecar in context; otherwise anchor:none or an independently-visible locator + explicit warning), claim_ref_alignment_audit_agent Step 4 gains the precondition bound to the existing ref_retrieval_method == manual_pdf discriminator (sidecars join on ref_slug; the sha256 is confirmatory until #513 supplies an anchor-side hash field) with the [pdf_read_integrity_unverified] advisory rationale tag (never an UNSUPPORTED verdict on this basis alone), and the §3.6 orchestrator — the layer that CAN run Bash — runs the preflight once per locally-read corpus PDF and passes sidecars into audit and drafting context. Cross-model review rounds 1-8 (3 P1 + 2 P2, then 7, 2, 1, 2, 1, 1, 1 P1, all closed): rounds 6-8 finished the header scan's lexer fidelity (ten-digit object numbers; %-comment token separators; signed and zero-padded integer tokens — the scan now implements the full ISO 32000 separator AND numeric-token model, closing the lexical-obfuscation family structurally); round 5 extended the header scan's boundary/separator class to full ISO 32000 whitespace (NUL-preceded replacement headers are seen) and added the compressed-object variant (a direct raw replacement of an object whose active copy lives inside an object stream, appended after its container with a stale startxref, now vetoes PASS; a raw copy before the container is the legitimate superseded-into-objstm case and stays clean); round 4 fixed the object-header scan for ISO-valid bare-CR line endings (Python's multiline anchor does not treat CR as a line start, which blinded both xref-coverage checks on CR-only files); round 3 hardened the stale-startxref check against redefined-object-number variants (the newest raw copy of every directly-stored object must be the copy the active xref chain references, with a calibration guard for offset-shifted files) and switched the trailing-data predicate to ISO 32000 §7.2.2 PDF whitespace (NUL padding passes, vertical tab is data). Round 2 added the stale-startxref xref-coverage cross-check (raw object headers absent from the active xref chain veto PASS), plus a strict integer-object requirement on /Count (float/string coercion rejected), the FAIL-vs-UNAVAILABLE split in R-L3-1-D (positive truncation evidence refuses the page anchor; mere absence of verification — standalone dispatch, no-Python installs, unpreflighted files — is an explicit-warning advisory, never a manufactured refusal), the pdf_preflight_verdict key on citations handed to retrieve_fn (content-based passage selection BEFORE the judge reads a page-scoped passage), the orchestrator sha256 freshness re-check + cross-runtime coverage note, and the tagged-SUPPORTED row in the operational finalizer matrix. Round 1: trailing-data-after-final-%%EOF veto (a truncated incremental update otherwise PASSes on the older revision's agreeing counts); preflight moved to Stage 1 corpus intake, independent of the opt-in audit mode (an audit-gated preflight left default-mode runs sidecar-less at R-L3-1-D, gate-refusing valid citations); the executable-path enforcement — run_audit_pipeline(pdf_preflight_sidecars=...) tags rows at the Step-6 emission point after cache resolution so cache hits cannot bypass, and the finalizer surfaces [LOW-WARN-PDF-READ-INTEGRITY-UNVERIFIED] on SUPPORTED rows so content-based-fallback support does not render the advisory invisible; parser warnings survive early exits (appended in the capture handler's finally). 18-test synthetic-PDF suite + 8 pipeline/3 finalizer tests (no binary fixtures); agents/ mirrors re-synced; the #528 orchestrator content lock re-pinned per its documented procedure. Provenance: mechanism observed in kengo006/alexandria; ranked P1 of three in the 2026-07-11 dual-track adoption review. Spec: docs/design/2026-07-20-512-pdf-read-preflight-spec.md.

  • read_scope honest-coverage attestation on the human-read ledger + anchor-aware finalizer promotion (#513). ARS recorded source possession, AI verification, and a binary human-read mark — but nothing recorded HOW MUCH of a source was read: a TOC-only reading promoted a citation whose page anchor pointed at a chapter the user never opened. /ars-mark-read gains an optional declaration-only attestation — --scope {full_text,sections,abstract_only,toc_only,unknown} + repeatable --locator (requires sections; contradictory attestations are refused, not recorded) + --note (requires --scope) — written as an optional read_scope object on the ledger entry; absent means unknown, never fabricated or backfilled, and attestation args are rejected with --unmark. Placement honors the v3.6.8 firm rule the dual-track review verified: corpus entries are adapter-owned and never carry human-read state, so the attestation lives on the user-owned <passport-stem>_human_read_log.yaml, now covered by a sidecar schema (shared/contracts/passport/human_read_log.schema.json, version_records precedent, registered in the contracts README; test-time validation only — the CLI stays dependency-light). The consumer ships in the same PR (an attestation with no consumer is dead metadata): the Cite-Time Provenance Finalizer's LOW-WARN → ok promotion becomes read-scope-aware — absent/unknown/full_text promote (legacy marks impose no migration); abstract_only/toc_only resolve to the new draft-visible acknowledged-partial status LOW-WARN-PARTIAL-COVERAGE with an explicit coverage note; sections promotes only when a page/section/paragraph anchor falls unambiguously within a declared locator, and quote anchors promote only under full coverage. The governing attestation rides the settled latest-timestamped-event-wins precedence (a latest rescind always wins), the idempotency evidence enumeration names the attestation explicitly, and LOW-WARN-PARTIAL-COVERAGE keeps LOW-WARN's severity everywhere (contamination suffixes attach identically; the formatter passes it as an acknowledged variant with the note surfaced — distinguishable from an unacknowledged LOW-WARN at the terminal gate, never refused). CLI presence checks use is not None (an explicit empty string is invalid, not absent) and locator/note length bounds are enforced at write time in lockstep with the schema, so the CLI can never produce a ledger the schema rejects; two cross-model review rounds (5 P1, then 3 P1, all closed) drove the hardening: round 2 added LOW-WARN-PARTIAL-COVERAGE to every base-status enumeration (scripts/check_v3_10_policy.py _BASE_STATUS, the hard-gate handoff, the contamination/policy-stamp sections — partial markers get suffixes and policy_hash stamps like any LOW-WARN and are never refused by the freshness gate), put the citation's own anchor into the idempotency/revision-loop evidence enumeration (a revision moving an anchor out of declared coverage re-resolves the marker even with corpus and ledger unchanged), and mirrored the locators-require-sections rule as an if/then conditional in the sidecar schema so audit-time validation matches the writer contract. commands/ars-mark-read.md + formatter LOW-WARN remediation updated in lockstep; #528 orchestrator content lock re-pinned. Provenance: mechanism observed in kengo006/alexandria; ranked P2 of three in the 2026-07-11 dual-track adoption review. Spec: docs/design/2026-07-20-513-read-scope-attestation-spec.md.

  • Revision-round claim-drift guards: claim-strength ladder + deterministic token conservation (#569 / #570). Stands an advisory watch over the epistemic and token halves of the #390 honest-claim residual — the patch mechanism confines silent-distortion exposure to touched blocks but never checks a touched block's interior (DELEGATE-52, arXiv:2604.15597, the same round-trip subtle-modification finding #390 already anchors); a guard now watches each half, and whether the guards reduce the measured drift is a separate re-measurement (#652), not a claim this entry makes. Two complementary layers, both advisory-first, mechanism shape borrowed from Yila-AI/sci-ssci-skills by @MissOrangePeel (its sci-ssci-polishing claim-strength ladder + check_invariants.py). (1) Claim-strength ladder (shared/references/claim_strength_ladder.md): an ordered epistemic scale (is consistent with < is associated with < predicts < contributes to < affects/leads to < causes) whose invariant is "no silent move, either direction, without an authorizing roadmap item" — wired into draft_writer_agent revision mode (a patch op changing a claim's rung must name the item that authorizes the strength change, not merely an item touching the block) and into a new advisory Phase E6 in claim_verification_protocol.md (revision-round-only STRENGTH-DRIFTEDADV-E6-<n> rows, mirroring the ADV-E4/E5 pattern, never gating). (2) Deterministic token conservation (scripts/check_revision_token_conservation.py): a stdlib checker over numeric tokens, <!--ref:-->/<!--anchor:--> markers, bracketed/author-year citations, and protected terms, in pair mode or per-op against a #390 patch, with Unicode fold-before-split (the #524 ordering lesson) and per-op roadmap_item_ids attribution; framed necessary-but-not-sufficient, mirroring the v3.11 deterministic-gate/LLM-semantic split. Rather than cite an earlier-generation-model study as motivation (issue #569 precondition), a held-out set (evals/heldout/revision_claim_drift/, codex-generated scenarios, cross-model + maintainer-adjudicated judging) measured the CURRENT frontier model's baseline first: 2/8 = 25% claim-strength/hedge drift under hedge-drop and null-reframe pressure, 0/8 unauthorized numeric/citation drift (the one authorized change correctly isolated), 2/2 controls clean — the evidence that the ladder earns its place rather than closing as documented-negative-scope. 31 mutation tests, registered in the CI pytest manifest. Dual-track pre-ship review (/security-review + /codex review xhigh): security 0 findings (read-only stdlib lint, empty attack surface); codex closed 6 (4 P1 + 2 P2) — the checker's number regex now keeps signs inside brackets/after commas and scientific exponents (6.02e236.02e24 was a false-negative), narrative author-year citations now carry the author (Smith (2020)Jones (2020) on the same ref slug was a false-negative), E6 was rewired to consume the durable per-round revision-patch sidecars via a declared Revision-Evidence Bundle (its original inputs were never transferred across the Stage 4→4.5 handoff) and to audit every round on a multi-round path (not only the last pair), step 3a's attribution semantics corrected (the checker emits the deterministic signal; E6 supplies the authorization verdict), and E6/ADV-REV report tables + checkpoint options defined.

Fixed

  • SETUP Method 4a description-length figure de-drifted (#564). docs/SETUP.md / docs/SETUP.zh-TW.md stated the four skill description fields "currently sit in the 440-842 range"; the actual lengths have since grown to 566-986 characters. Replaced the hardcoded range with the durable comparative statement (each exceeds claude.ai's 200-character upload cap while staying under Claude Code's 1,024-character allowance), so the sentence cannot silently drift again as descriptions evolve. Rationale for not trimming the descriptions themselves is unchanged.
  • Write-scope guard launcher: watchdog-path stall removed; #545 flaky launcher tests fixed (#545). Two-layer root cause. (1) Production: on hosts without a timeout binary (stock macOS), hooks/run_guard.sh's watchdog subshell — and the sleep it forks — inherited the caller's $(...) capture pipe; after the parent killed the subshell, the orphaned sleep kept the write end open, so every healthy probe/guard run blocked for the full wall-clock bound (~2× bound per PreToolUse call; measured 6.1 s at the default bound). The watchdog now redirects to /dev/null, cutting the healthy watchdog path to ~0.15 s with decisions unchanged. (2) Tests: the harness pinned ARS_PROBE_BOUND=1, leaving so little margin that loaded machines pushed real interpreter + guard runs past the bound into the launcher's designed pass-through degradation — which every deny-expecting test read as a rotating, load-dependent failure. The harness default is now a generous 30 s (pure margin; only the hanging-candidate tests wait a bound out, and they override to 5 s), and a new LauncherSlowInterpreterTest pins the regression (a python3 that takes ~1.5 s per invocation must still forward the real deny). Suite: 21 tests / 70-80 s → 22 tests / ~23 s, green under 8-way CPU load.

[3.18.0] - 2026-07-18 — Self-improvement survey integration: advisory quality layers, risk-stratified claim gate, cross-model reviewer & judge tracks, cache re-validation

Added

  • Re-review judge independence + judge record (#539). The Stage 3' re-review judges revisions on the same model family that drove them — an analogous correlated-judge configuration to the one §8.1.2 warns about. When cross-model verification is active (configured + consented), every Priority 1 roadmap item's addressed-ness verdict gets an independent judgment-specific cross-model pass (provider transport reused, no grounding gate, closed verdict set incl. MADE_WORSE, non-conforming → unavailable); results land in the R&R matrix's Cross-model column (agree / diverges: <verdict> / unavailable / not_configured) — diverges is a Phase 2 synthesis review trigger, never a vote. Single-family runs carry a never-omitted verbatim disclosure line. Both cases emit a Judge Record (verification judge; Round-1 panel provenance copied seat-level from the #540 provenance block — carried into Stage 3' via the Editorial Decision Letter, now named on all handoff surfaces incl. the re-hashed state-machine lock; prompt/rubric surfaces; evidence seen; judging budget separate from generation) — Schema 6 optional judge_record, Schema 11 optional Priority-1 row fields. External motivation: Ren et al. (2026, arXiv:2607.13104 §8.1.2).
  • Cross-model reviewer track (#540). With ARS_CROSS_MODEL configured, ONE existing peer-reviewer slot (Reviewer 2) runs on the cross-model family — a substrate swap inside the fixed five-seat panel, explicitly NOT the retired 6th-reviewer design (whose five counterproductive conditions all attach to an added seat). Transport follows #523 dispatching-layer ownership with a two-call sprint-contract split (paper-blind Phase 1, then Phase 2 with the paper); the slot's vote enters the panel matrix unchanged; the synthesizer computes NO cross-family aggregate (forbidden-operations list holds) — splits are visible by inspection in the matrix, and the provenance block names which seat ran on which family. Activation requires the explicit cross-model consent, not the env var alone. Without a second provider, the new Editorial Decision Letter Review Panel Provenance block discloses the single-family correlated-error caveat instead of silently implying independence; dispatch failure falls back with the fallback disclosed. Authority: shared/cross_model_verification.md § Cross-Model Reviewer Track. External motivation: Ren et al. (2026, arXiv:2607.13104 §5.2) — repeated sampling of one model may amplify a confidently-wrong conclusion; heterogeneous critique models are among the named safeguards.
  • Cache-source staleness advisory + opt-in live re-validation (#541). The citation-verification cache (v3.11 Delta 2, 90-day TTL) gains an age-based advisory layer: VerificationCache.entry_age_days / stale_report compute the oldest-live-row age per citation, and Phase A § A0.5 emits ADV-CACHE-<n> advisory rows (citation, age, threshold) for cache-served verifications older than ARS_CACHE_STALE_ADVISORY_DAYS (default 30; 0 disables) — displayed with per-row options at the MANDATORY checkpoint via the #547 advisory-row template, never gating. The wiring is real code, not prose: verify_citation/verify_passport run cache-through by default (closing the #182 Delta-2 forward-decl — the four resolvers route via the detailed contamination-signals wrappers with interoperable cache keys), the CLI gains --no-cache, and ARS_CACHE_REVALIDATE=1 re-verifies stale rows live at the gate (per-row bypass + re-population; cost scales with stale-row count). Invalidation cascade is unconditional: after any re-validation the citation's summary row regenerates and Phase E verdicts re-run at the gate (no baseline exists to diff against). Cache robustness hardened: naive timestamps read as UTC, malformed rows are misses, clock-skew clamps to 0, and the advisory flag is computed from the emitted rounded value. Summary schema gains optional cache_age_days + cache_stale_advisory; cache/gate/CLI suites extended (52 gate+cache tests green). Framing: Ren et al. (2026, arXiv:2607.13104 §6.2.3) names scheduled review-and-attenuation and the staleness failure mode; applying it to the citation cache is ARS's design inference.
  • Risk-stratified Stage 2.5 claim verification (#549). Phase E Mode 1 sampling moves from a uniform 30% random spot-check to the #518 tier logic at claim level: HIGH-IMPACT claims (headline / numerical / causal / methods-critical / disputed — same definition family as shared/cross_model_verification.md step 2) verified 100% uncapped, plus a 10% RANDOM sentinel of the remainder (min 3 / max 10) for unbiased drift detection, with a top-up floor preserving the pre-#549 minimum of 10. Each audited claim's tier is recorded in the Claim Registry so coverage is inspectable. Mode 2 (Stage 4.5, 100%) unchanged — the delta is WHEN consequential distortions surface: before the review stage instead of at the final-gate backstop, cutting late-rework loops. Authority claim_verification_protocol.md § Sampling Strategy; mirrored in integrity_verification_agent + integrity_review_protocol.md. External motivation: Ren et al. (2026, arXiv:2607.13104) §3.3 active-acquisition targeting of failure modes/verifier disagreement + §9.2 resource-aware improvement; the high-impact-first allocation is ARS's design inference from those principles.
  • Search-bounded novelty claims (#548). Absolute priority language ("the first study to...", "no prior work has...") asserts the absence of literature — no cited source can support it, so the citation gate structurally cannot catch inflation there. draft_writer_agent gains a ## Search-Bounded Novelty Claims (#548) section: default emission is the bounded form ("To our knowledge, based on searches of [databases] through [date]...") filled from the Schema 2 search_strategy actually used, nearest prior work named instead of claiming a vacuum, the bounding qualifier marked as a protected hedge (so abstract compression cannot silently restore the absolute claim), and the absolute form emitted only on explicit user confirmation (recorded for AI disclosure). Phase E gains an E5 novelty-claim classification (authority: claim_verification_protocol.md § E5; mirrored in integrity_verification_agent + integrity_review_protocol.md): primacy assertions classify as SUPPORTED_WITHIN_SEARCH or UNRESOLVED against the documented search — never "globally verified" — advisory-only, never gating, per-row user decision. failure_paths.md's Insufficient Novelty guidance stops advising bare "strengthen the novelty claim" and routes through the bounded form. External motivation: Ren et al. (2026, arXiv:2607.13104 §7.4) — discovery agents cannot easily verify novelty on their own and may exploit weak proxies.
  • Pipeline behavior robustness held-out set, seed v0.1 (#550). New evals/heldout/pipeline_behavior_robustness/ — metamorphic paired cases for the runtime routing/gate layer: 8 documented-ground-truth base scenarios (explicit-trigger routing, cross-phase clarify obligation, rebuttal-audit vs revision-coach input-shape gate, rejected-mechanism refusals, Stage 4.5 IRON-RULE hold under deadline pressure) × 3 perturbation axes (terse / verbose / long-context padding), en + zh-TW pairs, invariant = same expected behavior per pair. Lives under heldout/ (LLM-judged, no script entrypoint) with per-item rule_anchor citations so ground truth stays reproducible from the shipped rules; paired-consistency + long-context-degradation metrics and a fresh-session measurement protocol mirror the #505 runs. v0.2 forward note: wrapper/reorder axes + cross-model variant authoring before the set gates routing-layer changes. External motivation: Ren et al. (2026, arXiv:2607.13104 §8.2.1) — evaluate prompt-policy behavior under paraphrases, formatting shifts, and longer contexts.
  • Per-sub-question scope bindings + Phase E scope-conformance advisory (#547). RQ Brief Schema 1 (shared/handoff_schemas.md) gains an optional sub_question_bindings field — each sub-question records which parent-scope constraints (population / timeframe / geography / domain) it inherits and any user-approved deviations; absent = full-scope inheritance (pre-#547 briefs remain valid inputs; the E4 advisory then compares against the whole scope object — the advisory itself is new behavior). research_question_agent emits the bindings and never silently broadens; structure_architect_agent outline sections name the sub-question they serve and inherit its bindings. Phase E gains an E4 scope-conformance advisory (authority: claim_verification_protocol.md § E4; mirrored in integrity_verification_agent + integrity_review_protocol.md): claims whose stated scope exceeds their inherited scope surface as SCOPE-BROADENED rows at the Stage 2.5/4.5 checkpoints — advisory-only, never gating, per-row user decision (proceed open, or accept with justification; a requested reword is an ordinary revision instruction in the normal flow), no automatic rewriting. Closes the silent-broadening path from a narrow RQ to conclusions the evidence never covered. External motivation: Ren et al. (2026, arXiv:2607.13104 §5.1) — decomposition becomes vulnerable when sub-problems stop preserving the original task's constraints.
  • SessionStart update-available reminder for plugin installs (#543 → #544). New scripts/ars_update_check.sh (Bash 3.2, no jq, always-exit-0 fail-silent) compares the installed plugin version against main's .claude-plugin/plugin.json — 24 h cache at ~/.cache/ars/update-check, 3 s network ceiling, ARS_UPDATE_CHECK=0 kill switch — and the SessionStart announce prepends a one-line reminder pointing at /plugin update academic-research-skills when behind. Motivated by #543 (reporter six minor versions behind on a long-fixed bug): third-party marketplaces default auto-update OFF and surface no behind-signal. Hermetic 15-test suite (scripts/test_ars_update_check.py, manifest id 544-update-reminder). Spec: docs/design/2026-07-18-544-update-reminder-spec.md.
  • README motivation + calibration docs: self-improvement survey anchor (#542). The README / README.zh-TW.md "Why human-in-the-loop, not full automation?" sections gain Ren et al. (2026, arXiv:2607.13104, Self-Improvements in Modern Agentic Systems: A Survey) as a third, survey-level literature anchor — §7.4 scientific-verification limits, §5.1§5.2 human-auditing safeguards, §2.2 EURISKO user-as-external-evaluation-signal precedent — framed explicitly as design rationale, not empirical proof. academic-paper-reviewer/references/calibration_mode_protocol.md § Why this mode exists cites the survey's §8.1.2 evaluator-independence + calibration-against-a-verifiable-subset safeguards as the external statement of what calibration mode instantiates. The survey's actionable deltas stay tracked as issues (#539#541, #547#550). External motivation: Ren et al. arXiv:2607.13104 (2026-07).

[3.17.0] - 2026-07-16 — Pipeline boundary semantics, canonical cross-model handoff envelope, executable panel checker

Security

  • Tools allowlist for the three top-level plugin agents (#514, implemented in PR #521 by @madtriceps). The three plugin-exposed agents (synthesis_agent, research_architect_agent, report_compiler_agent; deep-research sources + byte-identical agents/ mirrors, six files) now declare tools: Read, Write, Edit, Grep, Glob in frontmatter — no Bash, no WebFetch/WebSearch — so dispatch-time capability is least-privilege even in hook-less installs, complementing the runtime Bucket A Bash deny (scripts/ars_write_scope_guard.py), which keys on agent name and continues unchanged. Retrospective entry: the code merged just after the v3.16.0 tag; documented here per the changelog-covers-merges gate.

Fixed

  • Blind-checkpoint transport moved to the dispatching layer (#523). The #518 blind disagreement checkpoints told their Bucket A primary owners (research_architect_agent, editorial_synthesizer_agent) to execute the cross-model curl transport themselves — unexecutable under the runtime Bash deny and, for the architect, the #514 dispatch-time allowlist, so on every hook-active run the check at an irreversible decision silently degraded to single-model, indistinguishable from a transient API outage. New Transport ownership (#523) contract in shared/cross_model_verification.md § Blind Disagreement Checkpoints: the owner commits its structured decision and emits the sanitized cross-model input as a handoff artifact; the dispatching layer (the main session running the skill, or pipeline_orchestrator_agent in pipeline Mode A — neither is Bucket A) executes § API Call Patterns, applies the mechanical enum comparison, and re-invokes the owner only for the divergence rebuttal; the editorial checkpoint's dispatched shape is an explicit, justified exception to the before-the-roadmap ordering (safe because the sprint-contract boundary keeps cross-model drivers out of the roadmap). The rule generalizes to any Bucket A cross-model owner — devils_advocate_reviewer_agent's independent DA critique routes the same way, with every successful response returned to the owner (no mechanical comparison exists for the dispatcher to resolve); non-fenced owners (integrity_verification_agent at the Stage 2.5/4.5 gates, deep-research devils_advocate_agent, the main session) execute directly, unchanged. No Bash/WebFetch re-added to any fenced agent (resolution (a); (c) rejected). Converged 0 P1/P2 across first-party security review + two codex gpt-5.6-sol xhigh rounds.

  • Pipeline prompt-surface contradictions from the #528 Mode-A replay (#529). The two genuine contradictions of the four ambiguities the replay surfaced: (1) the Methodology Blueprint was listed as a Stage 1 deliverable and in the Material Dependency Matrix but omitted from all three Stage 1→2 handoff surfaces — added to academic-pipeline/SKILL.md, references/pipeline_state_machine.md, and agents/pipeline_orchestrator_agent.md; (2) the post-review coaching trigger read "After Stage 3 or Stage 3' completion, Decision = Minor/Major", but routing sends a Stage 3' Minor directly to Stage 4.5 — the trigger is now split by stage (Stage 3 = Minor/Major; Stage 3' = Major only) and the Coaching Rules exclusion list extended to match. Text-only. Retrospective entry: the code merged before this entry was written; documented here per the changelog-covers-merges gate.

  • Stage 5 / Stage 6 boundary semantics — the two under-specified boundaries from the #528 Mode-A replay (#528). New authority section references/pipeline_state_machine.md § Stage 5 and Stage 6 Boundary Semantics, mirrored in academic-pipeline/SKILL.md and agents/pipeline_orchestrator_agent.md + references/process_summary_protocol.md. Stage 5: "Before finalization: always MANDATORY" now names exactly one checkpoint — the entry gate between Stage 4.5 PASS and the Stage 5 dispatch, carrying the format decisions; the in-stage content confirmation before the final PDF is Stage 5 execution (not a pipeline checkpoint), and the Stage 5 completion checkpoint (Final Paper delivered, before Stage 6) is FULL — never SLIM — but not MANDATORY. Stage 6: the state machine previously ended at Stage 5 → END with no Stage 6 at all; it now defines the Stage 5→6 transition, the decline path (Stage 6 is non-mandatory: declining marks it skipped and the pipeline still terminates completed), the terminal checkpoint after the Process Record is delivered, and the canonical terminal-acknowledgement vocabulary (finish / end / done / confirm, or an unambiguous natural-language equivalent) whose acceptance sets the pipeline global state to completed. All derived from existing text — no architecture change, no checkpoint relaxed. Thirteen codex gpt-5.6-sol xhigh review rounds drove the consequential closure across the wider surface set: the state_tracker_agent contract gains Stage 6 (stage_id enum, SSOT block, prerequisite rows, terminal/decline action pairs), the FULL checkpoint-type row stops claiming "before finalization" (it collided with the MANDATORY row), Stage 5 execution consumes the entry-gate citation-style decision instead of re-asking, Stage 6 joins the explicitly-skippable list (the skip validator would otherwise reject the pinned decline path), the engagement-tracking SLIM downgrade gains its FULL-checkpoint exception, and the whole-pipeline collaboration-observer pass is re-timed to Stage 6 record compilation (before delivery — "at pipeline completion" could not coexist with completion-after-acknowledgement). New scripts/check_pipeline_boundary_semantics.py defrift lock pins all four #528 resolutions across the five surfaces with 66 mutation tests (one adverse-value witness per closed gap), wired into spec-consistency.yml and the unified pytest manifest; because twelve rounds showed sentence-level pins alone cannot converge on prompt surfaces, all five files also carry bibliography_agent-style whole-file sha256 content locks — any byte change fails CI until the pinned hash is updated in the same commit. Closes #528.

Added

  • Canonical cross-model handoff envelope + dispatcher consumer contract (#527). The #523 owner→dispatcher→owner transport path was internally coherent but enforced by prose only — no canonical delimiter, no machine-stable schema, no malformed-result mapping, no pinned consumer trigger, so every test could stay green while a dispatcher silently treated a checkpoint owner's handoff as an ordinary deliverable. #527 closes that: one canonical [CROSS-MODEL-HANDOFF v1] envelope (checkpoint_kind / owner_agent / correlation_id / expected_result / owner_decision-outside-payload / payload) defined in shared/cross_model_verification.md § Cross-model handoff envelope, with scripts/cross_model_handoff.py as the NORMATIVE grammar (parse + outcome routing as pure functions) and a deterministic owner→dispatcher→owner fixture suite on a fake transport (scripts/test_cross_model_handoff.py — no external API, no manuscript upload; literal pins guard the module constants against self-referential testing). The three checkpoint owners (research_architect_agent, editorial_synthesizer_agent, devils_advocate_reviewer_agent) emit the envelope with their closed kind/result-shape pair; the Mode-A orchestrator pins the consumer contract (recognition as a transport request never a deliverable; malformed envelope/result → [CROSS-MODEL-ERROR: malformed_*] → outcome unavailable, never a fabricated judgment; agreement → mechanical fill with NO owner re-invocation; divergence → re-invoke the original owner with the minimum return context, the dispatcher never authors the rebuttal; DA full-return: every successful response goes back to the owner; ARS_CROSS_MODEL unset stays byte-equivalent). New scripts/check_cross_model_handoff_contract.py pins the contract across all five surfaces (including a prose-enums-follow-the-module invariant) with a per-branch mutation-witness suite; both suites wired into spec-consistency.yml + the unified pytest manifest. Closes #527.
  • Defrift lock for the #514 tools allowlist (#524). New scripts/check_tools_allowlist.py + a 74-test suite (a failing witness per invariant branch), wired into spec-consistency.yml and the unified pytest manifest. YAML is the authority, not a line scan: every semantic decision reads a duplicate-preserving node tree (yaml.compose, which keeps a shadowed duplicate key visible and resolves an alias into shared node identity), the frontmatter fence is a column-0 --- only (an indented --- inside a block scalar can't truncate the block and hide keys below it), and any frontmatter that will not compose to a mapping or uses a merge key (<<) / alias is a fail-closed error. Invariant 1 pins the tools: Read, Write, Edit, Grep, Glob value on all six #514 surfaces: the node tree must carry exactly one tools key whose value normalizes to exactly the canonical five, plus an additive byte-exact raw-line witness (CR-sensitive, so a symmetric LF→CRLF conversion is drift; fires when the verbatim pinned line is absent). This closes the drift scenario where a future PR edits a source+mirror pair symmetrically (re-adding Bash, dropping a tool, or typoing a name) and passes every CI gate green, because check_agents_mirror_sync.py pins only pairwise byte-equality and the runtime guard keys on agent name, never frontmatter; changing the allowlist now requires touching the lint's pinned value in the same commit (standard lock semantics). Invariant 2 reconciles the frontmatter channel against the runtime channel: any agent whose name is a Bucket A key in scripts/ars_phase_scope_manifest.json must not declare Bash in a tools: key in any YAML-legal form — comma string, quoted string, flow/block list, inline comment, Bash(...) permission specifier (BashOutput is a different tool and not flagged) — failing closed on a missing/non-mapping manifest, unparseable Bucket A frontmatter, or an unrecognized tools shape. Twelve rounds of codex gpt-5.6-sol xhigh review plus first-party self-probing drove the design from a line-scan first cut to a yaml.compose node-tree authority (the byte-exact witness anchored to the composed key's start_mark.index; frontmatter fences found with splitlines, which recognizes every YAML line break; the whole tools value folded through Cf-format-char stripping + NFKC BEFORE any split — so every compatibility separator becomes ASCII first: a fullwidth comma U+FF0C that split(",") would miss, and a fullwidth-paren specifier Bashgit:* U+FF08/U+FF09 that the ( split would miss, both reduce to their Bash base — so an invisible-character or homoglyph re-spelling of a tool name, or of a separator around it, cannot masquerade as a different token), closing a series of YAML-form fail-opens — quoted/flow/escaped Bash, escaped-key duplicates, merge-key injection (plain <<, chained <<: [*a, *b], alias-to-merged-mapping, a merge tag on a non-scalar ? !!merge [x] key, and a merge buried in a sequence), a #-in-quoted-key comment-strip bypass, an indented-fence truncation, !!binary/!!str tag tricks, a leading-BOM skip, parser-dependent duplicate name/tools keys, nested agent files missed by a non-recursive glob (now rglob), directory symlinks rglob does not descend (now fail closed), bare-CR (old-Mac) frontmatter read as absent, a RecursionError on pathologically deep nesting (now fails closed), and a zero-width/BOM/fullwidth re-spelling of Bash slipping the exact-string membership test (Bash, Bash, str.strip() leaves Cf format chars, so the token stayed distinct from Bash; now folded, with the byte-witness confirmed to still fire on an invisible-char canonical value), and the two ordering corollaries where a compatibility separator escaped an ASCII split and so Bash was never isolated as its own token — a fullwidth-paren permission specifier Bashgit:* (U+FF08/U+FF09) past the ( split, and a fullwidth comma ReadBash (U+FF0C) past the , split — both fixed by folding the whole value before any split — plus two byte-witness false-positives (a tools: line inside a description: block scalar, and Unicode line breaks NEL/LS/PS that YAML counts but split("\n") does not, both fixed by anchoring the witness to the composed key's byte offset); each closure carries a witness. The round-12 pass established the separator-class fold as complete: an exhaustive first-party scan of NFKC-stable alternate separators (ideographic/Arabic commas U+3001/U+060C, division/fraction slashes, semicolons) confirmed none can isolate a bare Bash token for any consumer — they do not fold to the ASCII ,/( the split (or a normalizing consumer) honors, so Read、Bash stays one non-Bash token everywhere — plus a scan confirming no codepoint NFKC-decomposes INTO ash and no non-Cf codepoint folds to empty (no token-merging attack); this boundary is pinned by a documenting non-bug test so the separator set is not later over-broadened into false positives. Plus the one-line allowlist mention the #521 review flagged as absent: docs/PERFORMANCE.md / docs/PERFORMANCE.zh-TW.md § plugin agents and the SessionStart announce script's plugin-agents line.
  • Machine-readable degradation registry + omission reason-provenance (#511 Part A). New shared/contracts/degradation_registry.json: a six-row auditable INDEX of every graceful-degradation mechanism (citation resolver outage → unreachable/unresolvable; contamination-signal API degradation → omit-field; VLM absence → skipped/PASS WITH NOTES; submission-package incompleteness → not_checked/exit 3; cross-model absence → warn-and-continue with required disclosure; non-SR compliance warn-cap — the one legitimate severity cap, because compliance already has a native block>warn>info scale). Each row records failure class → emitted degraded state → diagnostic marker → downstream consumer → terminal-policy effect → the per-mechanism authority as a verbatim CONTENT anchor (line numbers forbidden — the issue's own line refs had drifted by implementation time) → the tests/lints/schemas pinning the behavior. The registry indexes, it never re-authors: semantics stay in each authority file, and new scripts/check_degradation_registry.py (31 mutation tests, spec-consistency + pytest-manifest wired) fails CI when any anchor or pin stops resolving. No score caps — rows use native semantics per the issue's explicit rejection. Plus the one live weakness closed: literature_corpus_entry.schema.json gains optional contamination_signal_omissions (closed enum api_degraded only; derivable omissions — manual exemption, no-arxiv-id skip — deliberately not recordable), guarded by manual-entry forbid + per-key mutual exclusion with contamination_signals + arxiv-requires-id rules, and written on BOTH paths: bibliography_agent at ingest (sha256 baseline updated per the F2 procedure) and the backfill layer — new contamination_signals.build_signals_with_omissions() (manual checked upstream so a None from the resolver means exactly "degraded"; build_signals_object becomes an equivalence wrapper) + idempotent record/clear helpers, with both migration tools recording the omission once on a degraded lookup and clearing it when a later run computes the signal (recovery + idempotency + dry-run tests). A degraded lookup is no longer indistinguishable from "never computed". Advisory-only: R-L3-2-C k/k_max counting is unchanged. Design: docs/design/2026-07-15-511-degradation-registry-design.md. Closes #511.
  • Transport-fixture integration test for the citation gate (#511 Part B). New scripts/test_transport_fixture_citation_gate.py + checked-in redacted raw API bodies under scripts/fixtures/transport_bodies/ (success / miss / error per resolver, all metadata synthetic — 10.5555 example-prefix DOI, fictitious arXiv ID). A URL-dispatch fake at urllib.request.urlopen (any unrouted URL fails the test) feeds the bodies through the four ACTUAL client implementations (crossref_client / openalex_client / semantic_scholar_client / arxiv_client) into verification_gate.verify_citation, pinning the gate's 3-class verdict end-to-end: all-hit → true (ID fast path, one request per resolver), fabricated IDs (404 + real empty-result bodies through the title fallback) → false, total 5xx outage → unresolvable never verified. Closes the gap where the per-client suites pin one client at a time and the citation eval replays already-reduced resolver_outcomes authored from the same reducer rule — client parsing was never integration-tested. Wired into the unified pytest manifest. Deliberately NOT a product --offline mode and NOT a replication of the 51-case gold set (scoped out in #511 as inflation). Part A (degradation registry) remains open.
  • Executable sprint-contract panel checker (#510). New scripts/check_panel_synthesis.py recomputes both v3.6.2 decision layers from the primary artifacts — Layer 1: each reviewer's own scores → declared fired conditions → own ## Editorial Decision; Layer 2: the panel scoring matrix → quantifier thresholds → precedence → the synthesizer's declared fired_conditions: set AND emitted decision — and fails on mismatch (self-consistency gate on LLM output, not a correctness gate). Exit codes are classified by artifact source (1 synthesis-layer → retry synthesizer once; 2 contract/infra → abort; 3 reviewer-report → unusable reviewer ⇒ [PANEL-SHRUNK]; precedence 2>3>1), with a --layer1-only mode for per-reviewer verification at Phase-2 lint time. The §9 expression vocabulary is implemented as a closed grammar (unknown forms, orphan dimension literals, and empty priority scopes all fail closed — no vacuous truth), report/synthesis output grammars are pinned in the five reviewer agents + synthesizer prompts (role line, score:/fired: lines, exactly-once decision line, fenced-code stripped, duplicate sections rejected), and cardinality is guarded against duplicate paths/byte-identical reports/role-set forgery. Ships with protocol §8.1 runtime wiring, the zero-fired accept-grade fallback aligned across all surfaces (derived from the contract's F0 action, never hardcoded), and the majority quantifier corrected from a ⌈N/2⌉+1 transcription error to simple majority ⌊N/2⌋+1 (evidence chain in #531 — every concrete threshold in the v3.6.2 design says 3/5, 2/3). Design: docs/design/2026-07-15-510-panel-synthesis-checker-design.md; cross-model design review (gpt-5.6-sol xhigh) drove the grammar pinning, fired-set verification, and exit-code classification.

[3.16.0] - 2026-07-12 — Model tiering, cross-model gate hardening, WP advisory sharpening

Added

  • Model tiering: judgment/execution split with two opt-in directions, default untouched (#517). New ARS_MODEL_TIERING env switch and canonical shared/model_tiering.md, motivated by Lance Martin's "Cost effective harnesses with Fable" (2026-07-10; advisor-checkpoint configs measured ~90% of frontier-solo quality at ~34% of token cost, with delegation paying only when workers absorb enough tokens to offset per-handoff coordination cost). Default (unset): byte-equivalent pre-#517 behavior — every agent stays model: inherit (same opt-in philosophy as terminal_policies). economy (frontier-tier session): the 13 execution-type agents dispatch exactly one tier below the session model, floor Opus-class, never Sonnet (academic-prose tolerance is untested; the article's numbers came from ML tuning) — draft_writer explicitly flagged as the highest-savings / most quality-sensitive downgrade point. quality-boost (below-frontier session): the judgment-type agents dispatched at the Stage 2.5/4.5 integrity gates and the final-review surfaces step up to the frontier tier; nothing is ever downgraded. Both directions have explicit no-op conditions with a one-line announcement; unknown values warn once and behave as unset (fail-open to the safe default). Tiers are relative positions, never hard-pinned model ids (the v3.7.0 opus command floor retired in the Fable 5 harness pass is the cited precedent). Because many ARS roles execute inline today (no per-role model choice), the mechanism is dispatch-shaped: when a direction applies to a role, the session dispatches it as a subagent pinned to the target tier — inline roles included — and falls open to inline-on-session-model with a one-line announcement where subagent dispatch is impossible; docs/PERFORMANCE.md's (en/zh-TW) "no separate model routing layer" sentence is reconciled with a pointer. The frozen 39-agent classification (26 judgment / 13 execution — the issue header's 25/12 arithmetic corrected, membership unchanged) lives twice on purpose: a machine-readable scripts/model_tiering_manifest.json and the canonical doc's table, pinned to each other AND to the *_agent.md files on disk by new scripts/check_model_tiering.py — set equality with a repo-wide stray sweep (a new skill dir can't smuggle unclassified agents), tier-enum + duplicate checks, and EXACT per-(tier, skill) token-set comparison against the doc table (missing/extra/duplicate tokens, per-row counts, duplicate rows all fail; 15 mutation tests; wired into spec-consistency.yml + the local pytest manifest). Prompt-caching guidance (when a direction is active, route repeated same-stage calls to the SAME worker so its cache accumulates) documented in the canonical doc and each of the four SKILL.md files' compact ## Model Tiering (#517, optional) dispatch block — scoped so the unset default stays byte-equivalent, dispatch shapes included. No agent-file edits (the sha256-locked bibliography_agent.md untouched), no schema change, no hook. Spec: docs/design/2026-07-12-517-model-tiering-spec.md.

  • Cross-model gate hardening: risk-stratified sampling, blind disagreement checkpoints, id-status allowlist, promotion bakeoff (#518). Four upgrades to shared/cross_model_verification.md and its consumers, from a 2026-07-11 cross-model consult (gpt-5.6-sol, xhigh). (1) The integrity-gate cross-model sample moves from uniform random 30% (min 5, max 15) to risk stratification across four mutually-exclusive tiers (highest-precedence tier wins, one verification per reference): HIGH-IMPACT references (headline conclusions, numerical claims, causal claims, methods-critical, disputed) verified 100% uncapped at both gates; a 10% RANDOM sample of the remainder at Stage 2.5 (round-up, min 3, max 10); at Stage 4.5, NEW-CHANGED references (behind claims new or changed since 2.5) verified 100% uncapped plus a 10% CONTROL sample of the unchanged remainder replacing RANDOM — verification budget concentrates where the paper's weight rests, and the results table gains a Tier column (integrity_verification_agent updated in lockstep). (2) The two irreversible checkpoints — research-design freeze (research_architect_agent) and final editorial decision (editorial_synthesizer_agent) — gain optional blind disagreement checks: the primary commits its own decision in the same structured form first (the architect in a new Design-Freeze Checkpoint Audit blueprint section; the synthesizer's is its emitted decision), the cross-model then produces an independent structured decision from the same inputs (never seeing the primary's decision — same anchoring-prevention rule as the integrity samples; the editorial input is the panel's panel_size N usable reviewer cards, never a hardcoded five), differing enum values trigger a targeted rebuttal addressing each cross-model driver against the evidence on file, and divergence escalates to the user — a review trigger, never a vote, never averaged; under a sprint contract the check runs strictly post-Step-3 against the mechanical protocol's editorial_decision and its drivers never enter the scoring matrix. (3) The "6th reviewer — Planned" section is retired, not deferred: the consult's counterproductive-conditions list (score averaging, role duplication, findings treated as confirmed defects, majority-vote false confidence, synthesizer context burn) matches ARS's documented anti-patterns one-for-one; the blind checkpoints are the replacement design, and the live mirrors (.claude/CLAUDE.md, shared/raise_framework.md, SETUP feature tables en/zh-TW) drop the "remains planned" claim. (4) The model-detection snippet separates "which provider endpoint" from "is this id known-good": new CROSS_MODEL_ID_STATUS=validated|provisional|unlisted announcement with an explicit warning for unlisted first-party-prefix ids (gpt-made-up no longer passes silently) — routing itself is byte-identical, an unlisted id still takes the grounded route and never falls through to the ungrounded compatible branch. Plus a § Promotion Bakeoff operationalizing the gpt-5.6-sol provisional→validated criteria: a 30-reference paired same-day run (20 real / 10 fabricated; committed as a versioned, labeled, sha256-recorded fixture before any run counts; 3 repeats with ≥2/3 majority verdict, a 111 split scored conservatively against the model that produced it) against five non-inferiority thresholds (grounded-search completion, mismatch recall, false-disagreement rate, jq-guard shape stability — a hard requirement, p95 latency), entry-gated by scripts/cross_model_smoke_test.sh, results recorded under audits/ either way — and a deliberate two-step outcome: a full pass makes the id validated, while the recommended default flips only with an additionally stated superiority or operational-benefit reason. Spec: docs/design/2026-07-12-518-cross-model-gate-hardening-spec.md. Related: #517 (model tiering) will reference the checkpoint surfaces added here.

  • GPT-5.6 Sol listed as provisional cross-model verifier + explicit reasoning-effort control (#515). OpenAI's gpt-5.6-sol (released 2026-07-08) joins the canonical model table in shared/cross_model_verification.md as provisional pending ARS validation — endpoint support (Responses API + hosted web_search), the reasoning-effort enum (none|low|medium|high|xhigh|max, default medium), and pricing (same standard rates as GPT-5.5; premium is reasoning: {mode: "pro"} on the standard slug, NOT a -pro model id) were verified first-party against OpenAI's model page and GPT-5.6 guide, but ARS-specific behavior (grounded-search completion rate, citation-mismatch recall, false-disagreement rate, jq-guard response-shape stability, p95 latency) has no operating history, so GPT-5.5 stays the recommended default. The documented OpenAI Responses call pattern gains an explicit reasoning-effort control via new ARS_CROSS_MODEL_REASONING_EFFORT — set, it is passed as reasoning.effort so the run's effort is visible and reproducible; unset, the field is omitted entirely and each model's own provider default applies (forcing one value would silently change behavior for existing gpt-5.5-pro/legacy setups, a codex-review P2) — and both SETUP quick-setup blocks (en/zh-TW, parity-linted) mirror the new example lines. New scripts/cross_model_smoke_test.sh — a live, manual (not CI; needs OPENAI_API_KEY) promotion gate asserting HTTP 2xx, a completed web_search_call, a single verdict token, VERIFIED-carries-source, model echo, and effort echo — is the prerequisite for ever flipping the default to Sol. The canonical doc's Chat-Completions-web-search claim was re-verified against OpenAI's current web-search guide and deliberately left unchanged (a cross-model review suggested it was stale; first-party docs confirm it is still accurate).

  • WP advisory held-out miss-rate measurement + acceptance set, Part 2 (#501; direction from the PR #468 review thread, @brycewang-stanford). New evals/heldout/rq_framing_offlist/: a 48-item held-out set (32 shells outside the WP01-WP20 surface forms and the four in-prompt examples — 23 family variants + 9 off-list — plus 16 domain-native hard negatives), generated cross-model (gpt-5.6-sol), shell items regex-filtered (four negatives intentionally carry listed surface substrings as hard-negative material), dual-annotated with documented drops, English-only per the #468 caveat. Scored against the runtime LLM judge (isolated claude-sonnet-5 sub-agents, verbatim advisory section only, pre-#503 vs post-#503 variants, two post replicates): overall miss rate 0.34-0.38 (above the inherited FNR < 0.30 line), concentrated in decorated compound-title off-list shells (7/9 missed, stable across replicates; judges read generic topical nouns as the exemption's "specific mechanism"), family-variant generalization under the line post-#503 (0.17-0.22), false-fire 0/16 on both variants. Verdict: miss rate HIGH → per #501's decision rule the set is now the acceptance test for any future advisory change (protocol in the set's README; report at audits/rq-advisory-heldout-measurement-2026-07-11.md). Deliberately outside evals/gold/ (LLM-judged; no target.entrypoint, labels not reducer-reproducible). Closes #501; the measured off-list gap is tracked as a follow-up design issue.

  • Introduction & Title Rhetoric reference (#500; gap surfaced by PR #485, @lorenzo392). New academic-paper/references/intro_title_rhetoric_guide.md: CARS three-move Introduction guidance (territory / niche / occupation, with a licensed-gap rule, the universal-negative trap, purpose-sentence discipline, and a common-failures table) plus a title-crafting section (anatomy, four title types with claim-level cautions, checklist, weak→stronger worked examples, and a cross-check against the WP06/WP17/WP18 wording-pattern shells). Wired as a draft_writer_agent Step 1 setup checklist item; academic-paper/SKILL.md File Structure reference list updated (stale count 20 corrected to the actual 28). The declined PR #485 skill shape (new top-level skill) stays declined; this lands the two genuinely-uncovered content areas as a reference file per the maintainer response there.

  • Korean trigger keywords + routing boundary fixtures (#452 PR 1; proposal and Korean boundary phrases by @devCharlotte, who also authored the native-reviewed Korean README in #469). All four SKILL.md files gain a **한국어** trigger-keyword line plus a conservative Korean subset in the frontmatter description — intent-specific compounds only (논문 심사 / 논문 수정 / 초록 작성 / 체계적 문헌고찰 / 연구부터 논문까지 …), deliberately avoiding the broad standalone terms the proposal flagged (연구, 논문, 작성, 검토). The key 수정-vs-심사 disambiguation lands as two new routing smoke-test fixtures (tests/fixtures/issue_133_routing/09_korean_revision_not_review/, 10_korean_review_not_revision/) using the proposer's native-authored phrases; all six boundary cases from the issue pass a routing smoke test on the current primary model (6/6, recorded in the PR). No changes to agents, IRON RULEs, integrity protocols, schemas, modes, or output-language behavior. Closes #452 (the Korean README half shipped in #469/#471).

Changed

  • WP advisory exemption sharpening — decorated title-form shells now caught (#505; direction from the #501 Part 2 measurement). The exemption clause in both socratic_mentor_agent.md files (deep-research + academic-paper) is narrowed: it now requires a named or operationalized specific (an actual instrument/scale name, a named theory/model/dataset/policy instrument, a named site or population, a specified causal pathway — through what mediator/condition/process A relates to B, not merely that it does — or a stated tension between two identified explanations), declares ordinary domain-flavored topic-label pairs swappable, and adds a decorated-compound-title rule (an evocative pre-colon phrase plus a generic "X and Y (in Z)" subtitle gains no specificity from the decoration — the noun-swap test applies to the part after the colon alone). This closes the failure mechanism the #501 Part 2 baseline measured: judges reading generic topical noun pairs as the exemption's "specific mechanism", which rescued 7/9 off-list shells (six decorated titles plus one interrogative). Measured against the held-out acceptance set per its README protocol in two rounds (initial wording, then a cross-model-review-driven refinement — demographic descriptors excluded from "named population", single-topic subtitles covered — re-measured from scratch; 2 replicates each, same judge model): overall miss 0.375/0.344 → 0.094 in all four post-#505 runs, off-list 0.778 → all 9 items fired in at least one final replicate (final rep2: 9/9), false-fire 0/16 preserved in every run (including the four hard negatives carrying listed surface substrings); no shell missed in both final replicates; on-list gold set unaffected (regex detector untouched, fnr=0/fpr=0). All #505 constraints held: WP table unextended, advisory stays non-blocking and surface-phrasing-only, sentinel contract (test_check_rq_framing_patterns.py) unchanged; the new in-prompt example strings were substring-checked against every held-out item (zero hits) so the set stays held out. Measurement JSON evals/heldout/rq_framing_offlist/measurement-2026-07-11-505.json + reasoning excerpts appended; report at audits/rq-advisory-505-exemption-sharpening-2026-07-11.md. Closes #505.

  • Reviewer calibration protocol notes LLM-as-judge leniency direction (#484 → PR #506, merged). academic-paper-reviewer/references/calibration_mode_protocol.md gains a directional-prior subsection under "Failure cases this mode does NOT fix": when the simulated panel's output is read as a pass/fail signal, assume leniency relative to human expert review until your own calibration shows otherwise, anchored to FARS (Tang et al. 2026, arXiv:2606.31651 — automated reviewer mean 5.00 over 165 papers vs 3.23 paper-level mean from 282 human expert reviews over 140 papers; a descriptive ~1.8-point gap, and the automated score functioned only as a relative ranking). The direction is a working prior (heuristic extrapolation from one measured setup, default-until-measured); the magnitude is explicitly non-portable — never a correction factor or threshold change. Docs only; the panel remains advisory infrastructure behind human checkpoints. FARS added to References.

  • WP advisory generalization, Part 1 (#501; direction from the PR #468 review thread, @brycewang-stanford). Both socratic_mentor_agent.md files (deep-research + academic-paper) now state that the WP01-WP20 table is illustrative, not exhaustive, and name the operative judgment: the noun-swap test (phrasing is shell-like when it survives swapping its nouns for any other field's nouns). Off-list shells that clearly survive the swap may fire the advisory at the same high-confidence bar; domain-native phrasing that names a mechanism, instrument, site, or tension does not survive it and must not trigger. Advisory stays non-blocking and surface-phrasing-only; sentinel contract unchanged (test_check_rq_framing_patterns.py). Part 2 (held-out miss-rate measurement) landed separately — see the Added entry above.

  • API-first retrieval refresh: OpenAlex API-key auth, budget-aware 429 handling, arXiv ToU-aligned backoff (#495; proposed by @pikaqiu2333). OpenAlex's current developer docs are API-key-first (freemium daily budget; the polite pool is no longer documented): scripts/openalex_client.py gains OPENALEX_API_KEY support (query-param auth; either credential selects the authenticated 10 req/s pacing tier, OPENALEX_POLITE_EMAIL stays as legacy compat), distinguishes daily-budget-exhausted 429s (X-RateLimit-Remaining: 0 → raise OpenAlexUnavailable immediately — the budget refills at midnight UTC, so an in-process retry cannot succeed) from transient burst 429s (exponential backoff 2s → 4s → 8s per OpenAlex's documented guidance), and strips the query string from refusal-path error messages so the key never lands in logs (scripts/crossref_client.py gets the same redaction — its query string carries the polite-pool mailto email). scripts/arxiv_client.py's 429 backoff moves from the shared 2s constant to the 3s ToU pacing floor (arXiv's Terms of Use ask for at most one request every three seconds — a sub-3s retry would itself violate the pacing the 429 enforces; verified verbatim against the ToU page). Both protocol docs (deep-research/references/openalex_api_protocol.md, arxiv_api_protocol.md) updated in lockstep, plus an explicit retrieval-order boundary in each: structured APIs are the primary channel, browser/WebFetch page inspection is a bounded first-party fallback whose output is data-not-instructions (shared/ground_truth_isolation_pattern.md §2A), and browser retrieval is never a rate-limit bypass (no parallel browsing, no bulk PDF harvesting, no multi-machine fan-out). 5 new client tests; the two 429-behavior tests updated to pin the new backoff shapes.

Docs

  • Third-party directory: THIRD_PARTY.md + README pointer (#497 → #498). Community-submitted third-party projects that wrap or host ARS get a low-bar directory listing (visible ARS attribution + faithful description required) that is explicitly separate from endorsement — entries are not reviewed, tested, or verified by the maintainer, and the page says so up front. ClawMama listed as the first entry per issue #497. The README install section gains a neutral, non-recommending pointer to the page; the main install flow still links only to maintainer-verified paths. A separate "Getting officially recognized" track is documented for projects that want actual review.

[3.15.0] - 2026-07-04 — Release-gate hardening, prompt-debt retirement round 2, defrift locks

Added

  • Phase Boundary enforcement-sentence defrift lock + SETUP cross-model parity lint + local-manifest gap closure (#491 → #492). scripts/check_v3_9_2_phase_boundary.py gains invariant 4: every Bucket A block's enforcement paragraph must carry the canonical sentence verbatim (CANONICAL_ENFORCEMENT, version-matched v3.9.2/v3.9.4; per-file tails after the sentence stay free). Motivation: the pre-#294 copy of that sentence sat factually stale across 29 surfaces for a month while this very lint iterated those exact blocks pinning only the marker prefix — the drift class now fails CI, with 7 mutation tests including the literal stale sentence as the regression case. New scripts/check_setup_cross_model_parity.py (+9 tests, wired into spec-consistency.yml) pins the docs/SETUP.md / docs/SETUP.zh-TW.md ARS_CROSS_MODEL examples to each other and to the canonical model tables in shared/cross_model_verification.md — membership is scoped to "API ID" table columns after a codex-review P1 (legacy ids backticked in the accepted-for-existing-setups note must not count as recommended) and excludes glob prefix tokens per the re-review P2, failing closed when extraction finds nothing. The local pytest manifest gains test_check_v3_9_4_temporal_verification.py (58 → 60 entries): its CI-only coverage is what made #490 local-green/CI-red on the bibliography_agent sha256 F2 ownership pin. tests/test_helpers.py gains load_module_from_path(); the enforcement-sentence mechanism is cross-referenced with the firm_rules.md canonical-block framework in both directions (deliberately separate: factual status prose vs behavioral firm rules).

  • Release-doc alignment gaps closed in the version-consistency lint — three new invariants + a tag-time gate (#487). A 2026-07-03 audit of scripts/check_version_consistency.py against the suite-level release-doc discipline found the #357/#345 extensions had already closed the docs-version-ceiling, zh-TW-parity, and package-metadata invariants, but four sub-items had no machine check. Now enforced: invariant 9 — the latest CHANGELOG entry's body must be ≥ 100 characters (a bare heading is not release notes; historical entries stay exempt); invariant 10 — .claude/CLAUDE.md "Last Updated" must lie within ±7 days of the latest CHANGELOG entry's date (the CHANGELOG date is the baseline, never "today", so re-running the lint later cannot flip the result); invariant 11 — the newest ## vX.Y… Key Additions heading must match the suite version at the heading's own precision. New --tag mode plus a tag-version-match.yml workflow (push: tags: v*) require the pushed tag name to equal the suite version — the one comparison nothing performed at tag time — and re-run the full lint at the moment of tagging. A fifth candidate check ("every version cited in docs/ must exist in CHANGELOG") was evaluated and rejected: ARS's spec-version naming culture (e.g. the v3.6.6-design-shipped-as-v3.6.8 note) plus component versions make it structurally unenforceable (600+ legitimate references would false-flag). The latest-entry body ends at the next release heading via a fence-aware line scan, so a ## [...] line inside a fenced code block in the release notes can't truncate the body (even one shaped exactly like a real entry); CHANGELOG / Last-Updated dates are right-bounded and shape-gated to strict YYYY-MM-DD then parsed fail-closed, so a prefix like 2026-04-222 isn't mis-read as 2026-04-22 and an impossible date like 2026-02-30 reports a lint error instead of crashing. 20 new mutation tests (33 → 53), including the tag-gate hard-fail when the suite version is unreadable (a garbage tag co-occurring with a broken .claude/CLAUDE.md must not slip through the one gate meant to catch it).

  • Command-invariants CI gate — announce list machine-checked against the actual command inventory (#486). Vendors release-discipline-toolkit v0.3.0 under tools/release-discipline/ and adds .command-invariants.toml + a command-invariants.yml workflow. The lint discovers commands/*.md (16 today) and asserts scripts/announce-ars-loaded.sh lists exactly that set — missing command, stale extra, and the hardcoded Slash commands (N) count all fail CI — plus a version-lockstep check pinning .claude-plugin/plugin.json to the newest CHANGELOG release entry. Motivated by the announce list drifting twice (PR #193: 10 listed vs 12 actual; this change: 14 vs 16); the validator was verified against the live drift before installation (caught exactly the two missing commands, zero false positives).

  • CHANGELOG-covers-merges pre-tag release gate (#483). scripts/check_changelog_covers_merges.py machine-checks that every release-worthy commit merged since the previous release tag is documented in CHANGELOG.md above the previous release's section ([Unreleased] or a release-prep-promoted newer section — spec §0.2), with conventional-prefix exemptions (chore/test/ci/build, docs(design)/docs(superpowers)/docs(release)/docs(i18n)) and fail-closed behavior on missing tags/headings. New changelog-covers-merges.yml workflow gates every release/**-headed PR into main; the CONTRIBUTING manual step covers branchless tag flows. Would have blocked the 16-entry Unreleased backlog rolled up in 3.14.0. 47 tests, registered in the CI pytest manifest.

Changed

  • Prompt-debt retirement round 2: the 17 never-deep-scanned agents (#489 → #490). Continuation of the #476 track — the 2026-07 audit covered only the 22 Bucket A agents and explicitly deferred the rest. This pass deep-scanned the remaining 17 (all 5 academic-pipeline agents, 5 deep-research, 5 academic-paper, field_analyst, shared compliance_agent) plus 2 new commands and 3 carry-forward re-checks, via 4 parallel sub-agent batches + an independent codex cross-model challenge (12/12 excerpts verified; +1 finding, +1 scope amendment, +2 priority upgrades). 13 findings — 2 P1, 11 P2; the user rejected 2 (recorded so the next audit does not resurface them). The two P1s were live self-contradictions: both socratic_mentor agents carried stale pre-v3.0 "suggest quitting after 15 rounds" copies against a documented typical run of 20-30 rounds — auto-end machinery is now a single authority per file (threshold unified at 30), with stagnation/max-round constants single-sourced. The stale "prompt-level only / deterministic hook deferred to v3.10 #134" enforcement sentence (false since PR #294 shipped the write-scope guard) was rewritten at 23 agents + 4 SKILL.md + 2 mirrors, then tightened 39 → 35 words in the /simplify pass. Few-shot and duplicated-process scaffolds trimmed across 7 agents (question-template tables, citation-form examples, resume exemplars, triple-written dispatch rules incl. a missing view progress row restored to the authoritative table); SETUP model-lineup drift fixed in both languages. The 2026-06-10 F-007 deferred negative-framing item closes as verified, no rewrite needed — zero safely-reframeable negatives across the three top-density files, confirmed by both tracks. bibliography_agent's F2 baseline hash updated per the lint's documented procedure. Audit report: audits/harness-retirement-2026-07-04.md. Net 181 deleted / 98 substantive prompt lines excluding the report.

Fixed

  • SessionStart announce updated to the full 16-command set. /ars-3w and /ars-rebuttal-audit were missing from both the resume short-form list and the startup long-form listing, and the stated count said 14 — commands shipped after the list was last hand-synced were silently invisible at session start. Both forms now list all 16 with the correct count (announce script # version bumped 1.0.0 → 1.1.0); the new command-invariants gate prevents recurrence.

  • DOI badge served from shields.io (#482). Zenodo's badge endpoint rate-limits GitHub's camo image proxy (HTTP 429), intermittently rendering the README DOI badge as a broken image even though the DOI resolves fine. The badge image is now a static shields.io badge in all five READMEs; the link target stays the concept DOI (10.5281/zenodo.20696614), which always resolves to the latest version.

[3.14.0] - 2026-07-02 — Claude Science importability, eval-comment rendering, prompt-debt retirement

Added

  • Claude Science / GitHub-importer compatibility — explicit skill paths in the marketplace manifest (#480). The plugin entry in .claude-plugin/marketplace.json now declares "skills": ["./academic-paper", "./academic-paper-reviewer", "./academic-pipeline", "./deep-research"]. The repo-root skills/ directory holds symlinks, which GitHub-API consumers cannot traverse — Claude Science's "Import from GitHub" previously reported "Not importable: no skills/ dirs with SKILL.md" and found zero skills (same failure class as the #413 agents/ materialization). Per the Claude Code plugins reference, for a marketplace entry whose source is the marketplace root the listed paths REPLACE the default skills/ scan, so Claude Code installs keep loading the same four skills from their real paths — no content change, no double-loading. Verified end-to-end on Claude Science (4/4 skills detected at the merge commit). New README / SETUP.md guidance documents the import flow and its limits: imported skills carry the methodology (research / writing / review protocols); Claude Code-specific machinery (slash commands, hooks, subagent orchestration) does not transfer, and imports are point-in-time snapshots that require re-import after ARS updates.
  • Eval-harness PR comments render as a verdict + per-task table (#479). The eval-harness workflow previously pasted the whole eval_report.json into every PR comment as one raw fenced block. New display-layer module scripts/render_eval_comment.py emits a one-line verdict (✅ N/M measured tasks passed · K pending (not wired)), a per-task markdown table (metric / value / threshold / result; pending tasks render placeholders), and folds the full JSON into <details>. The row verdict mirrors the gate's failure signal (aggregate AND per-class, #328), with a test pinning agreement with scripts._eval_threshold_gate so the mirror drifts loudly in CI instead of rendering green on a blocked run; table cells escape pipes and all line boundaries so manifest-sourced strings cannot break or spoof rows (codex review finding, fixed in-PR). Evaluation logic is untouched: run_evals, the threshold gate, and the [eval-regression-acknowledged] ack contract are byte-identical. 13 unit tests, registered in the CI pytest manifest; the workflow-honesty test suite gains a pin so the comment cannot silently regress to a raw dump.

Changed

  • Prompt-debt retirement: expired writing-harness scaffolds removed from four Bucket A agents (#476 → #478, net 111 lines). The 2026-07 harness-retirement audit (#477, report under audits/) classified prompt scaffolds that encoded assumptions about what prior models could not do. The five P2 findings plus one hands-on finding were applied to abstract_bilingual_agent, citation_compliance_agent, draft_writer_agent (academic-paper) and research_question_agent (deep-research). Three-track verification: sub-agent audit + independent codex review (two fabricated back-references caught and fixed; re-review clean) + eval harness (citation_extraction and rq_framing_patterns both at 100%).
  • Platform Port Reminder CI (#473). A remind-don't-block workflow surfaces the "Platform ports (community-maintained only)" CONTRIBUTING.md policy when a PR adds a new top-level directory (motivated by PR #470, where a contributor could not have known the policy existed), plus a PR-template pointer.

Docs

  • Native-reviewed Korean README (#469; contributor credit #471). README.ko-KR.md contributed and translated by devCharlotte, with Contributors credit parity across the language READMEs.
  • GitHub Copilot repository instructions (#465). .github/copilot-instructions.md orients Copilot-based contributors to the repo's structure and conventions.
  • Permission-mode guidance (#464). Install docs now recommend Claude Code's auto permission mode over Skip Permissions.

Rolled up from [Unreleased] (code landed before the v3.13.0 tag)

Provenance note: the entries below shipped in the repository between the v3.12.0 and v3.13.0 tags, but their changelog bullets had accumulated under [Unreleased] across releases. They are versioned here so the release record is complete — no new code ships with these bullets in v3.14.0.

Added

  • Diff/patch revision mode — Slice B revision-mode adoption (#89 Item 7, spec #390, sub-issue #424). The MVP ship-gate slice: academic-paper revision mode now runs anchorize → patch → deterministic apply → finalizer instead of full re-emission. draft_writer_agent gains the ## Patch-Document Revision Emission (#390) contract (patch document as a phase6_*/revision_patch_round<N>.json sidecar — hashes copied from the block manifest, never computed; [PATCH-ESCALATION-REQUIRED:] pre-drafting escalation tag; retry-once; provisional Schema 8 items with mechanical fields left to the orchestrator). pipeline_orchestrator_agent gains ## Revision-Round Patch Sequencing (#390) (five normative steps with a no-rewrite window between manifest generation and apply; two-layer escalation gate with the MANDATORY checkpoint wording; never auto-fallback to full re-emission; escalated rounds re-anchorize under a new ID generation and stamp mode: full_reemission_escalated; preserved_ratio surfaced next to the #389 round-trip count). Schema 8 ResponseItem gains optional change_block_ids (orchestrator-populated from the apply report, §3.5 role split). New protocol doc academic-paper/references/revision_patch_protocol.md (exact Mode B commands, exit codes, apply report as a required re-review input, marker lifecycle). Two recorded ship decisions land as a spec §0 amendment with cross-model concurrence: touched_ratio threshold = 0.6 (now the apply-script CLI default, strict >, 1.0 disables) and the insert_after heading-anchor exemption (anchoring on a heading no longer flags when the inserted text carries no headings; heading-bearing text still flags). §10 open items closed the verified way: formatter_agent gains ## ARS Marker Stripping (#390) (all marker kinds stripped from converted final outputs only AFTER marker-dependent gates; working drafts keep markers) and word_count_conventions.md gains the strip-<!--...-->-before-count rule (first-party check found NEITHER rule previously existed — the spec's "expectation" had nothing to point at); max single-op new_text size folded into the existing triggers (no separate cap). New lint scripts/check_390_revision_patch_discipline.py (8 invariants: writer/orchestrator/SKILL/Schema 8/protocol-doc/marker-rules block-scoped literals, threshold value lock, spec-example schema validation) + 30 mutation tests, wired into spec-consistency.yml + the pytest manifest.
  • Diff/patch revision mode — Slice A deterministic toolchain (#89 Item 7, spec #390). First implementation slice of the DELEGATE-52 rank-1 item: the deterministic tools exist and are tested, zero prompts touched (Slice B wires revision-mode adoption). New shared parser scripts/_block_parser.py (fail-closed §3.1 block segmentation: fence/heading/table/list/blockquote/text + skipped YAML frontmatter; setext underlines, line-initial raw-HTML openers, and footnote definitions rejected by name, never swallowed; duplicate-ID / orphan-marker / marker-stack rejection; read-side-only hash normalization). New scripts/ars_anchorize_draft.py (script-owned <!--block:BNNNN--> stamping — the LLM never assigns IDs; idempotent and content-neutral; emits the block manifest <draft>.block-manifest.json, the ONLY legitimate hash source a patch may copy from). New scripts/ars_apply_revision_patch.py (two-phase fail-closed apply: validate-everything-touch-nothing then byte-span splicing, so untouched blocks are byte-identical by construction; structural-shape triggers gated by --acknowledge-structural; touched_ratio recorded in every report with the threshold VALUE deliberately deferred to Slice B; machine-verified pure_move pairs; atomic temp+rename writes; apply report with preserved_ratio counters). Two new schemas under shared/contracts/patch/ (revision_patch.schema.json — the DOC-BODY-START branch is the only legal hash-less op shape; block_manifest.schema.json). 86 new tests across three suites incl. the §8.3 byte-identity property test (seeded randomized patches; untouched blocks + marker lines + separator bytes asserted byte-equal), wired as 3 new CI pytest manifest entries.
  • Submission-package verifier Slice 4: terminality (#394 — closes the issue; all four slices landed). The opt-in blocking layer, shaped by a cross-model gate-1 plan review (1 P0 / 4 P1 / 2 P2, all adjudicated; the P0 resolved as maintainer Option B). New terminal_policies.submission_package key (closed enum {advisory, strict}, per-key absence = advisory via the evaluator runtime convention — the citation_existence pattern; no JSON-Schema default). The §5.3 single-homed boundary is sharpened, not moved (Option B): the orchestrator stays the sole reader/selector of the policy and hands the resolved value down via the new --policy CLI flag; the script mechanically applies it — stamps header.policy_slug (argparse default None: a flag-less standalone run is unevaluated, stamped null, and a null-stamped report never satisfies pipeline freshness — never "default advisory"), and under strict emits the terminal verdicts: a strict-eligible fail → stdout token TERMINAL-BLOCK policy=submission_package + exit 1; else a strict-eligible not_checkedVERIFICATION-INCOMPLETE + new exit 4 (fail-closed §5.2 — a missing parser/profile cannot waive the class the scholar opted into; not_applicable never composes into either verdict, keyed on status not the eligibility bit). Terminal signals are the stdout tokens, never raw exit codes — exit 1 also carries nonterminal heuristic fails (gate-1 P1). New --check-freshness mode (REQUIRES --policy): recomputes the package fingerprint + compares the stamped slug, mismatch/null/missing → STALE-REPORT + new exit 5, no checks re-run, no writes. The fingerprint exclusion set grows to report + provenance_summary.md (gate-1 P1: the formatter appends the advisories section AFTER stamping — fingerprinting the advisory carrier would self-stale every evaluated report). Orchestrator gains the Stage 5 post-formatter Submission-Package Terminal Gate section (resolve-absence-to-advisory + always-explicit --policy, gate-on-tokens, fix loop bounded 2 rounds then surface, strict-needs-a-venue-profile remediation stated plainly, freshness-before-reuse, recompute-each-pass C-V6(h) mirror); formatter gains the Submission Package Advisories provenance_summary.md section (mandatory and non-empty iff any fail/warn/not_checked under advisory; stamp-only Invariant 13 untouched). New scripts/check_394_submission_policy.py (5 invariants) + 26-test companion — invariant 4 is an AST single-homed guard (Subscript/.get access of terminal_policies), not a literal grep, because the verifier's docstring legitimately says the word (gate-1 P2). 17 new verifier tests (79 in the verifier file; 105 total with the lint companion) incl. an evaluate_policy decision-table unit pin (the advisory/strict divergence lives inside the evaluator, not at the call site), byte-equivalence by before/after hashes, and three killed mutations (eligibility gate, fingerprint exclusion, null-freshness reason token). Gate-2 cross-model diff review (2 P1 + 1 P2, all adopted) + an independent context-reviewer pass over the two prompt sections (2 P1 + 4 P2, all adopted): a FRESH report now re-emits its policy verdict on --check-freshness (same token + exit semantics as a live run — a recorded terminal verdict can never evaporate across a resume), the report header gains inputs_fingerprint over venue-profile/passport/join-map bytes (a report produced under one venue profile is STALE under another; legacy reports without the field never read fresh), the v3.10 policy_hash marker stamp is scoped to CITATION-TIME keys (submission_package never stamps markers — a package-only strict passport no longer forces marker re-finalization or stale-refuses legacy markers; oracle + test updated), the orchestrator gate's advisory path now explicitly re-dispatches the formatter in append-only mode (the advisories section had no named writer), TERMINAL-BLOCK's stdout-vs-marker channel reuse is disambiguated in place (the policy= value is the discriminator), the fix-loop round is defined (dispatch formatter → re-run verifier; never a 3rd), VERIFICATION-INCOMPLETE remediation is routed away from the formatter fix loop (a missing profile is not formatter-fixable), token matching is pinned to line-prefix, and the freshness policy_mismatch line reprs the stamped slug (a forged report cannot inject a fake token line into stdout). A final confirmation round (2 P2 + 1 P3, all adopted) added the reuse-side roster guard (a hand-edited checks: [] report is STALE-REPORT reason=roster_mismatch, never a clean re-evaluation — the report file is outside the package fingerprint, so content validation must not be skipped on reuse), --join-map to the orchestrator's live verifier command (live and freshness invocations must carry the same input set or the inputs fingerprint can never match), and direct-value enum comparison in lint invariant 5 (the string "None" must not pass for JSON null).
  • Submission-package verifier Slice 3: Family A blind-review residue scan + Family D assessment (#394). The high-embarrassment-cost class. Trigger is presence-or-declaration (§3.1): an anonymized variant in the package (filename stem token anonymized/blind/…) or a declared blind_review: double — untriggered packages report the new not_applicable status (additive report-schema enum), visibly distinct from not_checked so a single-blind package is not condemned to exit 3 forever. Checks: A1 PDF /Author + XMP dc:creator via pypdf (the only new parser dependency; defusedxml is additionally declared in requirements-dev as XML-bomb hardening with a stdlib fallback; pypdf absent → NOT-CHECKED(parser unavailable), never folded into pass per §1.4), A2/A3 DOCX metadata + tracked-changes/comment authors read RAW from the zip parts via stdlib zipfile + XML (defusedxml hardening when available) — a refinement over the planned python-docx: closer to the §1.3 artifact≠rendered-view premise and the DOCX residue class has no missing-parser hole at all, A4 acknowledgments section in the blind variant (deterministic signal, strict-eligible ONLY when the profile declares the new acknowledgments_forbidden_in_blind: true — the §3.1 load-bearing two-axes rule, enforced via a downward-only eligibility override), A5 self-citation phrasing (heuristic by class; ships a first-party zh-TW draft list per §10 item 1; curated by the maintainer 2026-06-10, adding 本文作者先前), A6 author-name tokens from the non-anonymized artifacts' metadata appearing in package filenames (heuristic; the metadata-source originals themselves are exempt), A7 declared-double-blind with no anonymized variant = fail (the most basic residue: the blind version is missing). Family D ships nothing: the slice-3 deliverable is the adjudication-ready assessment (docs/design/2026-06-10-394-family-d-repro-lock-assessment.md, recommending no-check with the B4 required_sections escape hatch — the repro_lock gates-don't-read-it boundary stands untouched; adjudicated Option 2 by the maintainer 2026-06-10). 16 new tests (55 total; corrupt-docx honesty, parser-absence honesty, A4 conditional-strictness mutation killed).
  • Submission-package verifier Slice 2: scholar-declared venue profile + Family B limits checks (#394). Second slice of the #394 spec. New shared/contracts/submission/venue_profile.schema.json (standalone, Invariant 11 pattern; declared_by: scholar is the only provenance value and the CLI refuses a profile without the stamp) and a --venue-profile flag enabling five deterministic, strict-eligible checks: B1 manuscript word count, B2 abstract word count (both ±2% tolerance per §3.2), B3 keyword count range, B4 required sections (case-insensitive heading containment), B5 reference-count ceiling against the same machine-readable reference list Family C uses. The no-inference rule is structural (R-L3-2-D mirror): without a profile every Family B check reports NOT-CHECKED(no venue profile); a partially-declared profile runs what it can and NOT-CHECKEDs the rest with the undeclared field named; declared limits whose actuals cannot be located (no abstract section, no keywords line) report NOT-CHECKED with the reason, never a guess. This also makes the exit-code semantics visibly honest: a profileless run that is otherwise green exits 3 ("passed what was checkable", §8), not 0. Word counting reuses the canonical whitespace-split convention (shared/references/word_count_conventions.md); LaTeX counting adjudicated per spec §10 item 4 as naive detex + whitespace-split, with the method and counted file declared in the report detail. Intake Step 3 gains the optional declared-values-only venue-profile follow-up (plan mode exempt, mirroring Steps 12/13) + a PCR Venue Profile row. 14 new tests (39 total) incl. tolerance-boundary and provenance-gate mutations; fixture venue_clean passes every B check against profiles/full.yaml, fixture venue_violations fails all five against profiles/tight.yaml (mutation discipline). Cross-model review (codex) adopted in full: schema-strict CLI validation (closed field set, bool≠int), word_count_scope: all counts everything, and canonical-name manuscript selection with NOT-CHECKED(ambiguous manuscript) instead of silently picking the wordiest candidate.
  • Submission-package verifier Slice 1: CLI skeleton + Family C reference integrity (#394). First implementation slice of the 2026-06-10 #394 spec (slices are dependency-ordered; Family C ships first because it needs zero new parser dependencies). New scripts/verify_submission_package.py standalone CLI: point it at any output package directory and it runs the two-way reference-integrity set check (orphan in-text citation = fail, uncited reference entry = warn) and writes submission_verification_report.json validating against the new shared/contracts/submission/submission_verification_report.schema.json. The deterministic joined marker path consumes <!--ref:slug--> markers plus a real prose-reference join — the passport's citation_verification_summary[] (--passport), an explicit scholar-supplied map (--join-map), or a package .bib via the documented slug==citation_key identity relation — and markers with NO join source report NOT-CHECKED(missing prose-reference join), never a guessed comparison (§3.3). Non-ARS / post-converted packages fall back to format-aware best-effort extraction (\cite{} for LaTeX, author-year regex for Markdown), heuristic-classed: the schema STRUCTURALLY forbids signal_class: heuristic + strict_eligible: true, so the fallback path can never be promoted to blocking by any later policy slice. Report header carries extraction_path, not_checked_count (incompleteness is never folded into pass, §1.4), package_fingerprint (spec §10 open item 3 adjudicated: the audit-snapshot manifest convention — byte-sorted path:sha256 lines, fingerprint = SHA-256 of the manifest, report file excluded), and policy_slug (always null until the slice-4 orchestrator hook; the script never reads terminal_policies, §5.3). Exit codes separate "all checked, pass" (0) from "passed what was checkable" (3) per §8. 19 tests + 7 fixture packages with mutation discipline (orphan / uncited / no-join failures proven to fire); CI-wired via the pytest manifest. Advisory-only: no manuscript byte changes, no pipeline hook yet (slice 4). #394 stays open until all slices land.
  • Design doc: deterministic submission-package verifier (#394, blindspot-audit F-5, design-first — no implementation yet). docs/design/2026-06-10-394-submission-package-verifier-spec.md designs scripts/verify_submission_package.py, the script-layer backstop for the mechanical subset of the formatter's prompt-layer submission checklists (the #182 promotion pattern: LLM self-check → deterministic gate). Three check families in adjudicated priority order — blind-review residue (raw-structure scan: PDF/DOCX metadata authors, tracked-changes/comment authors, self-citation phrasing; artifact ≠ rendered view is a stated premise), venue-declared limits vs actuals (scholar-declared venue_profile schema, never inferred from the journal name — R-L3-2-D mirror), reference integrity (two-way set check; the deterministic path requires an explicit slug↔key join source or reports NOT-CHECKED) — plus a stretch assessment of repro_lock presence/shape checking that leaves the recorded gates-don't-read-it boundary untouched. Two load-bearing rules: signal_class and strict_eligible are separate axes (heuristic checks are structurally excluded from strict; A4's deterministic signal still isn't block-worthy because the de-anonymization judgment is the scholar's), and strict fails closed on incompleteness (VERIFICATION-INCOMPLETE when a strict-eligible check can't run — a missing parser must not silently waive the one class the user opted into blocking on). Terminality via a new additive terminal_policies.submission_package key, evaluated by the orchestrator against a fingerprint+policy-slug-stamped report (package-level freshness guard — explicitly a new post-format gate, not the ref-marker stamp path). Cross-model reviewed (codex): 2 P1 (Family C join requirement; strict fail-open on NOT-CHECKED) + 4 P2 + 2 P3, all adopted. 4 dependency-ordered slices; advisory-only until slice 4.
  • Real-use findings release-notes convention documented; showcase refresh tracked (#395, blindspot-audit F-9). CONTRIBUTING.md gains a Release checklist section documenting the convention: a release's CHANGELOG entry includes a Real-use findings subsection when issues were discovered through actual use on a real paper — one line per issue naming the run — so lived-experience provenance has a fixed, greppable home instead of being buried in spec prose (the v3.6.7 production chapter run surfaced 17 drift patterns and nothing structural recorded that provenance class; release motivation since v3.8 has been almost entirely external papers, which is itself a per-release signal worth seeing). Omitted when empty, never padded. The showcase refresh half of F-9 (no post-v2.7 end-to-end artifact set exists) is split to tracking issue #404, blocked on the next real paper with no artificial deadline per the adjudication.
  • POSITIONING records two non-goals; cross-paper workflow guide ships (#397, blindspot-audit F-1B/F-7). POSITIONING.md's recording discipline (five Rejected mechanisms with rationale) had two adjacent boundaries existing only as silence. A new "Recorded non-goals" section records both with the same boundary-plus-review-criterion treatment: post-publication lifecycle (own-paper citation tracking / errata / OA self-archiving — the front is research-to-publication; monitoring_agent is unaffected since it alerts on cited literature, not the scholar's own output) and research-program-level state (no cross-paper claim registry / limitations memory / reviewer-history profile — the per-paper Material Passport stays the only state carrier, a deliberate anti-leakage consequence). The supported no-mechanism path for returning authors ships as docs/cross-paper-workflow.md: (1) re-feed the prior paper's passport through the existing input port — a prior ok is a head start, not a waiver, stamps re-derive under current policies; (2) bring prior limitations / unresolved reviewer points to RQ incubation as scholar-supplied Socratic input — ARS asks about your reading, never derives next-RQ candidates (Kong L2 cross-linked); (3) Claude Code assistant memory may serve as a personal reminder layer with the load-bearing caveat that ARS gates never read or trust it — the workflow must work identically on a machine with no memory at all. Documentation only; no schema, flags, or cross-run mechanism.
  • Intake Step 13: citation-verification level surfaced at the configuration interview (#392, blindspot-audit F-8, adjudicated "keep default, add a prompt so the user chooses"). The v3.11 citation-existence gate's strict mode existed only in README prose and the v3.10/v3.11 specs — a QUICKSTART user had no signal the choice existed. The intake interview gains Step 13: "Citation verification: mark only (default) / strict", with one sentence of field guidance (strict suits DOI-dense fields; mark-only suits grey-literature-heavy fields). Byte-equivalence is load-bearing: a strict answer seeds terminal_policies.citation_existence: strict on the Material Passport at the point it is materialized (the finalizer stays the sole policy evaluator); a mark only or absent answer records the PCR row and writes nothing — per-key absence already means advisory (Invariant 7), so an unprompted run is byte-identical to pre-#392. Plan mode exempt (mirrors Step 12). No default changes anywhere. Guarded by scripts/check_392_citation_verification_intake.py (4 invariants, mutation-verified): Step 13 heading present (rename = fail-loud parse error), the no-handoff directive affirmatively reaches Step 13 (then Step 13 — the exact #327 P1 orphaning that hit Step 12), PCR row present, and the advisory write-nothing rule + strict seeding target retained. 8 unit tests; wired into spec-consistency.yml + the CI pytest manifest.
  • Layer-5 contribution-significance probes extended into plan mode and revision coaching (#393, blindspot-audit F-2, adjudicated shape 1). ARS quality machinery was defect-oriented end-to-end — a paper could pass every gate and still be a micro-extension, because the only constructive contribution coaching (socratic_mentor Layer 5, SIGNIFICANCE & CONTRIBUTION) lived at the RQ-incubation stage. Layer 5 now defines three later-stage anchored forms with stable IDs — L5-W1 "Ten years from now, what will citers say this paper established?", L5-W2 "Remove this paper from the literature — what is missing?", L5-W3 "If this paper succeeds, who would make different decisions as a result?" — and two later-stage surfaces consume them strictly by ID (the question text lives in Layer 5 and only there; a Layer-5 edit propagates by reference instead of forking — the cross-model review's P1 on a first draft that carried labeled copies): (1) academic-paper plan mode gains Step 2.5 CONTRIBUTION SHARPENING between chapter negotiation and the argument stress test — the mentor asks the user to articulate the contribution their own Chapter Summaries claim, quoting only user-written text; if the user articulates one, [INSIGHT: contribution_claim] records it in the user's words, otherwise the open question is carried into Step 3, never filled in; (2) academic-paper-reviewer Phase 2.5 gains step 3, a contribution framing probe alongside the existing prioritization steps (5→6 steps; no external step-number references existed), anchored to what the manuscript already claims. The orchestrator's Stage 3→4 coaching sketch now explicitly defers to the reviewer SKILL.md six-step list as authoritative (net-zero line edit — the surface has 1 line of v3.6.7 budget headroom left). Boundary is load-bearing (Kong L2 verb test, per the #393 adjudication that rejected shapes 23): questions only — never propose, substitute, rank, expand, or select a contribution claim. Prose-layer only; no schema, scoring, or agent-roster change. Two review gates, all findings adopted: codex cross-model (1 P1 + 2 P2 + 1 P3 — ID-based single-sourcing, verb-test tightening, orchestrator deferral, conditional INSIGHT) and an independent context reviewer (1 P1 + 1 P2 — the repo has TWO same-named socratic_mentor_agent.md files and plan mode dispatches the academic-paper variant, which had no Layer 5 and no Step 2.5 in its own flow, so the probe would never have fired: the agent prompt gains a Step 2.5 section referencing L5-W IDs by full path, the protocol's inline reference is path-disambiguated, and L5-W3's anchor permission is tightened to noun-phrase-swap-only).
  • Interaction-count budget surfacing + Context Hygiene dispatch discipline (#388; DELEGATE-52 Items 4+5 from #89). The two cheap, high-confidence follow-ups from the re-ranked DELEGATE-52 work order (arXiv:2604.15597), both prose-layer. Item 4: the v3.2 Budget Transparency block in academic-pipeline/SKILL.md now also presents an interaction-count budget at pipeline start — the paper's core result is that long-horizon corruption compounds with document round-trips, not token volume, so the block enumerates the round-trip caps the pipeline already enforces (2 revision loops, 8+5 Socratic coaching rounds, the integrity fix→re-verify loop), states the worst-case total for the chosen mode, and reports the accumulated count at every stage checkpoint. Advisory only — the per-loop caps remain the enforcement layer; exceeding the stated worst case signals an uncovered loop and must be surfaced, never silently continued. Item 5: pipeline_orchestrator_agent.md gains a Context Hygiene at dispatch block targeting the paper's distractor ablation (non-target documents in context measurably worsen outcomes): each handoff carries the receiving agent's declared inputs plus the Material Passport — never the accumulated pipeline as a convenience bundle; scratch output and superseded drafts do not ride forward (later stages read passport entries, not raw transcripts); supersession means dispatching the current version only, with prior versions retrievable through the versioned-artifact trail. The passport carry-forward obligations (claim/audit aggregates, experiment_intake_declaration, slr_lineage) are explicitly exempt — trimming applies to loose materials, never passport fields. Carries an epistemic-status line (dispatch-assembly discipline, not a runtime guarantee). Scope note recorded in #388: this lands the single-dispatch-point version; #89's per-downstream-agent sketch stays open under the parent. Items 1, 2, 7, 8, 9 remain tracked in #89.
  • Repository-hygiene CI (#151). A repository-hygiene.yml workflow runs gitleaks over the full git history on every PR and main push, with the upstream default ruleset and no custom rules. The binary is version-pinned (8.30.1) and checksum-verified rather than pulled via the marketplace action (which needs an org license key; a pinned release binary keeps the supply chain auditable), and --redact keeps any genuine hit out of public CI logs. The only local configuration is a false-positive allowlist (.gitleaks.toml): the 2026-06-10 baseline evaluation found 43 findings across 518 commits — every one a bibliographic citation key (Becht2019UMAP, vaswani2017-arxiv-v1, forthcoming2024) in eval gold tuples / adapter fixtures / version-family examples matching the generic-api-key heuristic's key-shaped-string pattern, zero true positives — so those literature-identifier paths are allowlisted by path, never by rule edit (a new tuple under an allowlisted path needs no config touch). A seeded-credential mutation test confirms the configured scan still exits 1 on a real-pattern secret outside the allowlist (github-pat hit) — and recorded a method caveat: a low-entropy dictionary-word seed does NOT clear the entropy gate, so a valid mutation check needs a real-pattern, high-entropy seed. Closes the defer:v3.10 evaluation with an adopt decision: all three decision criteria pass (no infra burden — public pinned binary; bounded maintenance surface — path entries only; post-allowlist FP rate 0 on the full history).
  • Field-norm severity calibration across the reviewer surfaces (#215, Kim et al. 2026 arXiv:2605.20668v1 §5.1 W1 + §F.3.4). Closes the paper's largest documented AI-reviewer failure class: a critique that is content-correct against a discipline-neutral standard but severity-miscalibrated because the reviewer lacks the subfield's accepted-practice prior (W1, n=54 — the CERN/LHCb reproducibility example), plus the sibling significance-boundary error from the "would addressing this change the core result?" formula (§F.3.4, 56 errors). Three reviewer surfaces are hardened, each at severity-assignment time and applying to every field-norm-dependent finding (not only CRITICAL): domain_reviewer_agent.md gains a Step 5 hard rule — a severity that rests on a field norm MUST be grounded in an external checkable source (a reference, venue/data policy, community standard, reporting guideline, or documented expert practice — explicitly not limited to a literature citation, and not model knowledge), else down-rate to advisory + [FIELD-NORM UNVERIFIED]; devils_advocate_reviewer_agent.md gains a 9th challenge dimension (the DA turns the lens on its own findings, since adversarial intensity amplifies an ungrounded norm into a CRITICAL) plus two required CRITICAL/MAJOR output columns field_norm_boundary + evidence_crossing_rationale; calibration_mode_protocol.md gains a Phase 3.5 severity-miscalibration measurement + a low/med/high histogram in the Calibration Report — a signal the binary FNR/FPR matrix cannot show, where the classifier rates whether the reviewer supplied external grounding, not whether the norm is factually correct (guessing norm-correctness would repeat the very W1 failure under audit). A first-party regression fixture ships at evals/gold/field_norm_severity/ (10 cases — 5 W1 field-norm-boundary + 5 §F.3.4 significance-boundary — extracted verbatim from the paper with section/example-ID + paper-citation-token + verbatim-anchor provenance; the SAR 11.7T case flagged exception: true because experts concurred with the AI there). Because there is no deterministic detector for field-norm severity miscalibration, the fixture is a regression set, not a calibration set: scripts/check_field_norm_severity.py validates data integrity + first-party provenance (no FNR/FPR ritual), and scripts/check_215_field_norm.py asserts all three reviewer surfaces carry their blocks with block-scoped keyword checks (fence-aware) so a stray keyword cannot mask a missing rule. The two lints survived a three-pass cross-model (codex xhigh) review that drove finding count 4 → 2 → 0; every fix is mutation-tested (28 tests). Additive and backward-compatible; CI-wired via the spec-consistency workflow + pytest manifest. (#216 — the §F.3.6 reviewer-type parity half — was split out: it needs a different gold set of human-phrased vs AI-phrased paired cases.)
  • Surface-Form Parity self-check (#216, Kim et al. 2026 arXiv:2605.20668v1 §F.3.6). Closes the paper's reviewer-type asymmetry: an AI meta-reviewer applying two standards keyed off prose style — demanding literal precision from informal/vague (human-typical) wording, so it over-rejects correct concerns (29 of 41 correctness false negatives involved human reviewers), and crediting technical specificity in precise (AI-typical) wording, so it over-accepts incorrect ones (10 of 13 false positives involved AI reviewers). The root cause the paper names is a learned prior that specificity correlates with correctness. Key design call (after a codex xhigh consult): the hook is prose style, NOT the author label — so the mitigation is a Surface-Form Parity self-check (not "authorship parity"), and authorship is kept out of the runtime reviewer-item schema entirely (not merely audit-only). Two verdict-time surfaces carry the parity self-check (a codex review found the editorial synthesizer also arbitrates reviewer sub-claims and down-ranks "too vague" criticisms — exactly where §F.3.6 fires): devils_advocate_reviewer_agent.md gains a verdict-time parity self-check (a marker block, distinct from #215's severity-time gate) and editorial_synthesizer_agent.md gains a Step 1c arbitration-time check + a reworded "reduce weight if too vague" rule that fires only when vagueness makes a sub-claim unevaluable. The DA check: extract the checkable claim → judge it against the paper not the polish → do not down-rate informal/vague wording unless ambiguity changes truth conditions → do not credit technical specificity without checking → run the opposite-style counterfactual and revise / mark ambiguous on a flip. A mixed-provenance regression fixture ships at evals/gold/surface_form_parity/ (7 cases: 4 paper_verbatim §F.3.6 examples + 2 maintainer-authored counterfactual_rewrite paired variants carrying derived_from + semantic_equivalence_rationale + 1 maintainer_boundary documenting the "unless unevaluable" clause). Because there is no deterministic detector for the surface-form bias and the 29/10 split is directional (§H), the fixture is a regression set, not a calibration set: scripts/check_surface_form_parity.py validates integrity + provenance honesty (paper_verbatim quotes the paper; maintainer-authored items never claim paper-verbatim) + pair invariants (paired items hold claim + verdict constant, differ only in framing) + no rotting pdftotext line anchors — no FNR/FPR ritual. The schema decision is enforced at runtime by render_judge_view(), a whitelist projection (judge sees only an index-derived opaque handle + review_item_text) proven by a serializer-strip test to leak no blind field — including the nested provenance.reviewer_source author label and the answer-encoding fixture id itself (-cf / -ambiguous suffixes, per codex review). scripts/check_216_surface_form.py asserts the DA carries every load-bearing clause block-scoped + fence-aware (six-class mutation suite). run_evals discovers the fixture and marks it pending (no native measurer, by design — pinned by a test_run_evals test so it cannot false-green through the eval gate). Negative scope: #273 (rubric-aware calibration) is NOT folded in — it is a different mechanism (an interpretive caveat with no detection claim); #216 carries a cross-reference only (design note + PR body + manifest.yaml related_issues), with no shared prompt / gold / lint / runtime wiring. Additive and backward-compatible; CI-wired via the spec-consistency workflow + pytest manifest. Design note: docs/design/2026-06-09-216-surface-form-parity-design.md.

Changed

  • Plugin-root agents/ symlinks materialized as real byte-identical copies (#413, external audit). The three agents/*_agent.md files were relative symlinks into deep-research/agents/ (v3.7.0 Phase 2.1) — on Windows checkouts without developer mode / core.symlinks, and in zip-download installs, they materialise as one-line text files containing the link path, silently breaking the three plugin agents. Maintainer-adjudicated fix: real copies, with the single-source guarantee the symlinks provided (the v3.7.0 Pattern C3 rationale for symlinks-not-copies) taken over by a new CI lint, scripts/check_agents_mirror_sync.py — a hard-pinned mirror roster enforcing set equality (a deleted mirror silently un-ships an agent; an unrostered addition has no declared source), regular-file-never-symlink (the regression itself, checked before byte-equality because a symlink trivially byte-matches its own target), and byte-equality with the canonical source (fix hint names the copy direction: edit the source, re-copy, never edit the mirror). The two lints that leaned on symlink resolution adapt: check_version_consistency.py invariant 8 now excludes the mirror dir from the unique-agent count outright (real copies no longer dedup via resolve(); the exclusion is sound because the mirror lint pins every file there as a pure alias), and check_v3_10_134_write_scope.py I5 maps a root-agents/ file BY NAME to its deep-research/agents/ source before the roster check — with a negative test pinning that the mapping is not an allowlist (a name with no rostered source still flags as fail-open). 10 new mirror-sync tests (3 mutations killed: symlink-branch, byte-equality, unrostered-extra) + 5 adapted/added tests across the two existing suites; lint + pytest companion wired into spec-consistency CI. Cross-model review round (1 P2, adopted with an empirical repro): the I5 remap is restricted to DIRECT children of root agents/ — a nested agents/sub/agents/<rostered-name>.md no longer remaps to the deep-research source (which would have silently reopened the fail-open case the recursive glob exists to catch), pinned by a negative test. The skills/ directory symlinks are unchanged — materializing those means duplicating the four skill trees, a separate decision if Windows source-checkout support is ever pursued. (2026-06-10 audit; follows the #301/#347 4.7→4.8 pattern).** Trigger: the primary session model moved to Fable 5, which inverts the v3.7.0 model: opus frontmatter floor on the three heavy commands (/ars-full, /ars-reviewer, /ars-revision-coach) into a silent downgrade ceiling — those commands now inherit the session model (the 11 light-mode sonnet pins are deliberate cost routing and stay; the plugin agents were already model: inherit). Display-name drift retired at the remaining pin sites: the shared/cross_model_verification.md primary-model row is now generation-agnostic ("the inherited Claude Code session model" — it stops needing a per-release bump), the SessionStart announce + docs/PERFORMANCE.md(+zh-TW) cost anchors are provenance-labelled ("measured on Opus 4.x", order-of-magnitude) instead of asserting a two-generations-stale "$46 on Opus 4.7", and the disclosure-protocol e.g. list is refreshed. OpenAI verifier lineup unified gpt-5.4 → gpt-5.5 / gpt-5.5-pro: the citation judge already defaulted to gpt-5.5-xhigh while the verification doc still taught 5.4 — and the availability case-glob gpt-5.4*) rejected 5.5 ids outright; web_search-on-Responses support and pricing were verified first-party 2026-06-10, legacy gpt-5.4* ids remain accepted, and the cost table is re-anchored on gpt-5.5 ($5/$30 per 1M). Routing smoke recalibration (#133 fixtures): 8/8 routing-class pass on Fable 5 (clarify/proceed plus all three escape-hatch behaviors — byte-0 honored, mid-message rejected, case-insensitive accepted); two destination picks additionally required the Routing-Rules/MODE_REGISTRY context the manual protocol provides. The acceptance threshold in tests/fixtures/issue_133_routing/README.md is reworded from "100% on Opus 4.7" to "100% on the current primary model" so the definition stops drifting per release. Two bare anti-hallucination tails on the compliance surfaces are kept as annotated debt (high-stakes domain, silent failure mode — in-file harness-retirement annotations added). Deliberately out of scope, tracked separately: re-baselining the #272/#273/#274 model-behavior premises against the Fable 5 system card, and a negative-framing sample-reframe of the top-3 agent files at the next minor.

[3.13.0] - 2026-06-18 — Hook portability, provider-agnostic verification, guard correctness

Fixed

  • Write-scope guard: CLAUDE.md dropped from infra-protected globs (#459). Closes the residual half of #448/#449. #449 anchored infra self-protection on plugin_root (fixing #448 for the plugin install layout), but under the traditional git-clone + symlink-into-~/.claude/skills layout there is no CLAUDE_PLUGIN_ROOT, the plugin_root fallback resolves to the cloned repo root, and a user working IN that repo has plugin_root == workspace_root — so the bare CLAUDE.md / .claude/CLAUDE.md infra globs matched the user's own CLAUDE.md and re-denied it (the #448 bug, on a layout #449 cannot distinguish: home turf and clone-as-user are the same runtime condition). Fix (codex-consulted, Option 2): remove CLAUDE.md and .claude/CLAUDE.md from INFRA_PROTECTED_GLOBS. Unlike every other infra entry, CLAUDE.md is NOT load-bearing — it documents the guard binding, it is not the binding, so editing it cannot fail the guard open. The load-bearing enforcement files (guard script, manifest, hooks, plugin metadata, agent frontmatter, lint) stay protected on home turf; protecting ARS's own instruction doc from agent edits belongs in review/CI, not the write-scope guard. 3 new tests + 1 retargeted, mutation-verified (re-adding the globs fails the new tests); 73 guard tests pass.
  • Windows Python hook portability + graceful no-Python degradation (#454). The PreToolUse write-scope guard was wired as a bare python3 ".../ars_write_scope_guard.py". On Windows python3 is commonly a 0-byte Microsoft Store App Execution Alias stub, so the hook errored before the guard's own fail-safes could run and spammed the hook log every call. A new cross-platform launcher hooks/run_guard.sh (POSIX sh; hooks.json now invokes it via bash) finds a REAL interpreter — py -3 / python3 / python, each verified by a marker probe that must exit 0 AND print the marker (a stub that prints then exits non-zero is rejected) — then runs the guard as a supervised, time-bounded subprocess. Plan A graceful degradation (the guard is optional v3.10 hardening; ARS core needs no Python): if no real interpreter is found OR the guard subprocess misbehaves (non-zero, timeout, empty, or non-JSON / missing-key output, validated by a real json.load not a substring grep), the launcher emits a valid pass-through hook JSON and exits 0 — it never exits non-zero and stays silent on stderr on these degraded paths (PreToolUse is a hot path; per-call stderr is the spam #454 is about). Healthy-guard stderr advisories are relayed. New scripts/test_run_guard_launcher.py (21 tests, run from a temp plugin layout so the guard is always resolved from the launcher's own ../scripts/ — no production env back door); the hooks.json CI assertion now requires a line-anchored non-comment guard assignment AND the GUARD_OUT=$(... | run_bounded ... "$GUARD") exec call site rather than a bare filename substring (a comment or an echo-wrapped decoy no longer false-passes). New .gitattributes pins *.sh eol=lf. README documents the Git Bash prerequisite (without it Claude Code falls back to PowerShell, which cannot run the .sh launcher, so the guard is inactive and the hook logs per call instead of no-opping quietly).
    • Real-use findings: a two-model dual-track implementation review (codex + gemini, both POSIX-reproduced) hardened the launcher far beyond the original wiring fix, and the cross-model split was load-bearing — each model caught real bugs the other missed. Round 5 (codex) found the marker probe ignored exit status, the guard ran unbounded, and the JSON check was a substring grep. Round 6, once the tests exercised the REAL watchdog on a host with neither timeout nor setsid, found three fail-open bugs the back-door tests had masked: the no-timeout fallback fed the guard an EMPTY stdin (a real deny was silently lost — the guard was dead on any timeout-less host), an un-reapable orphan grandchild could wedge the $(...) capture, and the watchdog could false-report a timeout for a command that finished within the bound. The independent gemini track then refuted codex's first race fix (a successful kill does NOT prove the child is still alive — after wait reaps it the pid can be RECYCLED, so a blind kill could hit an innocent process and still false-flag a timeout) and added two fail-open findings codex missed: a predictable /tmp fallback when mktemp fails is a symlink-attack surface whose redirect failure reads as a broken guard, and the CI exec assertion was still gameable by an inline comment or an echo-wrapped call. Final state: stdin stashed on fd 3, stdout captured via a private temp file, timeout decided by a done-file handshake (the parent disarms the watchdog before reaping it; the watchdog kills/flags only while the done-file is absent — no pid-reuse race), mktemp failure degrades to pass-through instead of a guessable path, and the CI assertion is line-anchored. Orphan-grandchild leakage in the doubly-degraded no-timeout/no-setsid path, and a multi-megabyte payload held in a shell variable, are documented as accepted trade-offs (the real probe and guard spawn no grandchildren and ordinary hook payloads are small; the robust alternatives add temp-file lifecycle / symlink surface to a hot path).
  • draft_writer dual-phase static union documented + POSIX-safe Windows path matching (#451, #330). Documentation + portability fix for the draft writer's dual-phase static union; path matching made POSIX-safe for Windows checkouts.

Added

  • Provider-agnostic cross-model verification (#455). The cross-model verification layer now accepts OpenAI-compatible endpoints (MiMo, DeepSeek, self-hosted) alongside first-party OpenAI via a normalized compatible-verdict path (scripts/cross_model_verification/normalize_compat_verdict.py + check_cross_model_verification_sync.py). The grounded first-party OpenAI path is preserved and deliberately NOT routed through the standard OPENAI_BASE_URL (so an existing proxy user is never silently downgraded to the ungrounded compatible path — the grounded-proxy gap is tracked separately in #456). Design: docs/design/2026-06-16-453-provider-agnostic-cross-model-verifier-spec.md.
  • Opt-in Socratic adjacent-framing probe (STORM-borrowed perspective expansion) (#461; deep-research 2.10.0 → 2.11.0). When ARS_SOCRATIC_ADJACENT_PROBE=1 is set, the Socratic Mentor may, in exploratory sessions during Layer 1 (Problem Framing), surface ONE adjacent research framing the user has not raised — as a pure question ("an adjacent facet you haven't raised: — include it, or set it aside?"), never a proposed idea. Borrows the intent of Stanford OVAL STORM / Co-STORM (https://github.com/stanford-oval/storm): STORM's perspective discovery and Co-STORM's moderator inject framings adjacent to — but not directly answering — the current question to break local stagnation. ARS anchors framings in LLM internal knowledge (zero retrieval); majority-favour is a deliberate tradeoff for the novice target (mainstream-facet visibility helps researchers who haven't seen enough), with external-TOC retrieval left as a pluggable forward note. Hard-bounded by the Kong L2 verb test — never propose/substitute/rank/expand/select; surface-and-ask only, one facet at a time, max 2 per session ≥3 rounds apart. S4 (Scope Stability) is repurposed as an intensity knob (early scope-lock raises the tendency), reusing existing state — no new counter. One-push-then-retreat on decline; [ADJACENT-PROBE: ...] log tag flows into Stage 6 self-reflection (a high decline rate is the bias-visibility signal). Default OFF. No new agent / mode / schema — prose-layer only, same shape as the v3.5.1 Reading Probe. Gate fires exploratory (opposite of the goal-oriented Reading Probe). Two review gates, all findings adopted: a spec-compliance pass () and an independent semantic red-line reviewer that caught a Critical — the canonical GOOD example originally used "the teacher's mediating role," a hypothesis disguised as a category word (semantically identical to the BAD-propose row, and contradicting the agent's own WP14 flag); replaced with a true perspective phrase so the GOOD/BAD boundary is semantic, not grammatical. New lint scripts/test_adjacent_framing_probe_lint.py (10 tests, mutation-verified; adding the sibling env var also exposed and fixed an over-broad regex in test_reading_probe_lint.py that mis-flagged it as drift). See deep-research/agents/socratic_mentor_agent.md §"Optional Adjacent-Framing Probe Layer" and docs/design/2026-06-18-socratic-adjacent-framing-probe-spec.md.

Chore

  • Zenodo DOI added (#443, #434). CITATION.cff + README DOI badge wired to the Zenodo concept DOI.

[3.12.1] - 2026-06-15 — Reviewer-response triage modes (PR #433 integration)

Added

  • deep-research three-way-scan mode — a lightweight WHY/HOW/WHAT paper-comparison triage that sits between quick and lit-review. Produces a per-paper WHY/HOW/WHAT shortlist plus a cross-paper synthesis (common WHY, divergent HOW, strongest WHAT, unresolved gap), and escalates to lit-review / systematic-review for full coverage. (deep-research 2.9.4 → 2.10.0)
  • academic-paper rebuttal-audit mode — standalone advisory QA of an author's existing rebuttal/response draft against the reviewer comments (per-comment coverage table + gap list + risk flags for tone/evidence/misread). It generates nothing and, because a standalone invocation runs outside the pipeline, it explicitly suppresses Schema 11 emission / Material Passport writes / ready_to_submit status — enforced by a new check_rebuttal_audit_guard() lint with mutation coverage. Routed by input shape: both reviewer comments AND an existing draft → rebuttal-audit; comments only → revision-coach.
  • revision-coach scope extension — its trigger/docs now cover pushback/disagreement posture and non-journal scopes (conference rebuttal, grant-panel response, transfer-after-review).
  • /ars-3w and /ars-rebuttal-audit slash commands.

Credit

Integrated from @Yaobin29's PR #433. The original PR proposed a standalone reviewer-response skill; this release folds its genuinely-novel parts into existing skills as modes, per ARS's mode-based architecture. The rebuttal-audit mode rescues that PR's audit concept. Suite mode count 25 → 27 (still 4 skills).

[3.12.0] - 2026-06-08 — Kong auto-research feature track: experiment provenance, figure fidelity, cross-paper contradiction, partial-evidence decomposition

Added

  • Experiment Provenance Intake + claim→experiment alignment — a schema-first evidence-ledger layer for experiment-backed claims (#260, Kong et al. 2026 §3.3 + §7.4.3). ARS deliberately keeps experiment execution outside the pipeline; the scholar runs experiments externally and brings results back. This change adds the intake + alignment layer only — it does not run experiments, judge whether one was correctly designed/run/statistically-adequate/reproducible, auto-fill provenance, or require provenance for literature-only pipelines. Two blocks ship together. Block A — experiment_provenance[] intake array: a new optional Material Passport aggregate (shared/contracts/passport/experiment_provenance_entry.schema.json) where each scholar-entered entry carries a nested repro_lock (the same inline-object shape as the passport-level lock, re-declared not $ref'd because the source is inline prose, not a schema file), a planned_vs_executed[] record (each executed:false unit carries a gate-checked skip_reason), and negative_results[] / known_limitations[] arrays whose key must be present (an empty [] is well-formed and routes to a disclosure advisory; an absent key is malformed → gate FAIL, the absent-key rule ported from #261's C3). Block B — claim→experiment alignment: the claim manifest gains an optional per-claim planned_experiment_ids[] join field (parallel to planned_refs, minItems 1, optional-absent), and a new fourth ref_slug-less claim-finding aggregate experiment_alignment_results[] (experiment_alignment_result.schema.json) — alongside the existing uncited_assertions / claim_drifts / constraint_violations siblings — with an experiment-specific MECE verdict enum {ALIGNED, OVERSTATED, NOT_SUPPORTED_BY_PROVENANCE, PROVENANCE_INSUFFICIENT}. The verdict is produced by the integrity verification agent AT the gate (Stage 2.5 sampling / Stage 4.5 full), not by the citation-audit agent at the Stage 4→5 boundary — mirroring #261's Phase C3, so the row is emitted and gated in the same pass and the stage-ordering race (a verdict landing after the gate ran) cannot occur. A mixed-evidence claim carrying BOTH planned_refs and planned_experiment_ids is audited by both paths and the gate decision is worst-verdict-wins (an OVERSTATED experiment path blocks even when the citation path is SUPPORTED). experiment_id is frozen at intake (a post-intake rename is a re-intake event, not a silent edit). Seven new cross-array invariants land in scripts/check_claim_audit_consistency.py (JSON Schema cannot express cross-array integrity): EP-INV-1 (experiment_id unique/passport), EP-INV-2 (planned_experiment_ids resolve — doubles as the rename + forward-reference dangling-pointer guard), EP-INV-3 (experiment ids ⟹ empirical kind; mixed literature+experiment allowed), EP-INV-4 (declaration↔provenance symmetry), EP-INV-5 (declaration well-formedness when present: status enum / declared_by: scholar / non-empty declared_at — so a malformed declaration like status: "garbage" FAILs deterministically instead of slipping past the symmetry check), EA-INV-1 (finding_id unique), EA-INV-2 (alignment-row references resolve; a dangling experiment_id is a structural FAIL, never a PROVENANCE_MISSING verdict — that value is deliberately absent from the enum, so no fake judge fields are forced for a row where no judge ran). A persisted passport-level experiment_intake_declaration closes the anti-skip circularity with a fail-closed legacy boundary, split across two enforcement layers (stated precisely, not conflated): the lint deterministically enforces declaration↔provenance symmetry (EP-INV-4) and declaration well-formedness (EP-INV-5); the integrity gate (a Stage-1/Stage-4.5 check, NOT the lint) owns the ars_version numeric legacy decision and the declaration-presence FAIL — a passport is legacy_unknown (advisory) only with positive repro_lock.ars_version < #260-constant proof, everything else (including a passport with no repro_lock, or one with no ars_version) is treated as post-#260 so the declaration is REQUIRED and its absence FAILs at the gate, meaning a new run cannot dodge it by making its version unprovable. The ars_version numeric half is deliberately left at the gate layer (not promoted to a lint constant) because the #260 release version it compares against is frozen at ship time, not at intake. Literature-only pipelines therefore still emit a one-line no_experiments_declared declaration (no experiment_provenance[] needed). Producers taught in lockstep (schema-first writer-binding discipline): the three manifest emitters (synthesis_agent / draft_writer_agent / report_compiler_agent) emit planned_experiment_ids when an experiment backs a claim; the integrity agent gains a new disclosure-only Phase (D6) carrying the POSITIONING non-goal verbatim ("does not judge whether the experiment was correctly designed, run, statistically adequate, or reproducible by ARS"); the orchestrator carries experiment_alignment_results[] + the declaration forward; README intake detection sets the declaration. Drift guard: the repro_lock field set is single-sourced in scripts/repro_lock_validation.py (imported by both check_repro_lock.py and the new standalone check_experiment_provenance.py), with a drift test asserting the nested schema's required keys equal the shared constants. Three documented departures from the issue's literal text (each corrected after a first-party tracked-repo read): repro_lock is an inline-prose object, not a schema file, so "inherit repro_lock" means nesting the shape, not $ref'ing a non-existent file; the claim manifest had no experiment pathway, so the join is added, not assumed; and "Path X / Tier-1 required / writer-binding" are not named conventions in the tracked repo, so the discipline is described rather than cited by a name a reader cannot find. Schema + manifest edit + 7 lint invariants + standalone shape validator + drift guard + integrity/writer/orchestrator agent prompts + README mirrors + examples/passport_with_experiment_provenance.yaml (2 experiments, a mixed-evidence claim, an OVERSTATED alignment row) + full TDD suite (schema ±, fail-closed symmetry, declaration well-formedness, mixed-evidence two-row, verdict-derivation, mutation-verified non-vacuous invariants, reverse-invariant producer pins, drift, literature-only regression). The new schemas, the manifest field, and all seven invariants are additive and backward-compatible. Spec: docs/design/2026-06-08-260-experiment-provenance-intake-spec.md.
  • Cross-paper contradiction inventory — structured, inspectable enumeration in the synthesis layer (#262, Kong et al. 2026 §7.4.2). synthesis_agent already had prose-level contradiction handling (Anti-Pattern 3, the Step 3 Contradiction Resolution procedure, and the Contradictions & Resolutions table), but that prose narrative-discussed contradictions (including reconcilable-vs-irreconcilable verdicts) without making the set of assessed paper-pairs and the unresolved / checked-clear pairs enumerable for the scholar to confirm — the multi-paper relational-reasoning gap Kong et al. 2026 (arXiv:2605.18661 §7.4.2) document for research-synthesis systems. A new Step 3b — Cross-Paper Tension Inventory is added additive to (not a replacement of) the existing Step 3 prose: the agent emits a cross_paper_tensions[] markdown block — one entry per assessed candidate pair carrying pair_id, paper_a/paper_b, candidate_basis, overlap_topic, a_finding/a_evidence_pointer, b_finding/b_evidence_pointer, pair_assessment, resolution_status, an iff-resolved resolution_pointer, and scholar_confirmation. Prose-layer only — no JSON Schema, no lint invariant, no gold fixture (mirroring the #214 / #261 prose-layer decision, NOT the #213 schema-layer one): the producer (synthesis_agent) and the readers (the scholar plus the report/integrity LLM agents) all read prose, there is no deterministic downstream parser, and the judgment that matters — "is this a genuine contradiction vs. a conditional difference" — is irreducibly semantic, so machine-validating the YAML shape would prove field presence, not contradiction fidelity. This deliberately departs from the issue's literal acceptance (which read "schema adds contradiction_pairs[] block" + "calibration gold set accuracy ≥ 0.75"): the named "downstream consumers" (formatter, integrity_verification) are themselves LLM agents reading markdown — there is no machine consumer — so a schema would be the exact false rigor #261 rejected, and 20 LLM-judged pairs are too few and too nondeterministic across runs to wire as a hard CI gate (no calibration artifact ships in this change — any future or manual calibration should be recorded out-of-band with its model/date/prompt + a confusion matrix and stay non-blocking, never a pass/fail gate). The field model is corrected from the issue's non-MECE draft: the issue's single conflict_type ∈ {contradictory, conditional_difference, resolved_in_synthesis} folded conflict nature and resolution status into one enum and referenced an insufficient_overlap value not in it — these are split into orthogonal axes (pair_assessment ∈ {contradiction, conditional_difference, no_material_conflict, insufficient_overlap} × resolution_status ∈ {resolved_in_synthesis, flagged_unresolved, not_applicable}), and each side gains an evidence_pointer so a finding cannot be stated as free text the paper does not support. Candidate-pair scoping is a recall-limited heuristic, not an algorithm: an LLM agent does not execute an O(K²) enumeration, so the design states it as bounded candidate-edge generation (include a pair on shared RQ subtopic / shared construct / opposite finding direction / bibliographic coupling / scholar flag) with two honesty rules — bibliographic coupling is an inclusion signal only, never an exclusion rule (same-camp papers cite the same priors and tend to agree; cross-camp contradictions have low coupling), and cross-neighborhood pairs can be missed, so every inventory carries a mandatory Coverage Note stating the denominator and the explicit recall limitation and the agent must never write "all contradictions addressed." Inherits synthesis_agent's narrative-side discipline unchanged (advisory-only: the scholar makes the final call; the agent emits scholar_confirmation: pending, never self-confirms, simulates no audit step, and reads no entry frontmatter). No #111 dependency (that is a single boolean, per the issue's own correction). Adds examples/contradiction_pairs_example.md (6-paper remote-work synthesis covering a genuine unresolved contradiction, a resolved conditional difference, an unresolved conditional difference (so both resolution states of one assessment are shown), a no-material-conflict pair, an insufficient-overlap pair, and a Coverage Note that names the still-unpaired cross-neighborhood paper). Agent-prompt + output-template + doc example only; no schema, lint, or executable change.
  • Figure/Table Fidelity Gate — the visual analog of the §F.3.2 partial-evidence trap (#261, Kong et al. 2026 §3.4). The VLM Figure Verification Protocol checked "does the rendered figure match the source data?" (a faithful-rendering check) but could not check "does the caption's interpretation follow from the data, and does the manuscript cite this artifact for a claim it actually supports?" — a figure can render perfectly while its caption overstates the data or the manuscript cites it for an unsupported claim (Kong et al. 2026, arXiv:2605.18661 §3.4). This is the visual counterpart of the prose partial-evidence trap addressed for citations in #213 and for review synthesis in #214; same trap, different artifact type, separate implementation. Prose-layer only — no JSON Schema, no lint invariant, no gold fixture (mirroring the #214 prose-layer decision, NOT the #213 schema-layer one): the figure_table_trace[] producer (visualization_agent) and consumer (integrity_verification_agent) are both LLM agents reading a markdown Figure Package, so there is no deterministic downstream parser and machine-validating the YAML shape would be false rigor. academic-paper/references/vlm_figure_verification.md gains a Figure/Table Trace section defining a figure_table_trace[] block — one entry per figure (or manuscript table that has an entry) carrying all six required keys — artifact_id, source_data, transformation ({script, hash} OR a precise manual-derivation pointer — vague values like "computed manually" are treated as untraceable), caption_claim, supported_manuscript_claims (each as claim text + optional locator, not a bare id, since the visualization agent can run before the draft's claim manifest exists), and limitations (present even when []). visualization_agent.md emits the block in the Figure Package (new Step 6.6) and integrity_verification_agent.md Phase C gains C3. Figure/Table Caption Fidelity running at Stage 4.5: entry well-formedness (a malformed entry missing any of the six keys short-circuits to FAIL) plus four fidelity checks — trace completeness, caption-claim support (does the interpretation follow from data+transformation, with compound captions decomposed into atomic sub-claims using the #213 idea as prose guidance only, no PARTIAL verdict / sub_claim_breakdown imported; an entry takes its weakest sub-claim's verdict), bidirectional manuscript-claim linkage (each listed claim must reference the artifact and not overstate it, AND every substantive manuscript use of the artifact must be listed — incidental/structural mentions exempt), and limitation visibility (a known limitation must reach caption/Discussion/Limitations). Severity is split, not blanket-advisory: a caption that contradicts the data, an untraceable claim-bearing artifact, a missing/overstated manuscript link, or a dropped known limitation FAIL (block); only uncertainty signals are advisory — an empty limitations: [] emits a named [FIGURE-LIMITATIONS-EMPTY] note (never a silent pass) and a legacy figure with no trace surfaces a trace-unavailable note. At Stage 4.5, an updated Figure Package with no figure_table_trace[] (or one omitting an entry for a figure it contains) is a FAIL ("caption fidelity not verified"), so the check is not trivially skippable; a legacy figure with no Figure Package at all is the advisory case. C3 inherits the existing C1 data-cross-referencing layer (it does not re-render figures — that is VLM — or re-verify raw data — that is C1); its new coverage is interpretation and linkage. Adds examples/figure_table_trace_example.md (3-figure + 1-table ML ablation walkthrough covering a normal trace, a decomposed compound caption, and the empty-limitations advisory). Reference + agent-prompt/protocol text + doc example only; no schema, lint, or executable change.
  • Sub-claim decomposition before citation judgment — the citation-layer half of the §F.3.2 partial-evidence trap (#213). The unified citation judge (academic-pipeline/agents/claim_ref_alignment_audit_agent.md) emitted exactly one verdict per citation, so a compound claim ("X rose AND the effect held across Y") whose source supported one sub-claim but not the other was collapsed to a single binary check and the unsupported sub-claim was silently lost — the largest correctness-error class documented in AI meta-review (Kim et al. 2026, arXiv:2605.20668v1 §F.3.2). The judge now runs a required Step 0: decompose the claim into atomic sub-claims and judge each independently before choosing the citation-level verdict. A new prompt-layer PARTIAL verdict (supports some sub-claims, not all; no active constraint violated) is normalized at Step 6 to judgment=UNSUPPORTED, defect_stage=source_description, routing the unsupported sub-claim through the same gate-refuse path a fully-unsupported claim takes so partial support is never accepted as full resolution. Baseline correction: the issue body proposed adding PARTIAL to the schema judgment enum; first-party reading showed that is the wrong baseline — PARTIAL (like the existing VIOLATED) lives at the prompt layer, NOT in the schema enum, so the 18 cross-field invariants and the allowed-(judgment, audit_status, defect_stage)-matrix stay untouched (the normalized triple was already in the matrix). The decomposition is persisted in a new additive optional schema field sub_claim_breakdown[] on claim_audit_result (pre-#213 entries validate unchanged); its presence — not the defect_stage value — is the machine-readable partial-support signal for downstream consumers. A new lint invariant INV-19 pins the full normalization (breakdown present ⟹ judgment=UNSUPPORTED AND defect_stage=source_description AND true-partial: ≥2 items with ≥1 SUPPORTED AND ≥1 valid non-SUPPORTED sub_verdict), mutation-verified to discriminate. Malformed PARTIAL (breakdown absent / <2 items / not true-partial) takes the audit_status=inconclusive [partial_breakdown_malformed] path, never a silent bare UNSUPPORTED. Calibration gains 5 partial-support gold fixtures + a partial_support subset metric (scripts/claim_audit_calibration.py): because partial fixtures carry expected_judgment=UNSUPPORTED, a judge that stops decomposing and emits bare UNSUPPORTED passes the aggregate FNR gate; the subset metric counts a partial fixture as passed ONLY when the judge emits UNSUPPORTED AND a well-formed true-partial breakdown, so the regression surfaces as miss_rate > 0 while the aggregate stays green. The synthesis-layer sibling (#214) is out of scope. Schema + lint + judge-prompt + calibration + protocol-doc; the schema field and INV-19 are additive and backward-compatible.
  • Sub-claim inventory before consensus in the editorial synthesizer — the synthesis-layer half of the §F.3.2 partial-evidence trap (#214). The synthesis-layer sibling of the citation-layer #213. The editorial synthesizer (academic-paper-reviewer/agents/editorial_synthesizer_agent.md) aggregated consensus over a whole weakness bundle, so a compound weakness whose sub-claims carried different reviewer support was collapsed to one verdict and the minority sub-claim was lost — the single largest correctness-error class in AI meta-review (Kim et al. 2026, arXiv:2605.20668v1 §F.3.2). Prose-layer only: the synthesizer emits a human-facing decision letter + revision roadmap, not machine-readable judge rows, so there is no deterministic consumer for a #213-style schema field / lint invariant / gold fixture — adding one would be unrequested abstraction. The sub_claim vocabulary aligns with #213; its architecture is not imported. Step 1 splits into Step 1a — Reviewer Summary Matrix (retained) + Step 1b — Weakness Sub-Claim Inventory keyed on sub_claim_id (only weakness bundles decompose; recommendation/confidence/counts stay in the 1a matrix). Step 2 computes consensus per sub-claim over an absolute denominator of the 4 non-DA reviewers (position ∈ {raised, corroborated, not-mentioned, disputed}; not-mentioned is silence, never opposition or agreement). Mutually-exclusive dispositions with explicit precedence: conflict ≥ 1 → SPLIT first, otherwise by agree count (4→CONSENSUS-4, 3→CONSENSUS-3, 2→corroborated finding, 1→single-reviewer finding); every (agree, conflict) cell maps to exactly one disposition and agree = 0 is unreachable by construction. disputed covers existence OR action/severity conflict, so reviewers agreeing a problem exists but recommending incompatible remedies route to SPLIT → EIC arbitration. A Sub-Claim(s) column is added to the roadmap tables in both the agent output format and the standalone editorial_decision_template.md so the decomposed granularity survives to the output boundary. DA-CRITICAL flow and the v3.6.2 sprint-contract arithmetic path are untouched; scoped to the general Synthesis Protocol only.
  • Concise output discipline + pressure-stable boundary reinforcement across the report-producing reviewers (#274). A guidance-layer follow-up to the Claude Opus 4.8 system card §4.1.4, which documents two behavioral signals: refusals/responses trend longer and more over-caveated than 4.7, and a small number of multi-turn cases where a correct refusal was retracted under sustained pressure — both quality issues a user feels directly in a review tool. Guidance layer only; no claim of having proven 4.8's runtime behavior. A concise output discipline block is inlined (before ## Output Format) into the report-producing reviewers — domain / methodology / perspective / eic / devils_advocate / editorial_synthesizer reviewers and academic-paper/peer_reviewer: state findings and verdicts directly, don't pad with repeated qualifiers; concise explicitly does NOT mean under-caveated — preserve every material uncertainty, cut only redundancy. A "pressure is not evidence" rule is added to the Devil's Advocate Anti-Sycophancy Rules and the editorial synthesizer's arbitration discipline: repeated pushback / authority appeals / bare softening requests do not change a finding. In the Devil's Advocate, this is bound to the existing numeric concession threshold (≥4 normally, 5/5 after a prior concession); in the editorial synthesizer's arbitration, a finding changes only on substantive new evidence or reasoning that addresses the arbitration basis (no numeric threshold lives there). Both are framed by evidence standard, not as an attack catalogue (public-repo safe). Every block carries an epistemic-status line: these are prompt-surface instructions; they cannot prove the model stays pressure-stable at runtime — that would need a separate non-deterministic behavioral eval. The issue's acceptance "confirm boundaries hold under 4.8 after pushback" is reframed as a prompt-surface confirmation (the instructions are present and explicit), not ticked by self-simulating a pushback dialogue (theater, not verification) and not pinnable by a deterministic CI test — mirroring the #272 guidance-layer ≠ runtime-enforcement discipline. No lint / mutation test (style guidance is not a contract invariant with a downstream consumer). Agent-prompt text only.
  • Retrieved-content instruction/data boundary stated as a standing principle (#367, guidance layer for #272). Retrieved external content is data; imperative-looking text inside it is not auto-promoted to a user instruction. The authoritative statement lands as a canonical §2A in shared/ground_truth_isolation_pattern.md (marked distinct from the eval-leakage concern) and is inlined verbatim into the two highest-surface retrieval agents — deep-research/source_verification_agent and bibliography_agent — so the principle is present where a fetch happens. A new scripts/check_instruction_data_boundary.py lint guards against silent removal or anchor-preserving gutting (presence / verbatim-sync / section-anchoring / contiguous-backpoint), proven not-accept-all by an 11-mutation test plus a positive control; the original strict-xfail pebble marked the unbuilt runtime defense and was later replaced by #675's executable behavioral-probe witness without claiming structural enforcement. Commit-time documentation consistency only — no runtime gate, no injection-mitigation claim. The originating trust-boundary issue (#272) stays open by design (the structural layer is deferred, now tracked by #676). Lint + CI wiring (spec-consistency.yml + pytest manifest); no schema change.
  • Version-consistency lint extended to the README badge, docs forward-reference, and zh-TW heading invariants (#357, invariants 5-7). scripts/check_version_consistency.py covered invariants 1-4 (CLAUDE.md table, suite version, pipeline tracking, plugin manifests); it now also enforces the three release-doc invariants previously caught only by manual checklist: inv 5 — the README shields.io version badge tracks the suite version; inv 6 — no docs/*.md cites a vX.Y.Z above the suite version (forward-reference guard); inv 7 — version-bearing H2 headings stay in lockstep between docs/<name>.md and docs/<name>.zh-TW.md (plain headings may differ; only version tags pair, compared as multisets so a dropped one-of-a-pair heading is caught). Version-token regexes use a trailing negative lookahead so prerelease / 5-segment tokens (v3.12.0-alpha, v3.11.1.2.3) are dropped rather than partial-matched. Also removes the docs/PERFORMANCE.md cross-model onboarding section (en-only; aligns the en/zh-TW pair). TDD with 11 new test methods plus broadened aligned-fixture coverage, each invariant mutation-tested (stub to accept-all → matching test fails).
  • ARCHITECTURE.md component-version markers now policed by lint (#345, invariant-4 gap). scripts/check_spec_consistency.py policed version markers in the README (×4 langs), .claude/CLAUDE.md, MODE_REGISTRY.md, and SKILL.md — but not docs/ARCHITECTURE.md, where six "current academic-pipeline component version" strings were missed by the v3.11.1 bump and caught only by a manual first-party sweep (#343/#344). A new check_architecture_component_version() parses the suite version from .claude/CLAUDE.md and asserts the six current-component markers equal it (the mermaid orchestrator node + the component table row + the four stage rows). It anchors on the academic-pipeline <ver> component pattern and never inspects the timeline block, so a stale current-component marker fails while a feature-history marker (vX.Y.Z : <feature>, which records which version shipped a gate and must not be bumped on a patch) is left alone — the distinction a naive v3.x grep would corrupt. The version regex captures the repo's full 4-component grammar (the suite shipped v3.9.4.2) with a hard right boundary so a 3-component marker can't partial-match inside a longer one, and the component/stage row scan is anchored to markdown table rows so a narrative provenance mention isn't wrongly policed. Wired into spec-consistency.yml; 7 tests (aligned passes / stale component fails / stale timeline marker does NOT fail / missing markers fail / 4-component edge cases).
  • Same-family / rubric-aware calibration epistemic note (#273, Claude Opus 4.8 system card §6.3.7 / §6.6.3). An interpretive, doc-only follow-up to the system card's report of modest, partly-unverbalized grader-awareness signals (the model sometimes optimizes toward what a rubric appears to reward). ARS leans on rubric / gold-set judging (reviewer calibration, the citation-claim judge), so this affects how calibration numbers should be read — not what the suite does. Zero detection / mitigation claim: ARS does not and cannot detect or correct grader-awareness (the system card's own point is that it can be unverbalized); the only honest claim is interpretive. All changes land in calibration_mode_protocol.md "Failure cases this mode does NOT fix" plus a one-line pointer in integrity_verification_agent.md. An umbrella "same-source evaluation risk" framing names two forms — the existing factual form (same-source hallucination — fabricated references; canonical in the Anti-Hallucination Mandate, unchanged) and a new behavioral form (same-family rubric optimization), cross-referenced both ways; the integrity block's WebSearch counter-rules are explicitly scoped to the factual form only and are not edited to imply they mitigate rubric-aware judging. An epistemic note states that under same-family / rubric-aware judging the measured calibration error is a possible under-estimate, not a ceiling. A cross-model positioning clarification resolves the doc's own opt-in-vs-default-on tension (cross-model is opt-in "for best results" in ordinary reviewer / judge paths; calibration mode is the explicit default-on exception once invoked; absent cross-model is warn-and-continue, never a gate; the consent / privacy boundary for sending a manuscript to another provider is preserved). A single-model paraphrase spot-check is documented but honestly de-powered — reword the rubric and re-judge, stated plainly to reveal only surface wording sensitivity, unable to detect unverbalized grader-awareness, and no proof the judgment is correct (no score, no threshold, no gate). No schema, no lint, no gate, no calibration-threshold change.
  • Kong auto-research META closeout — negative scope + Tier D design lessons (#255, Kong et al. 2026). Closes the Kong et al. auto-research survey META after every feature sub-issue (Tier A #256#259, Tier B #260#262, Tier C #263, Schema follow-ups #266/#268/#269) had merged; the two remaining closing conditions were documentation-only and defined the project's negative scope. POSITIONING.md gains a "Rejected mechanisms (autonomous-research anti-patterns)" section placed after "What this is not", enumerating the five autonomous mechanisms ARS does not do — end-to-end pipeline, idea-generation agent, Paper2X auto-generation, autonomous experiment execution, wet-lab automation API — each with a Kong anchor and, for the three that abut shipped features, an operationally-checkable CONSIDER-vs-REJECT line (idea-generation ≠ shipped #257 wording advisory; Paper2X auto-gen ≠ fidelity audit; autonomous experiment execution ≠ shipped #260 provenance intake). Two Tier D design-lesson docs land under the existing docs/design/…lX… convention (not a new directory): L1 frames copilot-vs-auto-research as a research-state-authority review test ("does this let ARS create / select / execute / advance a research object of record without a scholar-authored seed or confirmation?"), and L2 sharpens the advisory-vs-idea-generation line for research questions with a verb test, cross-linked from POSITIONING.md and from #257. Verification notes split a verifiable claim (no autonomous mechanism in first-party ARS today; #257 / #260 are advisory / provenance gates) from a design commitment (a recorded boundary and review criterion, not a runtime guarantee). Documentation only — no schema, agent, or lint change.

Fixed

  • Originality weight reconciled to 20% across reviewer reference docs; rubric weights now lint-policed (#396). review_criteria_framework.md stated Originality at 15% (plus a 7-dimension weighted formula and its own score-to-decision mapping) while the operative scoring source — quality_rubrics.md, which the peer-review report template instructs reviewers to score against — and academic-paper/SKILL.md rule 14 both say 20% with a 5-dimension aggregate. The framework doc no longer restates any number: its dimension headers drop the weight suffixes (qualitative level descriptors stay) and §4 defers weights, formula, and decision mapping to quality_rubrics.md by name, noting that Literature Integration and Significance & Impact are reviewer-specific optional dimensions outside the numerical aggregate. Recurrence is guarded by a new lint, scripts/check_rubric_weight_consistency.py: quality_rubrics dimension-header weights must match its own aggregation-formula terms, the weights must sum to 100%, SKILL.md rule 14 must agree, and the framework doc must not restate a weight (Weight NN% / (NN%) both fail). Mutation-verified on all four invariants; wired into spec-consistency.yml + the CI pytest manifest. Surfaced by codex during cross-model review of the 2026-06-10 researcher-blindspot audit (F-14).
  • Score-trajectory scale contradiction reconciled to 0-100 (#399, found during the #396 reconciliation). shared/handoff_schemas.md declared score_trajectory scores as "1-5 scale" while every producer and consumer is 0-100: the report template scores 0-100 per quality_rubrics.md, and the canonical Early-Stopping Criterion is explicitly "delta < 3 points on the 0-100 rubric" (academic-pipeline/SKILL.md). The 1-5 comment is a pre-v1.4 fossil — the reviewer changelog (2026-03-08) records "Dimension Scores upgraded from optional 1-5 to required 0-100". Schema comments now say 0-100 (scale sourced from quality_rubrics.md; dimension names still from the framework doc), and the trajectory protocol's Stage 6 example — which mixed both scales in one table (1-5 scores, a "-0.2 within tolerance" verdict, and an "overall delta = 4" that matched neither) — is rebuilt on 0-100 with internally consistent deltas and verdicts. Exposure note (per the #399 acceptance): the thresholds themselves were never wrong — they were always defined against 0-100 in SKILL.md; the risk was a consumer reading only handoff_schemas.md, whose 1-5 trajectories would make regression detection (delta < -3) near-unreachable and early-stop (delta < 3) near-always-on. No evidence either check ran on 1-5 data (no real re-review artifact set exists post-v2.7; see #395). The reviewers' Confidence Score [1-5] is a deliberately separate axis and is unchanged.
  • Cross-model verifier now actually grounds its lookups, and an ungrounded result can no longer be laundered into VERIFIED (#346). shared/cross_model_verification.md told the cross-model verifier to "search the web to confirm," but the shipped OpenAI / Gemini API call patterns wired in no web-search tool — so a copied example produced a verifier that was told to search but could not, answering from parametric memory and confidently returning VERIFIED. For a hallucinated-citation gate that is the worst failure (a false VERIFIED manufactures confidence), and it shares the generating model's exact failure mode — fluent-but-wrong from memory — for the one task (existence lookup) where grounding is the entire point. Two-part fix, both at the API-pattern layer: (1) the OpenAI pattern moves to the Responses API with the hosted web_search tool and the Gemini pattern enables the google_search grounding tool, so "search the web" is executable; (2) both patterns gate the verdict text on proof a search ran — they emit NOT_SEARCHED and discard the text when the API returns no grounding evidence (an OpenAI completed web_search_call item / a Gemini response whose groundingMetadata carries webSearchQueries and groundingSupports tying the verdict text to retrieved chunks), and a VERIFIED carrying no supporting source URL/DOI is downgraded to NOT_SEARCHED. The protocol moves from batched (≤5 refs/call) to one grounded call per reference so the grounding evidence maps 1:1 to each verdict (a single grounding trace on a 5-ref response proves something was searched, not that each reference was) — a deliberate cost-for-provenance trade (a 60-ref paper samples 30% capped at 15, so ~15 grounded integrity calls, documented in the cost table). NOT_SEARCHED is a new status distinct from a transport failure: a transport failure (non-2xx HTTP — [CROSS-MODEL-ERROR]) means "no cross-model opinion" (fall back to single-model); a NOT_SEARCHED (2xx, but no grounding evidence) means "an opinion we have decided not to trust," counted separately and surfaced for re-run or human review, never as agreement with a Claude VERIFIED. academic-pipeline/agents/integrity_verification_agent.md (the consumer) is aligned in lockstep: its behavior summary drops the stale "batches of 5", adds the NOT_SEARCHED / ungrounded handling, and splits transport-failure graceful-degradation from the NOT_SEARCHED path. Surfaced during the 2026-06 harness-retirement audit (#301) by a second-model cross-check pass and filed as a live correctness gap, not a harness-retirement item. Documentation + agent-prompt/protocol text only; no executable script or schema change.
  • Cross-model grounding guards are now behavior-tested, and a fail-open in the Gemini source extractor is closed (#349, follow-up to #346). The #346 grounding guards shipped as bash/jq inside shared/cross_model_verification.md with no automated test — a future edit to the jq, or a provider response-shape change, could silently stop it failing closed (the exact silent-false-VERIFIED class the guard exists to prevent). The contract-bearing jq is extracted into canonical files under scripts/cross_model_verification/ (5 filters: OpenAI search-guard / text / sources, Gemini grounded-guard / sources), the documented bash now loads them via jq -f instead of inlining, and scripts/test_cross_model_verification_guards.py runs each filter against synthetic fixtures (grounded → extracts supported sources; from-memory / non-grounded → NOT_SEARCHED with blank sources), with two mutation tests proving the fixtures discriminate a working guard from an accept-all / naive one. Fail-open fix (malformed-response hardening): the source extractors trusted the shape and types of the model's grounding metadata. Several malformed-but-well-formed-JSON responses could fabricate a source (defeating the blank-source downgrade and resurrecting a false VERIFIED) or crash jq: a negative groundingChunkIndices silently selected a chunk from the end of the array; a string index, a groundingChunks/groundingSupports arriving as a string/object instead of an array, a Gemini chunk uri or an OpenAI url_citation.url that is a number/bool/object — each either crashed or surfaced a non-URL value as a "source". The canonical filters now fail closed on all of these: indices must be in-range non-negative numbers (select(type=="number" and . >= 0 and . < ($chunks|length))), the grounded-guard requires webSearchQueries/groundingSupports to be non-empty arrays (not merely truthy length, which strings/objects also have), every container on each extraction path is array-normalized before it is iterated or indexed (OpenAI outputcontentannotations; Gemini candidatesgroundingChunks / groundingSupports / groundingChunkIndices) so a container arriving as an object can't have its values surfaced, and extracted URLs are filtered to non-empty strings — so any malformed response yields blank sources → NOT_SEARCHED rather than a fabricated or crashing result. A doc-sync lint (scripts/check_cross_model_verification_sync.py) pins that the doc keeps wiring every canonical filter via jq -f and retains the NOT_SEARCHED / CROSS-MODEL-ERROR branches (with REQUIRED_FILTERS cross-checked against the on-disk .jq set so a new filter can't escape the lint). Both the test and the lint are wired into the CI pytest manifest + spec-consistency.yml, which now also ensures jq is present on the runner. Documentation + test/lint/CI only; no agent-prompt or schema change.
  • Cross-model Gemini guard is rederived from the source extractor; malformed array elements no longer crash the OpenAI filters (#351, post-ship review of #349). The post-squash review of #349 surfaced that the Gemini guard and the source extractor were two parallel jq programs asserted to agree, so each round found a new input where they diverged: a groundingSupports linking to no valid chunk (empty / negative / string / out-of-range / fractional index), a multi-candidate response where the guard's any-candidate scan passed on a grounded candidate while the extractor read the unsupported candidate[0], or a non-string uri. In each, the guard passed while the extractor returned blank — and the blank-source downgrade only rescues VERIFIED, so an ungrounded NOT_FOUND / MISMATCH could be trusted as grounded. The fix is structural: gemini_is_grounded.jq now embeds the exact same candidate[0] extraction gemini_sources.jq performs and passes iff it yields ≥1 source AND a real webSearchQueries signal is present — so the safety invariant guard-pass ⟹ at least one source extractable holds by construction for every input shape, not by two predicates kept in sync by hand. (The guard is intentionally stronger than "has a source": a chunks-but-no-search response fails it.) Separately, openai_text.jq no longer crashes join on a non-string text, and all OpenAI filters type-check each array element as an object before reading .type, so a malformed element (output: [5]) is skipped rather than crashing. +17 behavior tests across the new invariant, multi-candidate / fractional / non-string-uri cases, and the array-element-crash paths (guards 27→44). Every hole and fix verified first-party.
  • Judge-verdict cache key partitioned by prompt version so a prompt revision invalidates stale entries (#361). The judge-verdict cache key included judge_model but no prompt-version component, so a judge-prompt revision (e.g. #213's Step-0 sub-claim decomposition) did not invalidate stale entries — a verdict cached under the old prompt was still served until the TTL expired, silently bypassing the new prompt logic (a pre-existing cache-key design gap surfaced as P2#1 in the #355 post-squash review; affects every prompt revision, not just the decomposition path). _cache_key gains a prompt_version component kept separate from judge_model (independent axes), and invalidation keys on JUDGE_PROMPT_SHA256 — the SHA-256 of the canonical judge-prompt section, the single source of truth — so any prompt edit changes the key and invalidates stale entries with no reliance on a human bumping a label (JUDGE_PROMPT_VERSION is a decoupled human-readable label for logs/diffs only). Fail-CLOSED on unknown version: when the caller declares the prompt version None, the pipeline binds a run-local component (__unknown__:<audit_run_id>) so a stale entry is never served across an unknown-version boundary (cross-run hits disabled; within-run dedup for repeated citations preserved). A CI backstop scripts/check_judge_prompt_version.py hashes the canonical section (between the JUDGE-PROMPT-CANONICAL markers) and fails if it drifts from the pinned hash, forcing a re-pin in the same change (wired into spec-consistency.yml + the pytest manifest). The agent-prompt contract and lint docstring — which described invalidation as keyed on the JUDGE_PROMPT_VERSION label while the pipeline already falls back to the SHA256 — are re-attributed to the SHA256 fingerprint so a downstream implementer following the contract can't re-open the bug. RED→GREEN + mutation-verified.
  • Judge-supplied rationale bounded on success-path rows + null rationale guarded (#360). Judge-supplied rationale on success-path rows (completed + constraint_violation) is now bounded to the schema maxLength=2000 via a shared length-budgeting choke point, and a non-string (null) rationale degrades to the default instead of aborting the audit run. RED→GREEN + mutation-verified.
  • Failure-rationale bounding + PARTIAL gold-fixture requirement — two #213 sub-claim-decomposition gaps (#359, #213 follow-up). Two correctness gaps in the new PARTIAL machinery (neither reachable on pre-#213 inputs), surfaced by the #355 post-squash integration review and confirmed first-party. (1) The malformed-PARTIAL fallback could emit a schema-invalid row: the parse error embedded the offending breakdown's repr in detail, which became the fallback row's rationale and could exceed the claim_audit_result maxLength=2000. Fixed by bounding detail at a single choke point — a shared _AuditInvocationError base whose __init__ clamps detail so the "{fault_class}: {detail}" rationale always fits (budgeted against the widest fault-class prefix); JudgeInvocationError / RetrievalInvocationError inherit it. (2) Calibration could silently skip the atomic-decomposition metric: validate_gold_set did not require an expected_prompt_verdict=PARTIAL fixture to carry non-empty expected_sub_claims, so _breakdown_covers_expected early-returned True and scored miss_rate=0 for any generic breakdown; a new rule (e) rejects such a fixture at ingestion (fail-closed). RED→GREEN + mutation-verified for both.
  • Eval gold tuple 052 removed — a fabricated citation was mislabeled as a genuine unindexed paper (#250). Gold tuple 052-valid-unindexed-regional-paper was labeled fabrication_intent: false and its expert-verdict notes asserted it was a "GENUINE … real regional, non-English-indexed agronomy paper" (Sembiring & Ginting 2023, Jurnal Penelitian Pertanian Regional). First-party verification across all four resolvers (Crossref / OpenAlex / Semantic Scholar / arXiv) plus DOAJ, OpenAlex Sources, Crossref Journals, and general web search found no evidence the paper or the journal exists — a fabrication labeled as genuine, exactly the failure this repo exists to detect, and a direct violation of #250's closing condition (which requires a first-party-verifiable real-but-unindexed source). Functionally the tuple was redundant: the harness reduces pre-recorded resolver_outcomes (it does not live-query), and tuple 051 (the OQ-5 by-design false-negative, a no-identifier fabrication) already exercises the identical title-only-unmatched → unresolvable reducer path. Removes the tuple; expected_outcomes.json drops the 052 entry (51 entries); manifest.yaml sample_n 52→51 + drops the valid_unindexed distribution row; check_evals_gold_set.py drops valid_unindexed from KIND_ENUM; citation_verification_summary.py + test comments drop 052 references (expert-concordance 12→11, unresolvable support 7→6), realigning manifest/tests/summary with the gold-set README's already-described 51-tuple set. #250 stays open — the verified-real-but-unindexed canary is still genuinely unfilled; only a mislabeled synthetic proxy was removed.
  • ACL/EMNLP disclosure rows regrounded to the ACL Admin Wiki canonical source (#242). The ACL disclosure row pointed at the 2023 conference blog (still live, HTTP 200) but its content had drifted from ACL's current Exec-approved policy. The Admin Wiki — which ARR / EMNLP 2026 link to for current paper-integrity guidance — places disclosure in the Acknowledgements section and graduates it by use type, contradicting the old row's "dedicated Use of AI Assistance subsection". First-party verification: the Admin Wiki returns HTTP 200 via browser navigation with the full "Guidelines for Generative Assistance in Authorship" section present (the 418 reported in #242 was a curl-UA challenge, not a stable block; the repo has no CI link-checker, so the humans-vs-tooling URL tension recorded in #242 does not apply). The ACL row's Source URL → Admin Wiki anchor (access date 2026-06-07) with summary / required phrasing / disclosure location / prohibited uses / authorship / notes regrounded from the first-party wiki text and graduated per its clauses af (language-only and short-form input not disclosed; literature search needs no special disclosure but normal citation-accuracy rules apply; low-novelty text and AI-suggested ideas disclosed); the EMNLP sibling row → the EMNLP 2026 Paper Integrity Policy page (which defers to ACL's guidelines) and is consolidated to "see ACL row"; disclosure_mode_protocol.md prose aligned to Acknowledgements. An independent cross-model faithfulness pass against the first-party pages corrected two fluent-wrongness overstatements (the literature-search no-disclosure bucket; an EMNLP "adopts wholesale" claim).
  • Stale Opus 4.7 primary-model strings retired + repro_lock run-time fields documented as placeholders (#347). The 2026-06 harness-retirement audit (#301) found the agent prompts carry zero expired scaffolds, but two shared/ files still pinned Opus 4.7 as the primary model after the 4.7→4.8 migration, and a repro_lock example hard-coded run-time snapshot values that readers copy verbatim. shared/cross_model_verification.md: primary model → Opus 4.8, with the primary "API ID" cell now reflecting that it is the inherited Claude Code session model rather than asserting an unverified claude-opus-4-8 id string (cross-verifier ids gpt-5.4* / gemini-3.1-pro-preview confirmed current and left concrete), plus a note documenting why temperature is 0.1 (deterministic fact-check), closing the "undocumented sampling override" read. shared/artifact_reproducibility_pattern.md: the repro_lock example block uses placeholders for the three run-time snapshot fields (ars_version, model.id, s2_api_protocol_version) so a copy-paste records the actual run, not a stale literal (feature-introduction labels like v3.3.5+ stay concrete). examples/passport_with_repro_lock.yaml left unchanged — a self-consistent historical snapshot, not a stale current-marker.

[3.11.1] - 2026-06-06 — Post-ship correctness, hardening, and provenance fixes (#182 follow-up)

A patch release rolling up the post-ship advisory fixes surfaced after v3.11.0: a cross-model consent-gate extension to the integrity + collaboration paths (#322), a per-entry backfill parallelization (#138), and seven correctness/hardening fixes across the citation-existence gate, the v3.10 policy layer, the eval harness, the domain evidence profiles, and the #310 security-boundary edge cases (#323/#327/#328/#329/#331/#332/#333). No new features and no breaking schema changes. One API note: the #332 verify_citation/ verify_passport signature gains required keyword-only parameters. This is a fix to a contract-violating code path that first shipped in v3.11.0, not a deliberate signature revision — the old signature emitted a schema-invalid ref_slug: null, so any v3.11.0 caller relying on it was already producing contract-invalid output. The only in-repo callers (the CLI + the internal verify_passportverify_citation call) are updated in lockstep; see the #332 entry below for the full C-V4 rationale.

Security

  • Cross-model consent gate extended to the integrity-verification and collaboration-depth paths (#322). The explicit-consent gate that fronts every ARS_CROSS_MODEL upload — established for the two Devil's Advocate paths in #310 — now also fronts the two remaining agent paths that send user-derived material to an external provider on the env var alone: integrity_verification_agent (sampled citation/reference metadata) and collaboration_depth_agent (raw dialogue turns, which can carry the user's private reasoning and unpublished material). The gate is also added at the pipeline_orchestrator_agent re-dispatch point so the observer's agent-internal gate cannot be bypassed at the orchestration layer (defense in depth). All three mirror the #310 wording: no automatic send, explicit user consent identifying provider + model + content class, [CROSS-MODEL-SKIPPED] + single-model fallback when consent is declined, and a backpointer to shared/cross_model_verification.md. The collaboration_depth_agent advisory-only / never-blocks contract is preserved — the gate gates only the upload, never the observer's scoring role. Agent-prompt text only; no schema or script change.

Performance

  • Parallelize the OpenAlex + Crossref backfill lookups per entry in migrate_literature_corpus_to_v3_9_0.py (#138). When both openalex_unmatched and crossref_unmatched are missing for an entry, the two independent resolver calls (different hosts, per-instance throttle state, monotonic timing) now run concurrently via a 2-worker ThreadPoolExecutor instead of one-after-the-other, roughly halving per-entry network wait on a full backfill. Scope is deliberately bounded: only the two calls within one entry overlap — the corpus loop stays sequential (cross-entry parallelism is out of scope; the clients' per-instance throttle assumes serial use), all passport mutation / report bookkeeping / degradation logging stays single-threaded on the orchestrator thread, and an already-set field still never consults its client. A single missing field skips the pool and calls directly. Behavior is otherwise byte-equivalent to the sequential version, including the omit-on-Unavailable partial-degradation contract (now surfaced via Future.result()). Adds 2 tests (barrier-verified parallel dispatch + the previously-untested API-down degradation path); the 6 existing migration tests pass unchanged.

Fixed

  • Two edge-case correctness fixes from the #310 post-merge review (#323, closes #324). Post-merge codex review of #310 (security-boundary hardening) surfaced two issues #310's happy-path/crash-free tests did not catch, both verified first-party before fixing. (1) In scripts/adapters/folder_scan.py, a symlink escaping the input root wrote reason: symlink_outside_input_root to rejection_log.yaml, but that value is not in the rejection_log.schema.json reason enum — so the rejection log was contract-invalid exactly in the new symlink-rejection path. It now uses other + detail (schema-valid; the schema's allOf requires detail when reason == other). (2) In scripts/bootstrap_timeline_yaml.py, the lookup queried …/works/{quote(doi)} (encoded) but source_locator recorded …/works/{doi} (raw), so provenance named a URL that was never queried — affecting every DOI, not only reserved-character ones (/ encodes to %2F). It now records the encoded DOI to match the queried URL. Tests strengthened to assert emitted content (the rejection log is jsonschema.validate()d; a new test pins source_locator to the encoded lookup URL), not just exit code.
  • Domain evidence profiles wired end-to-end (#327). Three feature-logic gaps from the #259 post-ship review that survived on main because check_domain_evidence_profile.py only verified documentation-surface presence (C1C7), never the control-flow bound, the consumer parse logic, or the date-gate semantics. [P1] Step 12 (the profile producer) was orphaned from the no-handoff flow directive (bounded at "Step 1-11"), so the profile silently never activated on the common path; intake_agent.md's directive now affirmatively reaches Step 12 (new lint C8). [P2] The reserved-fallback row unknown_user_defined (requested: <reserved>) was misparsed as case (c), emitting a wrong [PROFILE-UNRESOLVED] malformed signal; the consumer now parses the effective token + parenthetical and emits a new [PROFILE-RESERVED-FALLBACK], with (c) narrowed to genuinely unresolvable rows (new lint C9). [P2] The currency (time-range) node was not profile-aware, so a canonical humanities source admitted at the peer-review node was re-excluded at the currency node (INVARIANT 5 violation); the currency node gains a humanities admit branch (purely additive — union/loosen-only, continues through the universal relevance + methodology gates, never short-circuits to Include) (new lint C10). TDD with a RED mutation fixture per defect.
  • Eval-harness gates honor binding per-class thresholds and exclude non-measured tasks (#328). Two correctness holes in the #263 eval-harness CI gates, invisible to the suite because no fixture exercised them. [P1] scripts/_eval_threshold_gate.py failed_tasks() inspected only aggregate_metric.passed, but manifests declare binding per-class thresholds distinct from the aggregate (e.g. citation_extraction aggregate accuracy ≥ 0.90 and per_class accuracy ≥ 0.85); a PR regressing citation_extraction.false.accuracy below 0.85 while the aggregate stayed ≥ 0.90 passed the gate when it should block. failed_tasks() now also iterates per_class, keyed <task>.<class>.<metric>. [P2] scripts/check_ranking_lift.py _flatten_report() flattened any task carrying an aggregate_metric with no status filter, so a not-yet-landed task's placeholder value: 0.0 entered the lift baseline as a real metric — once the task landed, its real value hit the zero-baseline branch and was spuriously flagged as a regression. Both consumers now share the same positive status == "measured" skip-guard so a future status (e.g. "error") is excluded consistently. Adds scripts/test__eval_threshold_gate.py (11 cases) + 5 _flatten_report status-filter tests.
  • v3.10 policy layer: laundering guard wired to real entries + per-block terminal-marker validation (#329). Two P2 enforcement/grammar gaps in the shipped v3.10 triangulation policy layer; the 45 policy-layer tests passed because each guard was only exercised in isolation, never wired to the surface it protects. [P2] assert_venue_type_source_clean (rejects a venue_type_source naming a lookup index under trusted_source_declared) had no production caller — the entry schema's own description promises "enforced by check_v3_10_policy.py" but nothing ran it over real entries, so a passport laundering a k=3-unmatched signal into a declared-trust signal passed both validators. It is now wired into check_literature_corpus_schema.validate_passport's entry loop (a laundered source fails; a legitimate publisher/registry feed name still passes; string-guarded so a non-string venue_type_source surfaces as a clean schema error, not a .strip() traceback). [P2] is_well_formed accepted a terminal TERMINAL-BLOCK marker missing the mandatory policy/reason/mode/policy_hash fields; _parse_inner now keeps per-block metadata and is_well_formed validates each block independently plus the marker-level policy_hash, so a complete later block can no longer mask an earlier block's stripped metadata (C-V6(g) multi-policy co-emission handled correctly).
  • arXiv resolver no-ID skip + non-Atom 200 guard + miss-safe cache decode (#331). Three post-ship defects in the #182 Delta 1+2 citation-integrity data layer (arXiv resolver + verification cache), all verified first-party; the 106 PR tests never exercised these paths. [P2] resolve_arxiv_unmatched ran a title search for citations with no arxiv_id (e.g. a DOI-keyed journal article) and returned true on a title miss — inflating triangulation k (k=3→k=4, rendering CONTAMINATED-QUADRANGULATION-UNMATCHED on a clean journal citation) plus a wasted ~3s request; it now skips the resolver when arxiv_id is absent, matching the spec's ID-gated skipped rule and the guard already in verification_gate._run_arxiv. [P2] A well-formed non-Atom 200 body (e.g. a proxy/CDN HTML error page) parsed cleanly and its empty entry list was cached as a real 90-day miss; arxiv_client._get now validates root.tag == {atom}feed and raises ArxivUnavailable (omit-on-degradation, not cached) on a non-feed root, while a genuine empty Atom feed still resolves to a miss. [P3] VerificationCache.get's bare json.loads aborted verification on a corrupt/non-dict payload; it now treats JSONDecodeError/TypeError/non-dict as a miss (clean recompute), honoring the documented "malformed cache payload = miss" contract. Two tests that codified the buggy behavior were reversed.
  • verification_gate reads ref_slug from the prose join, not the corpus entry (#332). verify_citation/verify_passport previously wrote summary.ref_slug = entry.get("ref_slug"), but literature_corpus_entry.schema.json is additionalProperties: false with no ref_slug property — so the normal (schema-valid) passport path emitted ref_slug: null and violated the summary contract (a required string). Two non-schema-conformant test fixtures masked it. ref_slug is now an explicit prose-sourced parameter parallel to anchor: verify_citation(entry, clients, *, ref_slug, anchor=None, …) and verify_passport(passport, clients, *, ref_slug_by_key, anchors=None, …), with a ValueError on any invalid join — a missing key, or a present-but-empty/non-string slug (validated once at the verify_citation emission point via a shared _is_valid_ref_slug so the per-citation and passport layers can't drift; the passport layer re-checks only to name the offending citation_key) — rather than a contract-invalid summary. The standalone verify_passport.py CLI (which has no prose document) now refuses by default with a clear error and offers an explicit --synthetic-ref-slug citation_key diagnostic escape hatch instead of silently fabricating a slug. API-stability note (C-V4): these are new required keyword-only parameters. The spec's C-V4 freeze names v3.10.0, but #182 was specced-but-not-implemented in v3.10 (spec §0 amendment) and first shipped in the v3.11.0 minor release — so no v3.10.0 caller can depend on the old signature, and C-V4 itself permits a minor release to add required fields. The only in-repo callers (the CLI + the internal verify_passportverify_citation call) are updated in lockstep.
  • check_evals_gold_set enforces statusqueried_by coherence via the shipped schema (#332). The gold validator's flat queried_by ∈ {id, title, null} enum check under-enforced the conditional coherence the summary schema requires (a ran resolver must carry id/title, a skipped/unreachable one must carry null, and queried_by must be present). It now validates each resolver_outcome against citation_verification_summary.schema.json's $defs.resolver_outcome — single source of truth, matching the existing I9b reduce-and-compare philosophy — and the now-dead STATUS_ENUM/QUERIED_BY_ENUM constants are removed. The shipped gold set already satisfies the stricter check.
  • Citation-existence advisory visibility + terminal-marker grammar reconciliation (#333). Two P2 self-consistency issues in the #182 citation-existence gate, neither a gate hole (the formatter's generic severity=HIGH-BLOCK refusal catches the strict token regardless). Item 1 (#342): the spec was internally self-contradictory — C-V6(b) claimed an advisory lookup_verified == false is BOTH "byte-equivalent to v3.9.x" AND "co-emitted in the ref marker", impossible for a firing row, and a second advisory marker token has nowhere to go (the v3.7.3 grammar caps one advisory slot, already taken by contamination's CONTAMINATED-* suffix). Resolved by a third path: the marker stays byte-equivalent (no new suffix, no grammar churn), and the advisory's visibility is carried in the output package instead — formatter_agent.md now requires a mandatory provenance_summary.md Citation Existence Advisories section listing every advisory false row, and provenance_summary.md is added to the Output Package Files Delivered table so the carrier can't be dropped. Every "co-emitted in/alongside the advisory annotation" claim was removed from spec §0 / C-V6(b)/(c)/(e) / Rule 12 (the contamination strict clause, which legitimately does co-emit a suffix, is untouched); new C-V6(b) lint in check_v3_10_policy.py + 3 mutation tests. Item 2 (#338): the canonical "Two marker grammar shapes" terminal enumeration in pipeline_orchestrator_agent.md listed policy=<contamination_triangulation|temporal_integrity>, omitting citation_existence even though the finalizer prose just below emits policy=citation_existence tokens; the enumeration is extended and the mode= clause reconciled per-policy (citation_existence is strict-only), + 2 parser fixtures.

[3.11.0] - 2026-06-04 — Deterministic citation verification gate (#182)

The v3.11.0 minor release ships #182 — a deterministic citation-existence verification gate that runs independently of LLM peer review. It cross-checks every cited reference against up to four bibliographic indexes (Semantic Scholar + OpenAlex + Crossref + the new arXiv resolver) and surfaces a per-citation lookup_verified status, so a fabricated citation with a provably-bogus DOI/arXiv ID is caught by deterministic lookup rather than by hoping a reviewer agent notices. The gate inherits the v3.10 terminal_policies opt-in model — default advisory, opt-in strict — rather than introducing a second hard-block philosophy: detection always runs and populates the summary, but a lookup_verified == false row is terminal only under terminal_policies.citation_existence == strict. Default behavior is non-blocking (advisory, /ars-mark-read-acknowledgeable); a user must opt into strict to make existence-failure terminal. The false definition is deliberately narrowed to ID-keyed unmatched (an exact DOI/arXiv lookup that provably fails), so a legitimately-unindexed humanities / non-English / regional citation with only a title-unmatched stays unresolvable and never blocks (C-V6(a); an acknowledged precision-over-recall tradeoff documented in the spec, mirroring strict_articles_only).

Five delta items (#182):

  • Delta 1 — arXiv API resolver + four-index contamination rendering. New scripts/arxiv_client.py verifies citation existence against export.arxiv.org (metadata + existence; no API key, no polite-pool email — built-in rate-limit pacing per arXiv ToU; accepts both old-style hep-th/9711200 and new-style 2605.07723 IDs). scripts/contamination_signals.py extends the v3.9.0 cross-index triangulation advisory matrix from three indexes (k=0..3) to four (k=0..4) with an arxiv_unmatched signal, and the orchestrator finalizer + formatter render the four new advisory suffixes (CONTAMINATED-ARXIV-UNMATCHED at the k=1/k_max=1 arxiv-only carve-out; CONTAMINATED-QUADRANGULATION-UNMATCHED at k=4/k_max=4; plus their two PREPRINT compositions). All advisory — the terminal gate / refusal list is unchanged (R-L3-2-E). arxiv_unmatched field added to literature_corpus_entry.schema.json.
  • Delta 2 — persistent verification cache. New scripts/verification_cache.py — a local SQLite store (~/.cache/ars/verification.db, override via ARS_VERIFICATION_CACHE_PATH; WAL mode; 90-day TTL) keyed by (citation_key, resolver_name, query_form), so the same paper cited across drafts is verified once. Each resolver entry point (crossref / openalex / S2 / arxiv) gains an optional cache parameter. New /ars-cache-invalidate <citation_key> command removes every cached row for a key (idempotent no-op when absent).
  • Delta 3 / C-V6 — citation-existence terminal policy. New terminal_policies key citation_existence (closed enum {advisory, strict}, per-key absence = advisory) in terminal_policies.schema.json, alongside contamination_triangulation. This replaces the original Delta-3 ARS_CLAIM_AUDIT default-flip as the gate's on/off control. The finalizer is the sole policy evaluator; formatter_agent.md rule 12 refuses on a lookup_verified == false row only under strict, co-emitting [UNVERIFIED CITATION — lookup_verified=false: ...] alongside the advisory annotation. HIGH-BLOCK is terminal — not /ars-mark-read-clearable. Manual entries structurally exempt.
  • Delta 4 — unified per-citation status surface. New shared/contracts/passport/citation_verification_summary.schema.json + scripts/citation_verification_summary.py write a lookup_verified (enum {true, false, unresolvable}) + anchor_present + resolver_outcomes (per-resolver {matched, unmatched, unreachable, skipped}) row per citation. The classification is anti-fabrication-biased (one ID-keyed unmatched is positive evidence of non-existence; a single transient outage does not cancel it) and the false form is narrowed to ID-keyed unmatched per C-V6(a).
  • Delta 5 — standalone verification_gate API. New scripts/verification_gate/__init__.py extracts the gate logic into a callable API composing the four resolvers + the unified summary writer (a second caller of the same lower-layer infrastructure as the v3.8 audit, not a duplicate). New scripts/verify_passport.py CLI runs the gate over a Material Passport standalone.

Lint + CI:

  • scripts/check_v3_9_0_triangulation.py (the canonical cross-version contamination-suffix oracle) rule 1 upgraded from subsection token-presence to a matrix-row oracle: each Delta-1 token must sit on the finalizer suffix-table row carrying its exact (k, k_max) cell, so deleting or mistokening an operational row fails even when the same token survives in surrounding prose. The formatter pass-through allowlist set-equality oracle extends 9 → 13 tokens.
  • scripts/_ci_pytest_manifest.toml backfills 5 data-layer test entries (citation-verification-summary / verification-gate / arxiv-client / verification-cache / verify-passport-cli) that shipped with the data layer but were not wired into the manifest runner at the time.

Spec: docs/design/2026-05-21-v3.10-182-promote-citation-gate-spec.md (§0 v3.11 amendment + INVARIANT C-V6).

[3.10.0] - 2026-06-01 — Triangulation policy layer, Kong et al. survey adoptions, eval harness, scoped-write guard

The v3.10.0 minor release bundles the opt-in contamination-triangulation terminal policy layer (#127 PR-B — default behavior byte-equivalent to v3.9.0), several Kong et al. 2026 survey adoptions (Rebuttal Commitment Ledger #256/#266/#268/#269, discipline-relative domain evidence profiles #259), the v3.10 measurement infrastructure (generalized eval gold set + ranking-lift gate, #184), the #134 scoped-write guard MVP (a deterministic PreToolUse hook fencing the 23 single-phase agents to their own phase directory; all Bash denied for those agents), the /ars-mark-read plugin commands (#190) + a broken-on-arrival fix (#195), a Simplified-Chinese README (#185), and CI hardening (#156/#155). Default citation-policy behavior is byte-equivalent to v3.9.0 unless a user opts into a strict mode (#127). The one default-on behavior change is #134's PreToolUse write-scope guard: the 23 single-phase agents are now fenced to their own phase directory and denied Bash — this constrains those subagents, not the user-facing skill outputs.

v3.10 triangulation policy layer (#127 PR-B — opt-in terminal modes, default behavior byte-equivalent to v3.9.0):

  • #127 PR-B — terminal policy layer. Ships the contamination-triangulation policy layer deferred by v3.9.0 (#102): opt-in strict modes that promote the advisory k=3 triangulation signal to a non-acknowledgeable terminal HIGH-BLOCK at the citation-emission boundary. Default behavior is byte-equivalent to v3.9.0 — an absent or all-advisory terminal_policies block changes nothing (Invariant 7). Built on PR-A's canonical firm-rules + sync-lint base.
    • Schema. New passport-level shared/contracts/passport/terminal_policies.schema.json (standalone, NOT inside the entry schema — Invariant 11): contamination_triangulation ∈ {advisory, strict, strict_articles_only}; temporal_integrity accepts only advisory (forward-reserved namespace — a schema-accepted temporal strict with no wired behavior would be a false-safety bug, Invariant 3). literature_corpus_entry.schema.json gains venue_type (closed enum incl. explicit unknown), venue_type_provenance (closed enum; the API-_inferred values are deliberately absent per R-L3-2-D), and venue_type_source (required iff trusted_source_declared). Pair dependencies: type ⟺ provenance (bidirectional); venue_type == unknown ⟹ provenance == unknown (one-way — a known type may carry unknown provenance, no data loss). All adapter-declared only; never inferred from free-form venue. check_literature_corpus_schema.py extended to validate a passport-level terminal_policies block before iterating entries.
    • Finalizer (sole policy evaluator). pipeline_orchestrator_agent.md gains a ## Cite-Time Provenance Finalizer — v3.10 extension section. Under a non-advisory passport it stamps policy_hash=<slug> on every ref marker (a fully-encoded human-readable canonical token of the non-advisory terminal_policies keys — sorted key.value join — so two distinct configs never collide). Under an all-advisory passport NO stamp is emitted: the marker is the bare v3.9.0 shape (byte-equivalent, Invariant 7) — the absence of a stamp is the advisory signal. Under strict, a k=3 ref co-emits a TERMINAL-BLOCK severity=HIGH-BLOCK policy=... reason=... mode=... policy_hash=... token ALONGSIDE (not replacing) its advisory CONTAMINATED-* suffix, so the "why" survives. strict_articles_only is a deliberate PRECISION mode — k=3 promotes only when DOI present ∧ venue_type ∈ {journal-article, conference-paper} ∧ declared provenance; a DOI-less or unknown-venue journal article stays advisory by design (humanities / non-English / regional coverage gap). Audit trail gains a terminal_blocked[] bucket; aggregate counts dedupe by ref slug across advisory + terminal buckets (non-additive). Manual-entry exemption preserved (k=3 structurally unreachable). HIGH-BLOCK is terminal — /ars-mark-read does NOT clear it.
    • Formatter (STAMP-ONLY two-gate). formatter_agent.md gains refusal rule 11 (generic severity=HIGH-BLOCK inside a <!--ref:...-->, NOT a per-subtype list) plus a ## Cite-Time Terminal Policy Gate (v3.10) section. Two ordered gates, never short-circuited: Gate 1 freshness (stamp mismatch / missing-stamp-under-non-advisory → [STALE-POLICY-EVALUATION]; missing-stamp-under-advisory passes, Invariant 7), Gate 2 HIGH-BLOCK refusal applied to every gate-1-passing marker (a stripped-stamp marker still carrying TERMINAL-BLOCK is still refused). The formatter never re-evaluates strict_articles_only logic (Invariant 13 — the finalizer is the sole evaluator). A bare-prose HIGH-BLOCK outside any ref marker never refuses (Invariant 12). v3.9.0 advisory pass-through allowlist unchanged.
    • Firm rule. R-L3-2-A reworded in firm_rules.md to the broad default-advisory + opt-in-strict form (covering contamination AND the forward-reserved temporal namespace; the wording explicitly states no temporal strict path exists yet, no over-promise). Contamination mirrors stay intentionally by-ID references (not full-block copies); the wording is single-sourced in the canonical block. check_firm_rules_sync.py gains a contradiction guard scoped to the R-L3-2-A reference sentence in each contamination-context file (rejects unqualified "advisory only / never block" claims now that strict can block) — deliberately NOT scanning the whole file, so the Collaboration Depth Observer's legitimate "never blocks" wording is not false-flagged.
    • Migration + adapters. scripts/migrate_literature_corpus_to_v3_10.py seeds passport-level terminal_policies (deep-merge — only absent keys, idempotent, dry-run; never backfills venue_type from free-form venue; clear error on a non-mapping terminal_policies; pre-v3.9.0 passports reported out-of-scope, not silently skipped). The three reference adapters (folder_scan / zotero / obsidian) now declare venue_type + venue_type_provenance (Zotero item type → adapter_declared; folder_scan → unknown/unknown; obsidian honors a frontmatter venue_type as user_declared, else unknown/unknown).
    • Lint + CI. New scripts/check_v3_10_policy.py (runs ALONGSIDE check_v3_9_0_triangulation.py, not a rename) covers the schema fields, the _inferred-rejection, the pair dependencies, the trusted_source laundering guard, the standalone schema home, the marker grammar (with a reusable parser + the five required fixtures: terminal co-emit / non-terminal advisory / non-terminal clean / legacy-no-stamp / bare-prose-no-refuse), the generic rule-11 shape, the formatter STAMP-ONLY two-gate, and the closed enums. Wired into spec-consistency.yml + _ci_pytest_manifest.toml. Spec: docs/design/2026-05-31-ars-v3.10-policy-layer-rescope-spec.md.

Added

  • #134 Slice 1 — scoped-write guard MVP (the Active Conductor rescope). New PreToolUse hook scripts/ars_write_scope_guard.py fences the 23 single-phase (Bucket A) subagents to their own phase directory: for Write/Edit/MultiEdit it normalizes the single top-level file_path (realpath, so ../symlink traversal resolves in true filesystem order), denies workspace escapes, unconditionally protects the enforcement surface (hooks.json, the hook/manifest/lint, agent definition files, .claude/CLAUDE.md), then enforces the agent's allowed_write_globs with a segment-aware iterative glob matcher (* never crosses /; dir/** is descendants-only; no recursion-limit crash on deep paths). All Bash is denied for a Bucket A agent — it uses the Grep/Glob tools to search and the structured editing tools to write. (The spec's "best-effort literal-target Bash" was taken to its sound conclusion: neither "this Bash writes a file" nor "this Bash is read-only" can be decided reliably from a command string without a sandbox, so all-deny is the only zero-fail-open Bash policy; spec carries an Implementation-outcome note + aligned §3.2/§3.3 wording.) Backed by scripts/ars_phase_scope_manifest.json (machine-readable scope for the 23 agents) and the fail-open guard lint scripts/check_v3_10_134_write_scope.py (three-way name cross-check: classification roster == manifest keys == on-disk frontmatter names, + filesystem exhaustiveness at any nesting depth, so rename/typo/new-agent drift can't silently fail the hook open). hooks.json PreToolUse wiring + CI steps (lint + hooks.json wiring assertion) + pytest manifest entries; TDD throughout with lint mutation tests. The structured-tool determinism is the load-bearing win; the Bash deny closes the direct-shell-write path for fenced agents entirely. Slices 2-5 (write-provenance ledger, task envelopes, return contracts, persistent conductor) remain forward-scope. Spec: docs/design/2026-06-01-ars-134-conductor-rescope-deterministic-write-guard-spec.md. Closes #134.
  • Kong A4 (#259): Discipline-relative domain evidence profiles. New academic-paper/references/domain_evidence_profiles.md defines 4 ship-ready profiles (cs_ml, general_social_science, humanities_interpretive, unknown_user_defined) + 5 reserved. intake_agent Step 12 emits a scholar-selected Domain Evidence Profile PCR row (never auto-selected; reserved selections fall back to neutral with a surfaced advisory). literature_strategist_agent resolves the row and applies loosen-only gate + upstream-filter changes — monotonic admit-only, and profile-admitted sources still flow through the universal relevance + methodology gates. New scripts/check_domain_evidence_profile.py (C1C7 documentation-surface lint, including a SHA-256 pin of the source_quality_hierarchy.md Field-Specific Adjustments block) + mutation suite, wired into spec-consistency.yml + the pytest manifest. Advisory only. Closes Kong et al. 2026 §7.4.6 domain-evidence-standards gap.
  • Kong A1 (#256): Schema 11 R&R Traceability Matrix gains commitment_extracted / fulfillment_status / unfulfilled_rationale optional fields. revision_coach_agent Step 3.5 extracts commitments; re_review_mode_protocol step 5 verifies + surfaces COMMITMENT_GAP advisory. Worked example at academic-paper/examples/commitment_ledger_example.md. Calibration seed at evals/calibration/commitment_ledger_seed.yaml (10 cases). Advisory only — author retains final responsibility. Closes Kong et al. 2026 §7.4.3 commitment-fulfillment gap.
  • Kong A1 follow-up (#269): Schema 11 required_evidence_type enum widened from 7 to 9 values, adding prose_edit and other. prose_edit is a seventh manuscript-evidence type for sentence-/paragraph-level changes too granular to bucket structurally (typo fixes, terminology clarifications, equation formatting, citation-style corrections); it verifies at revision_location like the other manuscript types. other mirrors the existing commitment_type escape hatch for genuinely uncategorizable evidence and triggers a new soft EVIDENCE_TYPE_UNSPECIFIED advisory at re-review (orthogonal to COMMITMENT_GAP; fires whenever required_evidence_type == other, regardless of fulfillment status). The prior 7-value closed set forced typo-level comments into wrong buckets (methods_paragraph) or out of the ledger entirely, violating the every-comment extraction rule. Synced across shared/handoff_schemas.md Schema 11, revision_coach_agent Step 3.5, re_review_mode_protocol Commitment Ledger Verification, and revision_tracking_template.md; worked example and calibration seed (now 12 cases, +E1/E2) extended. Advisory only. Surfaced by Gemini R3 review of PR #264, Finding 3. Closes #269.
  • Kong A1 follow-up (#268): Schema 11 Commitment Ledger refactored from three index-aligned parallel lists (commitment_extracted objects + top-level fulfillment_status[] + unfulfilled_rationale[]) to a nested-object shape — fulfillment_status and unfulfilled_rationale now nest INSIDE each commitment_extracted object. This makes length-mismatch / index-desynchronization structurally impossible, closing the Gemini R3 (PR #264) Finding 1 fragility where a dropped Markdown <br> or numbering error silently mispaired a status with the wrong commitment and produced a false COMMITMENT_GAP advisory. REPLACE, not coexist (spec §2): the parallel-list shape is removed entirely — no executable consumer, lint, or fixture carried it (the #263 calibration harness is unshipped; the seed is a non-runnable seed), so coexistence would only preserve the failure mode. Lifecycle fields are absent at extraction time (revision_coach_agent Step 3.5) and appended per-object during revision execution; the old unfulfilled_rationale: "" placeholder for fulfilled commitments is dropped (omitted, not empty-string). The equal-length validation invariant is retired (now structurally impossible); a legacy-normalization note instructs zipping any pre-#268 top-level arrays onto the nested objects before re-review. Synced across shared/handoff_schemas.md Schema 11 (incl. the #266 residual_action coherence prose, reworded from unfulfilled_rationale[i] index notation to object-field notation), revision_coach_agent Step 3.5, re_review_mode_protocol Commitment Ledger Verification, revision_tracking_template.md (three fragile <br>-separated columns collapsed into one per-commitment nested YAML ledger), worked example, and the 12-case calibration seed. author_fulfillment_claim (Gemini's promised-vs-claimed-vs-verified split) deferred — not required for the structural fix (spec §2). New scripts/check_268_nested_commitment_ledger.py (N1-N5 + N3b: seed extraction-field presence, no retired parallel-list keys, per-commitment lifecycle coherence via a _blank_rationale helper that treats missing/null/whitespace uniformly, case-level expected_commitment_gap oracle coherence with a real-boolean guard, no surviving index notation) + 18 mutation tests, wired into spec-consistency.yml + the pytest manifest. Advisory semantics unchanged. Surfaced by Gemini R3 review of PR #264, Finding 1. Spec: docs/design/2026-05-31-ars-268-schema11-nested-commitment-ledger-spec.md. Closes #268.
  • Kong A1 follow-up (#266): Schema 11 residual_action (concern-level) vs unfulfilled_rationale (per-commitment) coherence. Documented their semantic relationship (different granularity and tense — unfulfilled_rationale[i] is backward-looking and per-commitment, residual_action is forward-looking and concern-level, so a row may carry both without redundancy or contradiction), the multi-commitment single-string shape convention (residual_action stays one concern-level string, not expanded into a list), and a re_review_mode_protocol note that a populated residual_action alongside some fulfillment_status[i] == fulfilled is not a contradiction, cross-referencing the shared/handoff_schemas.md Schema 11 convention. Doc-only; advisory semantics unchanged. Closes #266.

Bug fixes (no version bump — corrects a broken-on-arrival behavior from #190):

  • #195 — /ars-mark-read crashed on real YAML passports. scripts/ars_mark_read.py:_load_corpus_keys used json.load() to read the Material Passport, but every adapter (folder_scan / zotero / obsidian) and every other ARS tool produces / consumes passport.yaml. The existing 11-test fixture in scripts/test_ars_mark_read.py wrote JSON-formatted passports, so the suite was green while real-world /ars-mark-read smith2024 --passport-path ./passport.yaml exited with json.JSONDecodeError before reaching citation-key validation. Two new TDD tests pin the adapter-format expectation (YAML happy path + YAML invalid-key hard error); _write_passport helper switched to yaml.safe_dump. Companion P2 also closed: existing-but-unwritable read-log file now surfaces the canonical [ARS-MARK-READ ERROR: ...] fail-fast rather than a bare PermissionError traceback, via an extra os.access(log_path, os.W_OK) check after the parent-W_OK gate. 14 ars_mark_read tests pass (was 11), full suite 1623 / 3 skipped. Surfaced by post-squash codex review of PR #191 (issue #192).

Plugin commands (prep for v3.10 — no behavior change to existing skills):

  • #190 — /ars-mark-read + /ars-unmark-read plugin commands. v3.6.8 spec §3.6 + Step 7 (round-2 R2-002, round-5 R5-003 amends) designed these commands as the user-facing affordance for the human-read signal, but the command surface itself was never shipped — commands/ carried only the 10 /ars-<mode> skill triggers. New scripts/ars_mark_read.py deterministic CLI implements the four §3.6 R5-003 fail-fast modes (no active passport / passport not found / parent unreadable / read-log unwritable), the §3.6 firm-rule-2 hard error on invalid citation_key, batch-level all-or-nothing semantics (any invalid key rejects the whole batch), and the §3.6 firm-rule-3 append-only write to <passport-stem>_human_read_log.yaml next to the active Material Passport. /ars-unmark-read writes rescinded_at: <ISO 8601> to the matching entry, never deletes. Two new thin markdown command files (commands/ars-mark-read.md, commands/ars-unmark-read.md) invoke the CLI via Bash; both declare model: sonnet routing per feedback_no_haiku.md. New scripts/check_v3_6_8_mark_read_commands.py CI lint per spec Step 7 acceptance: 2 commands exist, carry the literature_corpus[] validation reference, reference the human_read_log.yaml peer-file write target (NOT entry frontmatter, per §3.1 firm rule 3), and declare model: sonnet. 11 unit tests for the CLI + 6 unit tests for the lint. /ars-list-read and commands/ars-mark-read.zh-TW.md were spec-marked optional and remain deferred. Closes #190.

v3.10 measurement infrastructure (prep for v3.10 — no behavior change to existing skills):

  • #184 Phase 1a — citation-extraction gold subset. New top-level evals/ directory holds v3.10 generalized gold-set corpora for verification_gate.verify_citation measurement targets. Ships evals/gold/citation_extraction/ with 50 hand-curated tuples (all populated in this PR) + manifest.yaml + expected_outcomes.json. v3.10.0 binding thresholds: aggregate accuracy >= 0.90 across 50 tuples, per-class accuracy >= 0.85 for each of true / false / unresolvable (changing requires spec amendment per #184 §3.1.1 / E-V2). Distribution: 20 valid_doi + 10 valid_arxiv + 5 manual_exempt + 15 fabricated (= 50). The original valid_unresolvable source class was removed as unbuildable — no stable first-party-verifiable real-but-unmatched citation exists under current index coverage; tuples 031-040 were reassigned to fabricated; coverage gap tracked in #250. Tuple shape (locked per codex consult Q1-Q5): self-contained corpus_entry mirroring literature_corpus_entry, arxiv_id as tuple-level field (forward-looking — see #234 for #182 implementation alignment), human_expert_verdict optional (10/50 = 20% per Delta 5), fabrication_intent boolean enforced on fabricated tuples. New scripts/check_evals_gold_set.py enforces 9 invariants (I1 set equality / I2 tuple_id ↔ filename / I3 kind distribution / I4 no-dup-JSON-keys / I5 label ↔ kind / I6 arxiv_id placement / I7 fabrication_intent marker / I9 resolver_outcomes shape / I10 corpus_entry schema) via 17 mutation tests on a 3-tuple clean fixture. CI step wired into .github/workflows/spec-consistency.yml. Spec: docs/design/2026-05-21-v3.10-184-extend-eval-harness-spec.md.
  • #184 Phase 1b — eval harness + ranking-lift gate. New scripts/run_evals.py multi-task harness (python -m scripts.run_evals [--task <name>] [--baseline <path>] [--compare <path>] [--output <report.json>]): discovers every evals/gold/<task>/manifest.yaml, measures each task, and emits a report shaped by the new shared/evals_lift_report.schema.json (required harness_version / run_id / gold_set_version / per_task[] / caveats[] with the v3.8 honesty-disclosure minItems:1 convention). For citation_extraction the harness computes the predicted lookup_verified 3-class enum itself from each tuple's resolver_outcomes.*.status via the #182 Delta 4 reducer (verification_gate.verify_citation has not shipped — reconcile when it does); the metric is symmetric 3-class accuracy, unresolvable is never collapsed into false. For rq_framing_patterns it dispatches to the existing scripts/check_rq_framing_patterns.py runner and adapts its FNR / FPR / balanced-accuracy output into the per-task lift shape. --baseline + --compare produce a side-by-side report carrying lift_pre / lift_post; expert_concordance is emitted per class over the 10 human_expert_verdict-labeled tuples (advisory, never gates per E-V3). Missing entrypoint module / Phase-2 gold set yields a pending/skipped notice, never a traceback. New scripts/check_ranking_lift.py lift gate: pure compute_signed_lift(baseline, compare, direction) (higher-is-better (compare-baseline)/|baseline|, lower-is-better numerator inverted, zero-baseline +inf/-inf); blocks on any signed_lift < -0.05 or zero-baseline change unless the PR body carries [ranking-regression-acknowledged] + an OPEN issue URL and the declared Affected metric: <task>.<class>.<metric> matches the observed change (E-V4); OPEN-issue check via a monkeypatchable _issue_is_open seam (never networks in tests). New CI workflow .github/workflows/eval-harness.yml (Delta 3 path filter; concurrency group includes github.event_name; OQ-3 skip-guard for absent Phase-2 gold sets; deterministic [eval-regression-acknowledged] + OPEN-issue PR-body gate) and net-new .github/pull_request_template.md Eval-impact section. Tests: scripts/test_run_evals.py, scripts/test_check_ranking_lift.py, scripts/test_evals_citation_extraction.py, scripts/test_evals_lift_report_schema.py (incl. trivial-accept-all schema mutation). Spec: docs/design/2026-05-21-v3.10-184-extend-eval-harness-spec.md.

Localization (no version bump — no behavior change to skills):

  • #185 — Simplified Chinese README. New README.zh-CN.md (630 lines, mirroring README.zh-TW.md structure) translated by external contributor @xpfo-go (PR #181). Language switcher updated across the four READMEs (en / zh-CN / zh-TW / ja-JP); CONTRIBUTING.md README sync guidance extended to four locales. scripts/check_spec_consistency.py refactored to share zh-TW / zh-CN logic via ZH_README_CONFIGS tuple; both locales covered by test_aligned_zh_cn_readme_passes + test_stale_zh_cn_badge_fails regression tests (symmetric with the ja-JP tests added in #170).

CI / infrastructure (no version bump — no behavior change to skills):

  • #156 — Unified pytest invocation manifest. Twelve pytest scripts/test_*.py invocations in .github/workflows/spec-consistency.yml are now declared in scripts/_ci_pytest_manifest.toml and run via scripts/run_ci_pytest_manifest.py. Drift guard scripts/check_ci_pytest_manifest.py rejects (a) missing path, (b) duplicate id, (c) duplicate (path, args), (d) malformed args, (e) any pytest scripts/test_*.py re-introduced in the workflow outside the runner. pip install pytest consolidates from 12 redundant installs to one. 17 unit tests for runner + lint. python3 -m unittest scripts.test_* invocations stay inline (out of scope for #156). 41 disk test_*.py files that the manifest does not list remain unclassified — separate follow-up.

  • #155 — Re-attempt F4: harden test-count-monotonic.yml to fail on pytest collection errors. Both head and base count steps now capture pytest's exit code separately from the pipe, treat exit 5 (no tests collected) as a tolerable degenerate case, and fail the gate on any other non-zero exit. Previously, a 2>/dev/null | grep -c '::' || true swallow on the base step would silently set BASE_COUNT to 0 on a broken-import or fixture-missing error in the base commit, making the head-vs-base monotonic check vacuously pass. The original F4 fix landed in PR #153 commit 8121dfa during the v3.9.4.2 cycle but was reverted in 4abf9de when it surfaced #154 (now closed by PR #158). With #154 fixed and #156 keeping CI test discovery clean, F4 v2 ships symmetrically across head and base.


[3.9.4.2] - 2026-05-19 — Post-ship hotfix for PR #149 CI discipline gates

Trigger: Codex post-ship review of PR #149 (7 CI discipline gates mechanizing the release-cycle review chain) surfaced 4 P2 findings. v3.9.4.2 hardens 3 of 4; the 4th (test-count-monotonic harden) was reverted because it surfaced a pre-existing scripts/ package issue, tracked as #154 (since fixed by PR #158) and re-attempt #155.

CI gate hardening (PR #149 + #153):

  • F1 — harness-retirement scheduler context: harness-retirement-monthly.yml adds GH_REPO so scheduled runs have repo context for gh issue create (workflow was silently failing on cron without it).
  • F2 — release-cooldown tag filter: release-cooldown.yml filters PREV_TAG lookup to v* tags so non-release tags (e.g., legacy plugin tags) cannot bypass the cooldown gate.
  • F3 — release-cooldown hot-fix detection: release-cooldown.yml also reads annotated tag subject + accepts the hot-fix spelling variant; v3.9.2 was previously a false-negative hotfix under the old detector.
  • F4 (reverted): test-count-monotonic.yml harden landed in 8121dfa and reverted in 4abf9de when it surfaced scripts/ package import errors (ModuleNotFoundError: No module named 'scripts') — pre-existing latent defect masked by the prior 2>/dev/null | || true pattern. Tracked as #154 (now closed by PR #158) and re-attempt #155.

Release-cooldown symmetry follow-up (PR #157):

  • Override token [skip-cooldown] now read from both the commit message AND the annotated tag message. This v3.9.4.2 tag itself is the self-bootstrapping fix — the gate correctly identified v3.9.4.1 (3h prior) as the previous hotfix and fired the 24h cooldown, proving F2+F3 work end-to-end. The override symmetry patch makes the tag shippable.

Closes: #152. Follow-ups: #154 (closed by PR #158), #155, #156.


[3.9.4.1] - 2026-05-19 — Post-ship hotfix for v3.9.4 temporal verification

Trigger: Codex post-ship review of v3.9.4 squash commit af09cf5 surfaced 4 real bugs that per-task subagent reviewers missed during v3.9.4 implementation. v3.9.4 tag remains immutable; v3.9.4.1 patches the verifier and schema layer + brings docs in alignment.

Bug fixes:

  • #135 P1 (audit wiring): audit() now passes citation_provenance through to _pass_2_anachronism and _pass_4_causal. When a ref slug has confidence: low or conflict in citation_provenance.yaml, the verifier emits TEMPORAL-METADATA-MISSING instead of using timeline dates as arithmetic ground truth. v3.9.4 dropped citation_provenance on the floor — spec §3.4 first-party safety check was structurally broken.
  • #135 P1 (date parser): _date_to_interval() now parses all schema-valid date shapes including YYYY-MM (Crossref month-precision output) and YYYY-MM-DD..YYYY-MM-DD (interval precision used by effective_date_range). v3.9.4 only handled day/year/prose-month forms — schema-valid month/interval shapes raised ValueError and P2/P4 silently skipped the check via the existing except ValueError: continue guard.
  • #135 P2 (P4 direct-date binding): P4 now binds each side of a causal trigger to either a <!--ref:slug--> marker OR a direct date capture in the sentence. v3.9.4 required refs on both sides, silently dropping sentences like "The 2026 policy enabled the 2020 rollout." bound_dates.source distinguishes timeline_ref from draft_capture; bound_refs is empty when both sides came from direct date capture.
  • #135 P2 (schema absent-property bypass): citation_provenance.schema.json confidence:high allOf branch now requires both crossref_issued and pdftotext_cover_first_line to be present in addition to non-null (then.required added). v3.9.4 used then.properties only, which doesn't fire when a property is absent — so entries with confidence:high and both source fields omitted silently passed validation.

Documentation:

  • docs/ARCHITECTURE.md updated from stale v3.8.0 baseline to v3.9.4.1; Section 8 Evolution Timeline filled in v3.8.1 / v3.8.2 / v3.9.0 / v3.9.1 / v3.9.2 / v3.9.3 / v3.9.4 / v3.9.4.1 entries; Section 9 Skill Modes table aligned to current versions.
  • Suite-version needles aligned across MODE_REGISTRY.md, README.md badge + tag URL + section heading, README.zh-TW.md badge + tag URL + section heading, academic-pipeline/SKILL.md frontmatter, .claude-plugin/plugin.json, scripts/check_spec_consistency.py expected-text constants, .claude/CLAUDE.md skill suite table.

Test count: 1549 → 1561 (+12 net new tests covering all 4 fixes, 0 regression).


[3.9.4] - 2026-05-18 — Temporal Verification Layer (advisory)

External motivation: Issue #135 — LLM next-token objectives are systematically blind to deterministic factual classes including temporal ordering. v3.9.4 adds a deterministic advisory verifier at the Phase 4 → 5 boundary covering 5 failure modes.

Mechanisms:

  • M1: new Phase 2 sibling timeline_extraction_agent owning phase2_investigation/timeline.yaml + phase2_investigation/citation_provenance.yaml
  • M2: Phase 4 → 5 deterministic verifier scripts/temporal_integrity_audit.py (5 passes)
  • M3: Temporal Integrity Iron Rule in report_compiler_agent + draft_writer_agent
  • M6-minimal: First-party Crossref issued + pdftotext cover verification
  • M7-minimal: Date provenance + comparator materialization
  • M5-stub: User-declared version_family_id only

Zero modification to literature_corpus_entry, claim_audit_result, claim_intent_manifest. bibliography_agent unmodified (F2 invariant). 3 new sidecar schemas (aggregate-level with $defs).

Coverage estimate: 55-70% baseline / 65-75% with M7 minimal (LLM extractor blindness on tuple extraction is structural; advisory architecture acknowledges this).

Out of v3.9.4 scope (deferred to v3.10): M4 reviewer integration, M5 full version discovery, M6 full PDF audit, M8 relation manifest, CC5 catalog-completeness semantics, hard-block policy, OpenAlex lookup.

Spec: docs/design/2026-05-18-ars-v3.9.4-temporal-verification-spec.md.


[3.9.3] - 2026-05-18 — Housekeeping (#128 §1-3, §5-6)

Pure refactor + one latent-bug fix carrying over from the v3.9.0 /simplify review backlog. The v3.9.0 cross-index triangulation client family (Semantic Scholar + OpenAlex + Crossref) shipped intentionally byte-equivalent across 3 client modules for code locality; now that the family is stable, the dedup prevents sibling drift when threshold tuning, normalization rules, or throttle measurement need adjustment.

Refactor — extracted helpers (no behavior change)

  • scripts/_text_similarity.py — extracts 4 helpers + 4 constants previously triple-implemented byte-equivalent in semantic_scholar_client.py / openalex_client.py / crossref_client.py: _PUNCT_TRANSLATION, _normalize_title, _similarity, _TITLE_SIMILARITY_THRESHOLD = 0.70, _BACKOFF_SECONDS = 2.0, _MAX_RETRIES = 3. 14 new tests on the shared module.
  • scripts/_passport_yaml.py — extracts ruamel.yaml round-trip config (preserve_quotes = True, indent(mapping=2, sequence=4, offset=2)) + load_passport / dump_passport functions previously duplicated byte-equivalent in migrate_literature_corpus_to_v3_7_3.py + migrate_literature_corpus_to_v3_9_0.py. 7 new tests on the shared module.
  • contamination_signals._resolve_by_doi_then_title — private helper for the identical DOI-then-title control flow shared by resolve_openalex_unmatched (§3.4) + resolve_crossref_unmatched (§3.5). Both public wrappers preserve the v3.9.0 spec API surface; exception-type differentiation stays at the wrapper. 10 existing resolver tests verify byte-equivalent behavior.

Latent-bug fix — throttle measurement standardized on time.monotonic

  • OpenAlex + Crossref clients now use time.monotonic() for _throttle() elapsed measurement + _last_request_at anchor refresh, matching Semantic Scholar (which had standardized on monotonic per #115 R5-2). NTP / manual clock adjustments could push time.time() backward, producing negative elapsed and either inflated sleep (negative compared less than min_interval) or zero sleep — latent throttle-bypass / API-spam bug. Documented as a "maintenance smell" in #128 §6.
  • New tests (test_openalex_client::test_throttle_uses_monotonic_clock + test_crossref_client::test_throttle_uses_monotonic_clock) lock NTP-safe semantics: throttle reads time.monotonic and never reads time.time.

Dual-path import infrastructure

  • All 5 module-level cross-imports in openalex_client.py / crossref_client.py / semantic_scholar_client.py / migrate_literature_corpus_to_v3_7_3.py / migrate_literature_corpus_to_v3_9_0.py use the dual-path try/except pattern (sibling-first, namespace-package fallback). Follows scripts/slr_lineage.py precedent but inverted for class-identity preservation (pytest uses sibling-path imports; SemanticScholarUnavailable from scripts.contamination_signals is a different class instance than contamination_signals.SemanticScholarUnavailable).
  • Latent fix: scripts.semantic_scholar_client + scripts.migrate_literature_corpus_to_v3_7_3 are now import scripts.X-clean from repo root (were silently broken on main due to pre-existing absolute cross-imports). Caught by codex round-1 reasoning trace.

Deferred from #128

  • §4 — parallelize OA + CR per-entry calls in v3.9.0 migration tool carried to #138 (target v3.9.4 or v3.10). Introduces new behavior + ThreadPoolExecutor + test-rebuild scope; incompatible with v3.9.3 patch boundary.

Regression status

  • 1482 → 1505 passed + 3 skipped + 111 subtests (+23 new tests, 0 regression).
  • scripts/check_spec_consistency.py + scripts/check_version_consistency.py green.
  • 6/6 import scripts.X paths verified clean from repo root (3 from-OK-to-OK, 2 latent-broken-now-OK, 1 OK throughout).
  • Cross-model review: codex round 1 + 2 both 0 explicit findings (one P1 self-caught from R1 trace, closed pre-R2). Gemini 3.1-pro-preview round 1: 0 findings.

[3.9.2] - 2026-05-18 — Phase boundary hot-fix (#133)

Hot-fix for issue #133 (phase scope inflation). A user incident showed that ARS auto-dispatched a single-phase agent (bibliography_agent) when given ambiguous cross-phase input (pre-written abstract + pre-collected literature), and the dispatched agent then autonomously executed Phases 3-6, skipping mandatory independent crosschecks (DA / EIC / Ethics).

This release ships the prompt-discipline + advisory-verifier hot-fix. The deterministic gate (PreToolUse hook + multi-phase task envelope schema + author provenance) is tracked separately as v3.10 active conductor (#134) — long-term architectural fix.

Design history: 4 design rounds (v1-v4) + mid-impl review. Triple-track reviewer use cases (codex review --base main + inline opus subagent + self-review). Codex 0.130 broke on this repo context 5x consecutive per memory feedback_codex_0_130_docs_review_broken.md (49 files / 1529 lines on full branch is firmly in the broken corner); inline opus was the substantive reviewer throughout. Net effect: design has been challenged thoroughly; honest framing applied where prompt-only mitigation is known insufficient.

Added

  • Routing Discipline (Phase L1).claude/CLAUDE.md gains a new "Routing Discipline (v3.9.2)" section before existing Routing Rules 1-5. 3 routing classes: explicit intent → proceed directly; cross-phase materials → clarify with a-d options; no-materials ambiguous → clarify. [direct-mode] byte-0 escape hatch (case-insensitive; bracket-form strict). Anti-pattern explicitly named.
  • Intent clarification protocol — new shared/references/intent_clarification_protocol.md (~200 lines): trigger condition table, pipeline phase reference (Phase 0-7 marker conventions), clarification message template (a-d options, no AskUserQuestion tool), [direct-mode] mechanism spec with 5 worked examples, v3.10 carry-over notes.
  • Phase Boundary block on 22 Bucket A agents (Phase 1) — single-phase agents (deep-research × 9, academic-paper × 7, academic-paper-reviewer × 6) gain a ## Phase Boundary (v3.9.2) block customized per agent: phase number, deliverable type, MUST-NOT cross-phase writes, MAY-READ upstream context (Phase 5 reviewers granted explicit cross-phase READ for review), explicit coexistence with skill-specific protocols (v3.6.2 / v3.6.5 / v3.6.6 / v3.6.7 / v3.7.1). 16 Bucket B/C/D agents (multi-phase / phase-orthogonal / cross-phase-meta) intentionally NOT fenced — honest framing per opus HIGH-2 (placebo prose creates false-enforcement illusion).
  • Phase-by-phase invocation contract (Phase 3) — 4 SKILL.md files gain a "Phase-by-phase Invocation Contract (v3.9.2)" section: Mode A (orchestrator-driven, default) vs Mode B (phase-by-phase cross-session resume), Bucket A enforcement scope, coexistence with skill-specific protocols.
  • Advisory verifier (Phase 4) — new scripts/check_pipeline_integrity.py: scans working directory for phaseN_*/ (N=1-6), flags STRUCTURAL finding when phase5 dir lacks DA/EIC/Ethics filenames (the #133 pattern). HEURISTIC adjacent-phase-mtime rule (--strict, default OFF). Cross-platform, user-invokable, advisory output (exit 0 on findings), JSON + text output modes. Normative filename convention documented; v3.10 envelope provenance replaces filename matching.
  • Phase Boundary coverage lint (Phase 5) — new scripts/check_v3_9_2_phase_boundary.py: enforces 22 Bucket A agents have block, 16 Bucket B/C/D agents don't, and each Bucket A block contains 4 load-bearing phrases (Phase Boundary v3.9.2, MUST NOT, MAY READ, Enforcement v3.9.2). Wired to .github/workflows/spec-consistency.yml.
  • Classification spec — new docs/design/2026-05-18-ars-v3.9.2-agent-phase-classification.md: canonical 38-agent table with 4-bucket model (A=22, B=4, C=8, D=4) + per-agent out-of-scope inflation risk column.
  • 8 behavioral smoke test fixturestests/fixtures/issue_133_routing/: cross-phase abstract+lit (the #133 root case), single-phase explicit, no-materials ambiguous, /ars-slash command, [direct-mode] byte-0 honored, mid-message NOT honored, case-insensitive accepted, full draft+abstract+lit+reviews. Honestly framed as LLM-behavior assertions with cross-model spot-check criterion (100% Opus 4.7, ≥75% Sonnet 4.6 + GPT-5.5).
  • Plugin metadata bump.claude-plugin/plugin.json version 3.8.2 → 3.9.2 (was stale; also catches v3.9.0 + v3.9.1 deferrals); description updated for 38-agent ensemble and v3.9.2 phase boundary feature.

Fixed

  • .claude/CLAUDE.md Suite version was stale at 3.9.0 — v3.9.1 ship missed bumping it (latent lint bug surfaced during v3.9.2 work). v3.9.2 atomic bump fixes this.

Tests

  • 12 new tests in scripts/test_check_pipeline_integrity.py (verifier).
  • 3 new tests in scripts/test_check_v3_9_2_phase_boundary.py (boundary coverage lint).
  • 4 additional tests after Phase 6 mid-impl review absorption (dotfiles ignored, multiple phase5 dirs independent, Unicode stem matching, nested subdir recursion).
  • Regression baseline: 1463 → 1482 passed (+19); 3 skipped + 111 subtests unchanged; 0 failures.

Out of scope (carry to v3.10 conductor, issue #134)

  • PreToolUse hook (Phase 0.1 verified Claude Code payload includes agent_type field; hook implementation requires multi-phase schema first — both deferred to v3.10).
  • Multi-phase ars_phase_writes + ars_phase_reads envelope schema (scalar ars_phase cannot represent agents like devils_advocate_agent at Phases 1/3/5 or report_compiler_agent at Phases 4/6 — design correctly with envelope, not retrofit scalar).
  • Deterministic verifier with author provenance (advisory v3.9.2 filename-heuristic version flagged FP-prone in docstring).
  • Orchestrator cross-phase intake capability (pipeline_orchestrator_agent currently keyword-matches user phrasing; cannot reconcile cross-phase artifacts without explicit user signal — this is the conductor's core feature).

Migration notes

Existing in-flight projects: no break expected. v3.9.2 only adds prompt sections and an opt-in advisory verifier. Existing slash commands (/ars-*) continue to work without change.

User-facing behavior change: if you previously dropped pre-existing materials (abstract + literature) into a fresh session without invoking a specific slash command, ARS may now clarify with a-d options instead of silent dispatch. To bypass clarification for direct agent dispatch, prefix your first message with [direct-mode]. To run the full pipeline on pre-existing materials, invoke /ars-full.

If you see a Bucket B multi-phase agent (devils_advocate, report_compiler, argument_builder, visualization) producing out-of-scope content, this is a known v3.9.2 limitation — recurrence is expected for these 4 agents until v3.10 envelope ships. Remediation: switch to orchestrator-driven Mode A via /ars-full or report the case to issue #134 with transcript excerpt.


[3.9.1] - 2026-05-18 — v3.9.0 client hardening (#129 + #130)

Two-bug hotfix surfaced by codex review of ars-codex PR #13 (vendor sync to v3.9.0 74413a4). Both bugs exist in v3.9.0 main: #129 violates the v3.9.0 §3.7 per-API degradation contract; #130 crashes a defensive lint on malformed input. Neither changes the spec or schema.

Fixed

  • #129 — OpenAlex / Crossref response-read failures now translate to *Unavailable. In scripts/openalex_client.py:_get and scripts/crossref_client.py:_get, urlopen succeeded but resp.read() / body.decode("utf-8") / json.loads() failures (socket drop mid-stream, truncated body, garbled UTF-8 body, HTML 503 page returned with 200 status) escaped the client as raw OSError / http.client.IncompleteRead / UnicodeDecodeError / JSONDecodeError. scripts/migrate_literature_corpus_to_v3_9_0.py only catches OpenAlexUnavailable / CrossrefUnavailable, so one transient response failure during a 500-entry backfill aborted the whole migration instead of dropping just the affected field. Narrow except block around read+decode+parse now catches (OSError, http.client.HTTPException, UnicodeDecodeError, json.JSONDecodeError)HTTPException covers IncompleteRead (canonical mid-stream socket drop, inherits HTTPException not OSError, R1 codex P2 closure). Mirrors the existing 5xx-skip pattern: per-API tolerant per the v3.9.0 spec §3.7 documented degradation contract and bibliography_agent.md "Triangulation Extension".

  • #130 — check_claim_audit_consistency non-string manifest_id guard. _build_manifest_index (line 644) and _build_manifest_constraint_index (line 675) used manifest_id as a dict key via setdefault(mid, set()) / out[mid] = bucket before checking type. For malformed passports where the schema validator already noted manifest_id as array / object, the index builder raised TypeError: unhashable type: 'list' and terminated lint with a traceback before validate_passport() could return the schema finding cleanly. Added isinstance(mid, str) and mid guard at both sites, matching the surrounding _check_inv_17_for_manifest / claim_id invariant-walker pattern. Schema validator still records the type mismatch — the guard just lets the lint surface findings cleanly instead of crashing.

Tests

  • scripts/test_openalex_client.py: +4 tests covering OSError on resp.read(), invalid UTF-8 body, invalid JSON body, and http.client.IncompleteRead (R1 codex P2 closure).
  • scripts/test_crossref_client.py: +4 symmetric tests.
  • scripts/test_claim_audit_schema.py: new TSManifestIdNonStringGuard class with 2 tests (manifest_id as list / dict).
  • Regression baseline: 1453 → 1463 passed (+10), 3 skipped + 111 subtests unchanged, 0 failures.

Out of scope

  • Spec / schema / CHANGELOG narrative not touched — the degradation contract is already documented in spec §3.7; this just makes code honor it.
  • ars-codex adapter sibling: the same two fixes will surface on next vendor sync (v3.9.1 → ars-codex v0.1.8). No action needed in this release.

[3.9.0] - 2026-05-17

Added

  • Cross-index triangulation as v3.7.3 contamination_signals Vector 3 (issue #102). Two new optional boolean fields (openalex_unmatched, crossref_unmatched) inside literature_corpus_entry.schema.json. Manual-entry not-rule extended symmetrically to forbid all three lookup fields (preprint flag remains exempt — heuristic, not lookup).
  • OpenAlex API protocol (deep-research/references/openalex_api_protocol.md) + production client (scripts/openalex_client.py).
  • Crossref API protocol (deep-research/references/crossref_api_protocol.md) + production client (scripts/crossref_client.py).
  • bibliography_agent.md Triangulation Extension subsection — parallel S2/OpenAlex/Crossref lookups, per-API degradation, manual exemption, R-L3-2-D constraint, per-entry ingest log format.
  • Finalizer 4-tier advisory annotation in pipeline_orchestrator_agent.md: k=1 → CONTAMINATED-COVERAGE-NOISE (or legacy CONTAMINATED-UNMATCHED for k_max=1 S2-only), k=2 → CONTAMINATED-PARTIAL-UNMATCH, k=3 → CONTAMINATED-TRIANGULATION-UNMATCHED. All tiers advisory; gate refusal list unchanged.
  • formatter_agent.md pass-through allowlist extends from 3 v3.7.3 suffixes to 9 (3 legacy + 6 v3.9.0). Refusal rules 1-10 unchanged.
  • v3.9.0 lint (scripts/check_v3_9_0_triangulation.py): set-equality on formatter allowlist, refusal-list-unchanged guard. Exact-token extraction prevents substring collisions (R3 P2 closure).
  • Migration tool (scripts/migrate_literature_corpus_to_v3_9_0.py): backfill v3.7.3 → v3.9.0; stable-fields idempotency; per-API degradation tolerant; dry-run mode; daisy-chained migration scope (pre-v3.7.3 entries require v3.7.3 migration first).
  • 3 new firm rules in spec §3.3: R-L3-2-C (k computed over present fields, absent ≠ false), R-L3-2-D (no OpenAlex primary_location.source.type / Crossref type used for v3.9.0 classification logic), R-L3-2-E (refusal list unchanged; pass-through allowlist extends).

Design philosophy

  • v3.9.0 is the measurement layer for cross-index triangulation. The policy layer (strict modes, hard-block tier, venue-type-scoped strict, triangulation_policy field, venue_type field) is deferred to v3.10 per spec §2.3.
  • The k=3 marker is CONTAMINATED-TRIANGULATION-UNMATCHED (describes observable condition), not CONTAMINATED-LIKELY-FABRICATED (would infer cause unsupportable on humanities / non-English / dissertation references where coverage gaps are real).
  • R-L3-2-A preserved verbatim: contamination signals never block emission on their own.

Migration path

  • v3.7.3 corpora: run python scripts/migrate_literature_corpus_to_v3_9_0.py PATH to backfill the two new fields.
  • Pre-v3.7.3 corpora: run python scripts/migrate_literature_corpus_to_v3_7_3.py PATH FIRST, then v3.9.0 migration (daisy-chained per spec §3.7).

Review trail

  • R1 (commit d9280bf): 15 findings (3 P0, 8 P1, 4 P2) — closed.
  • R2 (commit 7d51215): 12 findings (0 P0, 3 P1, 9 P2) — closed.
  • R3 (commit 4297c27): 4 P2 findings — closed in Task 1 of impl plan.
  • Both tracks (codex gpt-5.5 xhigh + Gemini 3.1-pro-preview) READY-FOR-IMPL after R3.

[3.8.2] - 2026-05-17 — #118 uncited audit_tool_failure surface

Fixes the #118 carry-over from #103 R3 codex P2 #5. The ARS_CLAIM_AUDIT=1 uncited constraint-judging path used to silently substitute {"judgment": "NOT_VIOLATED", "rationale": "..."} on JudgeInvocationError, suppressing HIGH-WARN constraint checks on transient judge outage (judge timeout, API 5xx, network error, etc.). v3.8.2 routes those failures through a dedicated uncited_audit_failures[] aggregate at MED-WARN advisory tier, mirroring INV-14 semantics on the cited path but using a separate schema because claim_audit_result.ref_slug is required and the uncited path has no ref to bind.

The #118 issue body listed four candidate options. Option 1 (extend constraint_violation.schema.json) would have broken the judge_verdict: const VIOLATED invariant and re-derived every CV-INV. Option 3 (overload uncited_assertions[] with a fault_class field) would have polluted the D4-c LOW-WARN advisory channel with audit-time infrastructure signal. Option 4 (re-raise JudgeInvocationError and abort the audit pass) would have dropped audit coverage for the entire run on a single transient outage — bad UX for N>50 papers running against flaky judge endpoints. Option 2 (new aggregate) ships here: structural honesty, schema integrity preserved, audit coverage preserved.

Added

  • shared/contracts/passport/uncited_audit_failure.schema.json — new aggregate per spec §3.6. Required fields: finding_id (UAF-NNN), claim_text, section_path, scoped_manifest_id, fault_class (closed enum mirroring INV-14), rationale (MUST begin with fault_class prefix), judge_model, judge_run_at, rule_version: D4-c-v1-uaf-v1. Optional manifest_claim_id (non-null when failure was against an NC-C claim-level constraint, null when against MNCs only).
  • UAF-INV-1..UAF-INV-6 lint coverage in scripts/check_claim_audit_consistency.py rule 4d:
    • UAF-INV-1: finding_id uniqueness across the aggregate
    • UAF-INV-2: scoped_manifest_id cross-array integrity
    • UAF-INV-3: (scoped_manifest_id, manifest_claim_id) pair integrity when manifest_claim_id non-null
    • UAF-INV-4: per-(sentence, manifest) dedup with key (scoped_manifest_id, section_path, claim_text_hash)
    • UAF-INV-5: rationale fault_class prefix matches the row's own fault_class field
    • UAF-INV-6: cross-aggregate exclusivity vs constraint_violations[] (VIOLATED and audit_tool_failure are mutually exclusive verdict states at per-(sentence, manifest) level)
  • Finalizer §5 MED-WARN advisory row: annotation [CLAIM-AUDIT-TOOL-FAILURE-UNCITED — <fault-class>] next to the offending sentence. Always advisory; gate passes — retry on next pipeline pass is the remediation. Formatter REFUSE list unchanged (UAF is advisory, not gate-refuse).
  • UAF_RULE_VERSION = "D4-c-v1-uaf-v1" constant in scripts/_claim_audit_constants.py for shared use by pipeline runtime and lint.
  • 18 new tests keeping the regression baseline 0 (694 → 712 tests):
    • 15 schema + lint tests in scripts/test_claim_audit_schema.py::TSUAFUncitedAuditFailureInvariants
    • 3 pipeline integration tests in scripts/test_claim_audit_pipeline.py::TP23UncitedJudgeOutageEmitsUAF proving the swallow is replaced with UAF emit and no synthetic NOT_VIOLATED leaks into any aggregate

Changed

  • scripts/claim_audit_pipeline.py: swallow site at line 1211-1224 (the synthetic NOT_VIOLATED substitution) replaced with _uncited_audit_failure_entry(...) emission + continue. Pipeline return now includes uncited_audit_failures alongside the other five aggregates.
  • docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md: amended with new §3.6 (schema + UAF-INV-1..6 + co-emission rules), §4 step 5 stream (d) routing clause, §4 step 9 fourth error-handling bullet, §5 finalizer outputs list + advisory paragraph, §6 lint rule 4d + precedence rule 6 cross-aggregate exclusivity reference.
  • academic-pipeline/agents/claim_ref_alignment_audit_agent.md: Output emission table grows seventh row for uncited_audit_failures[]. Error handling table grows from 3 failure surfaces to 4 (the new uncited-path UAF row mirrors the cited-path audit_tool_failure row).

Fixed

  • #118: uncited judge failure no longer swallowed as NOT_VIOLATED; the HIGH-WARN constraint check path is now observable on transient outage. Pre-v3.8.2 a flaky judge endpoint could silently pass a draft with a real MUST-NOT violation; v3.8.2 surfaces the operational failure at MED-WARN advisory tier so a retry pass picks it up.

Review trail

Single-PR ship after spec → TDD → impl. UAF schema design followed the design-phase brainstorming rule: option 1-4 trade-off analysis happened in conversation with the user before any code, captured in a local gitignored decision memo. Implementation followed strict TDD RED → GREEN — 15 schema/lint tests + 3 pipeline tests all failed in their intended way (no schema file, no lint logic, swallow site still active) before the schema, lint, helper, and pipeline change landed. No regression on the 694 pre-existing tests.


[3.8.1] - 2026-05-17 — claim_audit lint hardening (#119 + #120 4×P2 closure)

Defense-in-depth patch on ARS_CLAIM_AUDIT=1 opt-in lint paths. Five fixes carried over from #103 R6 + R8 independent review, consolidated into one v3.8.1 release. No schema semantic change, no behavior change for well-formed payloads — pre-fix surfaces all crashed the CLI with TypeError / AttributeError instead of returning actionable lint findings or routing through the INV-14 audit_tool_failure translation boundary.

Fixed

  • #119 / #120 P2-2 — nested schema-invalid shapes no longer crash invariant walkers. Added _iter_dicts helper and narrow isinstance(str) guards in _check_inv_17_for_manifest, _check_manifest_invariants, _build_manifest_index, _build_manifest_constraint_index so that nested claim_intent_manifests[].claims as string, claims[].claim_id non-string, or audit_sampling_summaries[].audited_indices mixed types now surface as clean schema findings instead of crashing on for claim in "broken":, regex against non-string, or int <= str comparison. The schema validator still records the type mismatch separately — narrow walker guards prevent the second-stage crash without masking schema-vs-invariant double coverage (option 2 refined, not aggregate-level skip).
  • #120 P2-1 — CV-INV-4 dedupe scoped by scoped_manifest_id. Dedupe key extended from (section_path, claim_text_hash, violated_constraint_id) to (scoped_manifest_id, section_path, claim_text_hash, violated_constraint_id). Per M-INV-4, manifest_id is unique across the passport but constraint ids (MNC-* / NC-*) are only unique WITHIN a manifest — two manifests in the same passport may legitimately carry colliding constraint ids, and the same sentence may then violate both. Pre-fix, the dedupe false-positived these as duplicates. Spec wording in §3.5 + §7.1 4b updated.
  • #120 P2-3 — judge judgment isinstance(str) guard before set membership. _validate_judge_dict now rejects a non-string judgment (e.g. malformed {"judgment": [1, 2], "rationale": "..."}) as judge_parse_error → audit_tool_failure via the INV-14 translation boundary instead of bubbling TypeError("unhashable type: 'list'") out of the set-membership test.
  • #120 P2-4 — retrieve ref_retrieval_method isinstance(str) guard before set membership. Symmetric to P2-3 on the retrieval boundary. _invoke_retrieve rejects a non-string method as retrieval_api_error → audit_tool_failure instead of crashing on set membership.

Tests

  • scripts/test_claim_audit_schema.py: 3 new tests in TS9MalformedPassportGuard (nested string / non-string claim_id / mixed-type indices) + new test class TSCVDedupeManifestScope with 2 tests (cross-manifest collision must keep both; within-manifest true duplicate still caught).
  • scripts/test_claim_audit_pipeline.py: 2 new tests in TP12JudgeFailureAuditToolFailure (non-string list + dict judgment) + 1 new test in TP14RetrieveFailureAuditToolFailure (non-string list method).
  • Regression baseline: 682 → 690 tests (+8), 0 failures, 0 errors across full scripts/test_*.py discovery.

Design memo

A local, gitignored design memo carries the option-1 vs option-2 analysis, CV-INV-4 dedupe key shape rationale, and the release-framing decision.

Closes #119. Refs #120 P2-1, P2-2, P2-3, P2-4 (all four R8 findings).


[3.8.0] - 2026-05-16 — L3 Claim-Faithfulness Locator + Audit (v3.7.3 + #103 paired milestone)

v3.7.3 + v3.8 close the L3 (claim-faithfulness) gap end-to-end. v3.7.3 ships the locator infrastructure (every citation carries a three-layer anchor so the audit can fetch the cited passage); v3.8 ships the audit pass that consumes those anchors, judges whether the cited source supports the claim, and gate-refuses HIGH-WARN violations at the formatter terminal hard gate. The release also bundles 5 audit-trail-shipped feature PRs accumulated on main since v3.7.0 (#104 / #105 / #108 / #111 / #115). External motivation: Zhao et al. arXiv:2605.07723 (2026-05) — 146,932 hallucinated citations across arXiv / bioRxiv / SSRN / PMC in 2025.

#103 — v3.8 claim ↔ reference faithfulness audit agent (2026-05-16)

Parent issue: #103 — closes the L3 (claim-faithfulness) gap left open by v3.7.3 (which closed the locator-channel half). Spec: docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md + decision doc docs/design/2026-05-15-issue-103-claim-alignment-audit-decision.md (D1-D6 settled).

Why: Zhao et al. arXiv:2605.07723 (2026-05) shows 146,932 hallucinated citations across arXiv / bioRxiv / SSRN / PMC in 2025; v3.7.3 stopped the "no locator" path but a present-but-wrong claim ↔ source mismatch was still undetected. v3.8 adds a Stage 4→5 audit pass that judges every sampled citation against its retrieved excerpt, emits 5 new passport aggregates, and drives 5 new HIGH-WARN annotation classes through the formatter terminal hard gate.

New components:

  • claim_ref_alignment_audit_agent (1 new agent, academic-pipeline/agents/) — opt-in (ARS_CLAIM_AUDIT=1, default OFF for v3.8.0) audit agent dispatched between v3.7.1 cite finalizer and formatter hard gate. Takes citations + manifests + corpus + Stage 4 draft sentence stream (full uncited + D4-c filtered subset).
  • 5 new passport schemas (shared/contracts/passport/): claim_audit_result, claim_intent_manifest, claim_drift, uncited_assertion, constraint_violation. Cross-field invariants INV-1..INV-18 / M-INV-1..M-INV-4 / U-INV-1..U-INV-4 / D-INV-1..D-INV-4 / CV-INV-1..CV-INV-4 lint-enforced (JSON Schema can't express the conditional matrix relating judgment / audit_status / defect_stage / ref_retrieval_method).
  • Runtime pipeline (scripts/claim_audit_pipeline.py) — implements §4 step 1-6 + manifest set-diff (D6 set-of-text semantics). Per-citation judge wrapping (_invoke_judge + _invoke_retrieve translate transient failures to INV-14 audit_tool_failure rows: judge_timeout / judge_api_error / judge_parse_error / cache_corruption / retrieval_api_error / retrieval_timeout / retrieval_network_error). Cache hits re-validated through the same surface. Per-manifest uncited judge calls to prevent MNC id collisions across manifests.
  • 8-row finalizer matrix (scripts/claim_audit_finalizer.py) — discriminates paywall (LOW-WARN advisory) / fabricated reference (HIGH-WARN gate-refuse) / anchorless (HIGH-WARN defense-in-depth) / audit_tool_failure (MED-WARN advisory) via ref_retrieval_method alongside (judgment, defect_stage).
  • 5 new HIGH-WARN annotation classes in formatter_agent REFUSE list: [HIGH-WARN-CLAIM-NOT-SUPPORTED] / [HIGH-WARN-NEGATIVE-CONSTRAINT-VIOLATION] / [HIGH-WARN-FABRICATED-REFERENCE] / [HIGH-WARN-CLAIM-AUDIT-ANCHORLESS] / [HIGH-WARN-CONSTRAINT-VIOLATION-UNCITED]. Mirrors v3.7.3 R-L3-1-A asymmetry — /ars-mark-read does NOT clear; remediation is fixing the prose.
  • "Claim Intent Manifest Emission" sibling section added to synthesis_agent / draft_writer_agent / report_compiler_agent per v3.6.7 PATTERN PROTECTION pattern. The §3a SHA-pinned blocks stay byte-equivalent to commit e7e775a0e1b4.
  • Calibration runner (scripts/claim_audit_calibration.py + scripts/test_claim_audit_calibration.py + scripts/fixtures/claim_audit_calibration/gold_set.json) — 20-tuple gold set (12 alignment + 8 constraint); T-C1 threshold gate (FNR < 0.15 + FPR < 0.10), T-C2 per-class FNR/FPR, T-C3 gold-set shape integrity. Re-run: PYTHONPATH=. python3 -m unittest scripts.test_claim_audit_calibration -v.
  • 2 new lints + 1 new pytest module + 7 new unittest modules wired into CI (.github/workflows/spec-consistency.yml): check_claim_audit_consistency.py (38 invariant checks + schema validation), check_v3_8_annotation_literal_sync.py (formatter-finalizer literal drift gate). Test suite: 194 unittest tests across the 7 modules.

Review trail (Step 13 dual-track, 2026-05-16): 8 rounds codex (gpt-5.5 xhigh) + 1 round Gemini 3.1-pro-preview before Gemini quota exhausted. Trajectory R1 4P1+2P2 → R2 0P1+3P2 → R3 0P1+5P2 → R4 2P1+2P2 → R5 0P1+2P2+1P3 → R6 1P1+1P2 → R7 1P1+1P2+1P3 → R8 0P1+4P2 → ship. Per feedback_codex_review_surface_loop_design_phase.md design-phase P2 noise floor doesn't auto-converge; the user declared ship signal at R8 with all P0/P1 closed and 4 R8 P2 carried over to v3.8.1 (#120).

Carry-over follow-up issues:

  • #118 — uncited path NOT_VIOLATED swallow on judge failure (schema-level decision)
  • #119 — nested schema-invalid shapes still crash invariant helpers
  • #120 — 4 R8 P2 findings (CV-INV-4 dedupe scope / invariant walker short-circuit / judgment + method type-check before set membership)

Regression baseline (post-ship):

  • pytest: 1356 passed, 3 skipped, 103 subtests (was 1107 pre-#103, +249 tests across schema / pipeline / detector / manifest / finalizer / e2e / calibration / lint coverage)
  • v3.x lints: 7/7 PASS (v3.6.7 / v3.6.8 ×4 / v3.7.3 / v3.8)
  • personal-boundary: 0 violations (614 files scanned)
  • SHA-pinned zero-touch: shared/sprint_contract.schema.json 0 lines diff, shared/contracts/passport/audit_artifact_entry.schema.json 0 lines diff against main

#115 — Semantic Scholar client maturity: throttle + outage latch (2026-05-15)

Parent issue: #115 — follow-up to #105 PR codex round-5 [P2]×2 findings (R5-2 throttle + R5-3 outage latch). Both deferred during #105 ship per architectural-inflection discipline; this entry closes the SS-client maturity gap.

Modified files:

  • scripts/semantic_scholar_client.py — two additions:
    • Throttle (#115 R5-2): new ctor params clock + min_interval_seconds. Defaults: 1.0s unauthenticated (1 req/s per protocol), auto-drops to 0.1s when S2_API_KEY detected (authenticated 10 req/s tier). Pre-request pacing tracks _last_request_at; sleeps max(0, min_interval - elapsed) before each call. First request passes through.
    • Outage latch (#115 R5-3): _latched_unavailable flag set on URLError. Subsequent lookup() calls short-circuit with SemanticScholarUnavailable without invoking urlopen. New reset_outage_latch() method lets long-running tools retry between passport batches. HTTP 5xx does NOT latch (server-side error ≠ transport outage).
  • scripts/test_semantic_scholar_client.py — 9 new tests (5 throttle: first-no-sleep / back-to-back / past-interval / authenticated-tier / override; 3 latch: URLError short-circuits / reset restores / 5xx does not latch; 1 efficiency: 429-retry refreshes throttle anchor).
  • scripts/contamination_signals.py — new reset_client_outage_latch(client) helper. Production clients implementing the outage-latch pattern expose reset_outage_latch(); mocks may not. Helper invokes when present, no-ops when absent — avoids AttributeError when callers swap clients. 2 new tests.
  • scripts/migrate_literature_corpus_to_v3_7_3.pymigrate_directory resets the SS client's outage latch between passports so a transient network blip on one passport doesn't permanently disable lookups for the rest of the directory. Within a single passport the latch still short-circuits to protect a dead service from N retry waves.

Production behavior change:

  • _build_default_ss_client() API unchanged (SemanticScholarClient() no-arg). New throttle is automatic per protocol — no migration tool changes required.
  • For a 5000-entry unauthenticated migration: same ~1.5hr runtime (already constrained by 1 req/s); now achieves it via deterministic pacing rather than 429-retry exhaustion.
  • For an authenticated migration (S2_API_KEY set): drops to 0.1s/call = ~8min for 5000 entries.
  • Network outage during large corpus: previously retried every entry independently (up to 30s timeout per entry on the slow path); now the first URLError latches the client and subsequent entries short-circuit until the next batch boundary calls reset_outage_latch(). The migrate_directory helper does this reset automatically between passports.

Out of scope: migration tool (migrate_literature_corpus_to_v3_7_3.py) — #105 partial-fill / provenance contract correct as shipped. Protocol doc — already correct; this issue is implementation alignment.

Regression: 472 unittest (+8 #115 tests) + 201 pytest adapters + spec_consistency + preprint_venues all green.

#105 — v3.7.3 contamination_signals backfill migration tool (2026-05-15)

Parent issue: #105. Spec anchor: v3.7.3 §3.2 R-L3-2-B (the deferred batch operation; bibliography_agent computes signals at ingest, this tool delivers post-hoc backfill on legacy corpora). Design: docs/design/2026-05-15-issue-105-contamination-signals-backfill-design.md.

New files:

  • scripts/contamination_signals.py — two pure-function resolvers + emission rules + SemanticScholarClient protocol. compute_preprint_signal() (Signal 1, deterministic year+venue check against 10-server closed list). compute_ss_unmatched_signal() (Signal 2, dependency-injected SS client, returns None on manual exemption + API degradation per spec).
  • scripts/migrate_literature_corpus_to_v3_7_3.py — CLI tool: [--dry-run] [--verbose] <passport_or_dir>. Uses ruamel.yaml round-trip to preserve comments + key order + quoting style. Reports processed / patched / skipped_already_migrated / skipped_insufficient_data counts. Idempotent.
  • scripts/test_contamination_signals.py — 25 unit tests covering Signal 1 (15 cases: 10 preprint venues × year boundary, non-preprint venue, missing year, missing venue), Signal 2 (6 cases: manual exemption / match / no-match / API degradation × 2 paths / unexpected exception), emission rules (4 cases).
  • scripts/test_migrate_literature_corpus_to_v3_7_3.py — 9 unittest cases covering dry-run, full migration per emission rules, idempotency, insufficient-data skip, empty-corpus passport, directory scan (non-recursive), comment preservation.
  • docs/migration/v3.7.3-contamination-signals-backfill.md — user-facing migration guide (when to run, dry-run workflow, idempotency, SS API rate-limit considerations, what's out of scope).

Modified files:

  • shared/contracts/passport/literature_corpus_entry.schema.json — purely additive: new optional contamination_signals_backfilled_at field (ISO-8601 date-time string). Existing v3.7.3 ingest-time entries (which lack this field) remain valid; pre-v3.7.3 entries (which lack both this field and contamination_signals) remain valid.
  • scripts/adapters/tests/test_literature_corpus_entry_schema.py — 3 new tests for the additive field (valid present / absent / non-string rejected).
  • requirements-dev.txt — add ruamel.yaml>=0.17.

Open-question resolutions (user-chosen 2026-05-15):

  • Q1 API rate-limit handling: backoff-only via existing SS protocol (429 → 2s × 3); no resumable checkpoint (YAGNI per minimal scope)
  • Q2 schema field naming: scalar contamination_signals_backfilled_at ISO-8601 timestamp; strictly additive upgrade path if v3.7.4 needs structured provenance
  • Q3 multi-passport batch mode: directory-scan only; no --input-list (YAGNI)
  • Q4 YAML library: ruamel.yaml round-trip to preserve user-owned passport formatting (memory feedback_toml_duplicate_table_corruption spirit)

Spec discipline (per v3.7.3 R-L3-2-B):

  • Migration is offline + opt-in: user explicitly invokes; pipeline doesn't auto-trigger
  • Idempotency keyed on contamination_signals presence: first-migration timestamp preserved across re-runs
  • obtained_via=manual exemption preserved at migration time (semantic_scholar_unmatched field omitted, matches the v3.7.3 schema cross-field rule)
  • API degradation → field omitted (NOT set to False, per "absence ≠ negative confirmation" rule)

Files explicitly NOT touched:

  • deep-research/agents/bibliography_agent.md — v3.7.3 ingest-time computation frozen
  • academic-pipeline/agents/pipeline_orchestrator_agent.md — finalizer behavior unchanged
  • Existing scripts/adapters/* — adapters produce ingest-time entries; migration is downstream

Regression status: 1053 #108 baseline + 17 #111 baseline + 25 resolver + 9 migration + 3 schema = 1107 total. All green. No regression on the existing 4 allOf cross-field invariants (manual exemption + preprint year=2024 boundary verified by adapter pytest).

#104 — README motivation: add Zhao et al. corpus-scale evidence anchor (2026-05-15)

Parent issue: #104. Doc-only — no code changes.

Adds a third evidence anchor to the ### Why human-in-the-loop, not full automation? README section, between the ARS positioning paragraph and the PaperOrchestra paragraph. Closes the gap where v3.7.x trust-and-locator machinery appeared in the codebase without its corpus-scale motivation surfaced in the public-facing README.

Modified files:

  • README.md — new Zhao et al. paragraph
  • README.zh-TW.md — translated equivalent

Three motivation anchors now read in sequence:

  • Lu et al. (Nature 651:914-919) — case-study evidence of autonomous-pipeline failure modes
  • Zhao et al. (arXiv:2605.07723) — corpus-scale evidence of the citation-faithfulness problem (111M references / 2.5M papers / 146,932 conservative 2025 estimate / mid-2024 inflection / 85.3% bioRxiv-to-PMC persistence)
  • PaperOrchestra (Song et al., arXiv:2604.05018) — method-level technique source

Discipline (#104 acceptance criteria):

  • Statistics verified directly against Zhao et al. abstract (111M / 2.5M / 146,932 / conservative qualifier) + v3.7.3 spec which carries the body-level numbers (85.3% bioRxiv→PMC specificity, mid-2024 inflection) through prior 10-round codex + gemini cross-model review.
  • No claims that v3.7.x "closes" L3 — only "adds locator infrastructure" / "advisory risk signals".
  • L3 attributed to ARS terminology, not the paper's.
  • "Motivated by" not "responds to".

#111 — slr_lineage emission on systematic-review → academic-paper full handoff (2026-05-15, unreleased)

Parent issue: #111, follow-up to #108 (PR #110, merged 70c8678) round-8 P2 #1. Design: docs/design/2026-05-15-issue-111-slr-lineage-emission-design.md.

Version label v3.7.4 below is provisional and will be confirmed at the next release sweep per feedback_version_bump_sweep_checklist.md. If this work ships as part of v3.7.3 (the in-progress release at writing time), the version stamps in this entry and the prose files below are swept to the final label at release tag.

Closes the pipeline-plumbing gap surfaced by #108: disclosure --policy-anchor=prisma-trAIce now dispatches automatically when the documented deep-research systematic-review → academic-paper full → disclosure path runs, without the user manually supplying mode=systematic-review at cold-start.

New files added:

  • scripts/slr_lineage.py — two pure functions: (a) resolve_from_stages(stages) returns True iff any stage was produced by deep-research in systematic-review mode (bound to the deep-research producer specifically — a non-deep-research stage carrying mode='systematic-review' does NOT trigger SLR lineage); (b) emit(stages, incoming_slr_lineage) is the monotonic-OR wrapper the orchestrator calls at every handoff. The OR preserves any signal already persisted on the incoming passport (load-bearing for resume_from_passport=<hash> sessions whose state_tracker.stages is empty — codex round-1 [P2] closure).
  • scripts/test_slr_lineage_emission.py — 17 conformance tests: resolver semantics (7 cases: positive / non-SLR / mid-entry / empty / alias slr / non-deep-research / missing-mode), renderer integration (3 cases: pipeline-emitted dispatches without mode_param / non-SLR still blocks / pre-#111 cold-start fallback preserved), end-to-end pipeline handoff (2 cases), and monotonic-OR emit semantics (5 cases: resume preserves true / in-session false-to-true / no-evidence false / None incoming / default arg ergonomics).

Modified files:

  • shared/handoff_schemas.md — Schema 9 Material Passport gains optional top-level slr_lineage: boolean row + dedicated "Run-level lineage signal (v3.7.4)" subsection documenting semantics, producer, consumer, backward compat, and G1 boundary note (passport-level vs corpus-entry-level distinction).
  • academic-pipeline/agents/pipeline_orchestrator_agent.md — §4 Transition Management gains a "Run-level lineage emission (v3.7.4+)" step computed at every handoff transition before dispatch. Passport carry-line updated to reference slr_lineage from v3.7.4+.

Files explicitly NOT touched (matches #111 §Scope out-of-scope):

  • scripts/policy_anchor_disclosure_referee.py — #108 referee, contract unchanged
  • academic-paper/references/policy_anchor_disclosure_protocol.md — #108 protocol, unchanged
  • academic-paper/references/policy_anchor_table.md — #108 anchor table, unchanged
  • academic-paper/references/disclosure_mode_protocol.md — already references slr_lineage as pipeline-supplied
  • shared/contracts/passport/literature_corpus_entry.schema.json — G1 invariant frozen (corpus entry schema, not passport schema)

G1 boundary clarification: Decision Doc §4.4 #11 G1 invariant scope is literature_corpus_entry.schema.json (corpus entry data schema). Schema 9 Material Passport top-level extensions follow the v3.6.3 (reset_boundary[]) / v3.6.4 (literature_corpus[]) / v3.6.7 (audit_artifact[]) precedent and are permitted per Decision Doc §4.4 #11's "non-renderer code changes for §4.4 concerns are permitted" provision.

Backward compat: passports written by pre-v3.7.4 runs lack the slr_lineage field; renderer treats absence as false (cold-start path requiring explicit mode_param='systematic-review'). Identical to pre-v3.7.4 behavior.

Regression status: 1053-baseline frozen (no #108 contract drift); +17 new tests cover this issue's acceptance criteria #1-#3 plus codex round-1 [P2] (monotonic-OR emit across resume).

#108 — AI disclosure policy-anchor renderer (2026-05-14, audit-trail-shipped)

Parent docs: Decision Doc (docs/design/2026-05-14-ai-disclosure-schema-decision.md, PR #109, merged commit 20ed72d) + implementation spec (docs/design/2026-05-14-ai-disclosure-impl-spec.md).

Migration note (G1 + G6 invariants): no migration required. Decision Doc §2.1 G1 invariant: no ai_disclosure field is added to shared/contracts/passport/literature_corpus_entry.schema.json. Decision Doc §3 G6: no deprecation horizon — legacy entries (which by §1 fact-check do not carry any AI-disclosure field today) stay byte-equivalent. The implementation extends the runtime renderer path, not the data schema.

New files added:

  • academic-paper/references/policy_anchor_table.md — 4-anchor (PRISMA-trAIce / ICMJE / Nature / IEEE) × 16-field source-of-truth reference table carrying verbatim policy quotes lifted from discovery doc §4.3-4.6 (PR #107, commit 299c4b6) + per-anchor renderer rules.
  • academic-paper/references/policy_anchor_disclosure_protocol.md — LLM-prose runtime protocol for the new --policy-anchor=<a> track: 7-section flow covering inputs / G10 7-row precedence table / per-anchor render flows / auto-promotion forbiddance / venue-anchor conflict resolution / three-state completeness flag / 11-concern resolution map.
  • shared/policy_data/nature_policy.md — canonical Nature substantive policy source; both the policy-anchor track and the v3.2 venue track cross-reference this path for the G4 dedup invariant.
  • scripts/check_policy_anchor_table.py + scripts/test_check_policy_anchor_table.py — anchor table structural lint with 13 mutation tests + Nature dedup guard wired into the main lint command.
  • scripts/check_policy_anchor_protocol.py + scripts/test_check_policy_anchor_protocol.py — protocol doc lint with 12 mutation tests covering §4.3 8 invariants + §4.4 11 concerns + G10 7-row precedence table + auto-promotion forbiddance + anchor inventory closed-enum.
  • scripts/policy_anchor_disclosure_referee.py + scripts/test_policy_anchor_disclosure.py — executable specification (referee) of §3 G10 7-row decision table + 8 invariant predicates; 61 conformance tests covering every (input × expected output) combination + forbidden-path negative fixtures.

Modified files:

  • academic-paper/references/disclosure_mode_protocol.md--policy-anchor=<a> track added in parallel to v3.2 --venue=<v> track. Phase 1 dispatch becomes selector-aware (step 1a / step 1b venue / step 1c anchor). Venue-only flow unchanged; anchor flow delegates Phase 3+4 to policy_anchor_disclosure_protocol.md. Concern #7 venue+anchor conflict resolution enforced.
  • academic-paper/references/venue_disclosure_policies.md — Nature entry gains derivation note + dedup pointer to shared/policy_data/nature_policy.md. v3.2 venue rendering content unchanged (derived view, manual sync to canonical source until future refactor).
  • .github/workflows/spec-consistency.yml — 5 new CI steps wiring the new validators and conformance test suite into the existing spec-consistency job.

§4.4 11 open concerns resolved (4 user-chosen, 7 inline; full table in impl spec §3):

  1. Track-selection lookup: explicit slr_lineage input from pipeline orchestrator (user-chosen).
  2. Tool identity collection: auto-detect from session metadata (mirror v3.2 Phase 4).
  3. Prompt scope: per-(tool × task) tuple per PRISMA M6.a.
  4. IEEE section locator: free-form list with recommended IMRaD exemplars.
  5. Nature image metadata: hybrid output channel (annotation block + suggested inline patches) (user-chosen).
  6. UNCERTAIN per-facet finalization: USED-full + per-facet annotation alongside still-UNCERTAIN (user-chosen).
  7. Venue+anchor conflict: reject conflicting selectors with explicit error.
  8. Three-state completeness flag: full computation logic encoded in §6 of protocol doc.
  9. Test set scope: 86 new tests covering 8 invariants + 10 concerns × {positive, negative}.
  10. ai_used:true substantive-content gate: force v3.2 categorization flow (user-chosen).
  11. G1 invariant scope: data layer untouched; non-renderer pipeline plumbing permitted.

Known follow-up (out of #108 scope): the academic-pipeline orchestrator does not yet emit slr_lineage on the documented systematic-review → academic-paper full handoff. Authors targeting --policy-anchor=prisma-trAIce must supply mode=systematic-review manually until that plumbing lands in a separate PR (touches academic-pipeline/ + shared/handoff_schemas.md, outside §4.1 items 1-5 NO-CHANGE boundary).

Regression status: 967 baseline + 86 new tests = 1053 passing / 3 skipped / 0 failed. Public-repo boundary clean. Eight rounds of codex gpt-5.5 xhigh review (R1 4 P2 → R8 2 P2); shipped audit-trail-complete per user decision rather than pushing past Decision Doc 11-round high water mark. R8 P2 #1 captured as the known follow-up above.

v3.7.3 — claim faithfulness locator + contaminated-source advisory (2026-05-12, in progress)

External motivation: Zhao, Wang, Stuart, De Vaan, Ginsparg, Yin "LLM hallucinations in the wild: Large-scale evidence from non-existent citations" (arXiv:2605.07723, 2026-05). Corpus-scale audit of 111M references across 2.5M papers across arXiv / bioRxiv / SSRN / PMC finds 146,932 hallucinated citations estimated for 2025 alone, with the inflection point at mid-2024, 85.3% of preprint hallucinations surviving into the published record, and Google Scholar increasingly indexing citation-only entries. The paper names the L3 (claim faithfulness) gap explicitly: "real citations deployed to support claims the cited references do not actually make ... remains an open challenge for which reliable detection methods remain under active development." v3.7.3 closes the locator-channel half of that gap (anchor infrastructure for future L3 audit) and surfaces two contamination signals (preprint post-LLM-inflection + Semantic Scholar unmatched) as advisory cite-time markers.

L3-1 — Three-Layer Citation Emission (claim faithfulness locator):

  • deep-research/agents/synthesis_agent.md, academic-paper/agents/draft_writer_agent.md, deep-research/agents/report_compiler_agent.md gain ## Three-Layer Citation Emission (v3.7.3) H2 section that extends v3.7.1 Two-Layer with a third hidden marker: <!--anchor:<kind>:<value>--> where <kind>{quote, page, section, paragraph, none}. Production-mandatory locator rule (R-L3-1-A) requires <kind>none for every visible citation; emitting none triggers finalizer MED-WARN-NO-LOCATOR (gate-refused). Quote anchors capped at 25 words by whitespace split (R-L3-1-B). Anchor values come from corpus context only — no frontmatter reads (R-L3-1-C, inherits v3.6.7 partial-inversion discipline).
  • academic-pipeline/agents/pipeline_orchestrator_agent.md gains a ## Cite-Time Provenance Finalizer — v3.7.3 extension H2 section: 4-cell matrix becomes 5-cell along a new precedence-zero locator-presence axis. NO-LOCATOR resolution: [UNVERIFIED CITATION — NO QUOTE OR PAGE LOCATOR]<!--ref:slug--><!--anchor:none:-->.
  • academic-paper/agents/formatter_agent.md gains a ## Cite-Time Provenance Hard Gate (v3.7.1 + v3.7.3) section formalizing the terminal hard-gate refusal across all three v3.7.x severity tiers (HIGH-WARN-NO-ORIGINAL, MED-WARN-NOT-CROSS-CHECKED, MED-WARN-NO-LOCATOR).

L3-2 — Contaminated-source advisory signals:

  • shared/contracts/passport/literature_corpus_entry.schema.json adds optional contamination_signals: { preprint_post_llm_inflection, semantic_scholar_unmatched } object. Both sub-fields optional within the object; both default to absent (signals not computed). additionalProperties: false enforced on the sub-object. Backward compat: entries without the field stay valid.
  • deep-research/agents/bibliography_agent.md gains ## Contamination Signal Computation (v3.7.3) section. Signal 1 (preprint_post_llm_inflection): year >= 2024 AND venue ∈ {arXiv, bioRxiv, medRxiv, SSRN, Research Square, Preprints.org}. Signal 2 (semantic_scholar_unmatched): existing Semantic Scholar API protocol returns no match by DOI or title; exempted when obtained_via: manual; omitted (not false) on API degradation.
  • Pipeline finalizer (in pipeline_orchestrator) annotates ok / LOW-WARN markers with CONTAMINATED-PREPRINT / CONTAMINATED-UNMATCHED / CONTAMINATED-PREPRINT+UNMATCHED suffix per contamination_signals state. Annotations are advisory only — they do NOT change the gate decision (v3.5 Collaboration Depth Observer precedent).

Lint + tests:

  • New scripts/check_v3_7_3_three_layer_citation.py static lint: every <!--ref:slug--> must be followed by <!--anchor:<kind>:<value>-->; quote values ≤25 words; orphan anchors rejected.
  • New scripts/test_check_v3_7_3_three_layer_citation.py: 14 tests covering positive (5 kinds × passing cases, contamination-suffix marker, LOW-WARN-resolved marker, multi-citation) + negative (bare ref, orphan anchor, invalid kind, 26-word quote).
  • New 6 contamination_signals tests in scripts/adapters/tests/test_literature_corpus_entry_schema.py: absence / empty / both-false / both-true / unknown-subfield-rejected / non-boolean-rejected.
  • New V373ExtensionLineBudgetTest in scripts/test_v3_6_7_phase_6_6.py: 60-line budget for ## Cite-Time Provenance Finalizer — v3.7.3 extension block; existing Phase 6.6 +60 v3.6.7 budget test updated to subtract both v3.7.1 Step 3b AND v3.7.3 extension lines.

Regression status (final, post round-10 convergence): 967 tests pass, 3 skipped, 0 failed (42 new tests across rounds 1-10 fixes; pre-review baseline was 925). v3.6.7 + v3.6.8 + v3.7.1 + v3.7.2 lints all PASS unmodified. v3.6.7 PATTERN PROTECTION blocks remain byte-equivalent (SHA gate v2 unchanged). Material Passport literature_corpus_entry schema backward compatible (new contamination_signals field optional; cross-field rules only fire when explicitly set). New v3.7.3 lint wired into spec-consistency.yml CI workflow per F18.

Cross-model review closure (2026-05-12, 11 rounds total — 10 codex + 1 gemini cross-model):

Round Reviewer Findings Closures
1 (initial) Codex 0 P1 / 2 P2 F3 (untracked artifacts → closed at commit), F4 (NO-LOCATOR acknowledgment contradiction → removed /ars-mark-read promise from formatter+finalizer+spec Q5)
1 (initial) Gemini 3.1-pro-preview 2 P1 / 2 P2 / 1 P3 F1 (hyphen-encode → 3 prompts + lint + 3 tests), F2 (whitespace/newline tolerance → finalizer clarification + 4 tests), F5 (year<2024 schema cross-field → allOf + 4 tests), F6 (venue list 6 → 10 added ChemRxiv / EarthArXiv / OSF Preprints / TechRxiv), F7 (fenced code block isolation → helper + 4 tests)
2 Codex 0 P1 / 2 P2 F8 (lint regex widened to {0,2} suffix tokens → 3 tests), F9 (empty non-none anchor value rejection → 5 tests)
3 Codex 0 P1 / 2 P2 F10 (premature HTML comment terminator sentinel scan → 3 tests), F11 (schema manual-entry exemption → 4 tests)
4 Codex 0 P1 / 1 P2 / 1 P3 F12 (orphan_pattern lookbehind removed → 3 tests), F13 (schema venue list description sync 6 → 10)
5 Codex 0 P1 / 1 P2 F14 (malformed ref broad-scan detector → 4 tests)
6 Codex 0 P1 / 1 P2 F15 (prompt-vs-lint alignment on -- rule → 2 tests; prompts loosened to match lint's narrower contract)
7 Codex 0 P1 / 3 P2 F16 (finalizer status-suffix-tolerant for revision-loop reruns), F17 (standalone deep-research self-gate), F18 (CI workflow wires v3.7.3 lint into spec-consistency.yml)
8 Codex 0 P1 / 3 P2 F19 (decode value before empty check → 3 tests), F20 (formatter raw anchor:none gate), F21 (F17 self-gate scoped to standalone mode only via prompt mode-detection)
9 Codex 0 P1 / 1 P2 F22 (self-gate also rejects bare refs without anchor — parity with pipeline finalizer's precedence-zero "no anchor = anchor=none" rule)
10 (final) Codex 0 findings Convergence achieved.
  • No cross-finding overlap across reviewers. Codex and Gemini found complementary defect classes — Codex caught contract gaps + regex completeness + architectural integration; Gemini caught HTML comment parsing edge cases + cross-field schema rules + venue completeness. This is the canonical value split documented in feedback_codex_workflow_consolidated.md.
  • Cascade pattern: each round's closure introduced no new defects in its OWN scope, but interactions with other v3.7.3 surfaces surfaced new layers — F19 was an F9 layer (encoded-whitespace bypass after the F9 raw-value fix), F21 was a F17 regression (self-gate ran in pipeline mode and interfered with finalizer), F22 was an F17+F21 boundary (only catching explicit none markers missed bare-ref legacy form). The 10-round convergence trajectory is consistent with the v3.6.8 18-round implementation precedent and feedback_complex_spec_review_inventory_pattern.md.
  • F23+ not yet observed. Round 10 returned no findings on the 9th amended branch state, providing the convergence signal. Future codex challenge mode (adversarial scope) may surface architecturally deeper gaps; tracked separately as a v3.7.4+ concern.

Out of v3.7.3 scope (tracked as follow-up issues):

  • v3.7.4 retrieval-side hardening: OpenAlex + Crossref triangulation as second contamination signal (Vector 2 currently single-source via Semantic Scholar only).
  • v3.8 L3 full audit: claim_ref_alignment_audit_agent running LLM-as-judge over (claim, ref full-text) pairs. v3.7.3 anchors are the input; v3.8 verifies anchor content faithfulness.
  • AI disclosure schema split (per-stage: drafting / editing / reference suggestion / data analysis) — Zhao et al. Fig. 1l correlates AI-writing-signature with hallucination rate.
  • Public README motivation update citing arXiv:2605.07723.
  • Migration tool for legacy literature_corpus[] entries lacking contamination_signals.

Spec: docs/design/2026-05-12-ars-v3.7.3-claim-faithfulness-and-contaminated-source-spec.md.

Backlog — gbrain harness borrow analysis (2026-05-10, post codex review)

Source: 2026-05-10 analysis of garrytan/gbrain (14.2k★ agent harness for OpenClaw/Hermes), with codex cross-model review same day. Two candidates surfaced; they have different risk profiles and are tracked separately.

Candidate A — Shared shared/_invariants.md cross-skill rules file (gbrain pattern P3). Status: backlog, low-risk.

ARS cross-cutting rules are scattered today: Iron Rules in adapter overview, hedging contract in protected_hedging_phrases.md, citation precedence in agents' frontmatter, integrity gates referenced from multiple SKILL.md. When a rule evolves (e.g. v3.6.5 corpus protocol Iron Rules), secondary mentions drift.

Shape if adopted:

  • shared/_invariants.md enumerating positive invariants only (no rejected-reasoning column; that was the contamination vector in the 2026-05-10 anti-pattern-table evaluation)
  • File stays short, normative, and example-free — additional examples turn invariants into demonstrations and re-introduce few-shot drift
  • Each SKILL.md references it via a stronger convention than ## See Also (which reads as optional reading); proposed wording at adoption time
  • Frontmatter validated_against: <version> enables a stale-reference grep job on minor bumps. The grep job detects version drift only — it does NOT validate semantic compliance. Semantic checks remain a human / codex review responsibility.

Candidate B — Declarative shared/_review_pairs.yaml cross-model review config (gbrain pattern P6). Status: needs design spike before becoming a real candidate, higher-risk.

ARS cross-model review is currently invoked imperatively: ARS_CROSS_MODEL=1 env flag + manual codex review per phase. A declarative (deliverable_kind, reviewer_model, dimensions, when_to_invoke) map could improve reproducibility for Stage 2.5 / 4.5 integrity gates and Phase 6 in-pair evaluator review.

Three open problems before this is shippable:

  1. Refusal-routing semantics conflict. gbrain's chain (primary → DeepSeek → Qwen → Groq, silent switch) routes past refusal; ARS treats reviewer disagreement as signal. Borrowing the YAML format without resolving this imports the wrong invariant. Likely answer is "borrow the declarative-pairing shape, drop the refusal-routing chain entirely."
  2. Embedding governance in config. A YAML that decides "this deliverable triggers this reviewer with these dimensions" is workflow policy. Wrong shape locks in a bad routing decision across all phases. Needs a usage survey of existing manual invocations before designing the schema.
  3. Lower confidence than Candidate A. ARS already has review phases and cross-model invocation working manually; the missing piece is reproducibility, not the capability. If manual invocation isn't causing missed reviews or inconsistent reviews in practice, this should drop too.

Rejected from same gbrain analysis: P1 RESOLVER.md dispatcher (10 slash commands serve dispatch), P4 trust boundary (research tool, no untrusted caller class), P5 pain-triggered subagent routing (covered in user CLAUDE.md, repo-level not relevant). P2 friction protocol is a soft reject — codex review pointed out a first-class friction CLI captures pain at the moment of pain, which 5+ round codex review at deliverable-time does not. Re-examine if ARS skill development surfaces recurring author-time pain that retrospective review doesn't capture.

Meta-lesson from this analysis: "we already do something adjacent" is weaker than it sounds as a reject reason. The test is whether the existing mechanism captures the same signal at the same time with the same enforcement strength.

Added (v3.6.7 Step 6 Phase 6.8 — Step 8 evaluation case)

  • 17 micro-fixtures + 1 chapter-level integration fixture under tests/fixtures/v3_6_7_pattern_eval/ exercising the 17 numbered downstream -agent patterns (A1A5, B1B5, C1C3, D1D4) per spec §7. Each micro fixture: manifest.json (fixture_kind: "micro") + upstream_context/ (passport_snippet.yaml + prior_artifacts/) + bad_run/ + good_run/ with deliverable.md, expected_audit_findings.yaml, expected_orchestrator_action.yaml. Integration fixture under integration/chapter_level_run/ exercises A3+C2+D4+C1 across 3-round MATERIAL escalation → ship_with_known_residue acknowledgement per §7.3.
  • scripts/check_pattern_eval_manifest.py — fixture_kind discriminator routing micro (§7.2) vs integration (§7.3) JSON Schema 2020-12 manifest schemas; audit_verdict.schema.json validation on every expected_audit_findings.yaml; path-safety rejects absolute paths and .. segments; coverage cross-check enforces 17/17 numbered IDs covered (with hard-fail on unknown directory names per §7.5).
  • scripts/test_pattern_eval_runtime.py — 112-test parametrized harness reading expected verdicts as synthesized output and asserting against expected orchestrator action. Per-pattern parametrized tests (BAD signal + GOOD passes + run_id F1 regex + BAD/GOOD uniqueness); integration state runner driving §7.3 5-step procedure (load verdicts → drive §5.6 → verify pipeline state per round → feed escalation user_response → verify final passport state); Path A re-verification axis (≥6 A7 happy-path legs at rounds 2+3); finding-id lineage carry-forward per audit-template Section 6; per-phase synthetic injections (24 of 26 PHASE_TO_PASSPORT_MUTATION rows validated for "none" / "appended"); A1.5 supersession-preflight axis tests.
  • scripts/test_run_codex_audit_e2e.py — Phase 6.1 deferred end-to-end dispatch test (Linux Bash 4+ only; macOS stock Bash 3.2 self-skips). Mocks codex CLI via PATH-prefix shim emitting canonical Phase 2 JSONL stream. Validates wrapper produces 4 contract files + 3 diagnostic files; proposal entry validates against audit_artifact_entry.schema.json --mode proposal (Pattern C3 defense — verified_at/verified_by absent); --dry-run writes nothing; --round=2 without --previous-findings rejected with EX_USAGE.
  • .github/workflows/spec-consistency.yml — 4 new CI steps: Phase 6.8 manifest validation, pattern-eval-unit (micro fixtures + phase inventory + synthetic non-supersession), pattern-eval-integration (integration fixture
    • synthetic supersession), Phase 6.1 wrapper E2E (Linux runner only).
  • docs/design/TODO-l-doc-1-18-patterns-prose-retirement.md — files L-doc-1 follow-up enumerating 8 retirement locations for the docs-only PR retiring "18 patterns" prose to "17 patterns" per §9.2.
  • Spec amendments at docs/design/2026-04-30-ars-v3.6.7-step-6-orchestrator -hooks-spec.md: §7.4 success criterion 1 prose updated for C2 MINOR special case + D2 PASS convergence-policy assertion; §7.4 phase example updated escalationB11; §7.6 deployment note explaining named-step CI deployment (vs literal "two separate jobs"); §9.2 L-doc-1 row points at the TODO file; §7.3 example manifest snippet updated to F-101/F-103.

Notes

  • 11 codex review rounds converged to 0 findings. Cumulative 24 findings closed (4 P1 + 18 P2 + 2 P3) across rounds 1-10.
  • 135 Phase 6.8-specific tests; total repo regression 742 pytest + 251 unittest = 993 green + 3 skipped (macOS Bash 3.2 wrapper E2E gate).
  • v3.6.7 Step 6 + Step 8 now structurally complete: prompt-level pattern protection (Step 1+2) + version sweep (Step 7) + runtime audit-artifact gate (Step 6 §1-§11 + Phases 6.1-6.7) + synthetic evaluation case (Phase 6.8) deliver the §10 ship-quality target.

[3.7.0] - 2026-05-05

Claude Code plugin packaging. ARS now installs in one line on Claude Code CLI / VS Code / JetBrains via /plugin marketplace add Imbad0202/academic-research-skills

  • /plugin install academic-research-skills. The traditional git clone + symlink to ~/.claude/skills/ flow continues to work — both tracks are first-class.

Added

  • Plugin manifest + marketplace metadata (Phase 1, PR #68). .claude-plugin/plugin.json declares the suite. .claude-plugin/marketplace.json registers the plugin so a single GitHub-hosted endpoint serves both the marketplace listing and the plugin source. skills/ directory carries relative symlinks to the four existing skill directories so the plugin loader auto-discovers them without moving repo layout.
  • 10 slash commands at commands/ars-*.md (Phase 2.1, PR #69) mapping MODE_REGISTRY.md entries to /ars-<mode> triggers. Model routing pinned in each command's frontmatter — opus for full and revision-coach (architectural / review-interpretation depth), sonnet for the other 8. No Haiku per feedback_no_haiku.md.
  • 3 plugin-shipped agents at agents/*_agent.md (Phase 2.1, PR #69) as relative symlinks to the v3.6.7-hardened downstream agents in deep-research/agents/: synthesis_agent, research_architect_agent, report_compiler_agent. Underscore filenames preserved to match scripts/check_v3_6_7_pattern_protection.py hard-pinned paths and the INV-3 manifest-confined Clause 1 invariant. Symlinks (not copies) preserve a single source of truth and prevent the Pattern C3 attack surface that v3.6.7 §6 inversion sweep + INV-1/2/3 lint closes.
  • model: inherit added to those three source agent frontmatters (PR #69 R1 codex finding). Inherit chosen over pinning sonnet so an Opus session running the full pipeline keeps Opus agents (instead of being capped) while the user's existing PreToolUse warn-agent-no-model.sh hook gates Haiku at the dispatch boundary.
  • SessionStart announce hook at hooks/hooks.json + scripts/announce-ars-loaded.sh (Phase 2.2, PR #70). When the plugin loads, the hook injects additionalContext listing the 10 slash commands, the 3 plugin agents, and a token-budget pointer into the LLM's first turn. startup and clear source values get the full announce; resume and compact get a one-line ack to avoid burning context on every resume. Bash 3.2 compatible — runs on macOS stock /bin/bash with no brew install bash requirement. ${CLAUDE_PLUGIN_ROOT} quoted for install paths containing spaces.
  • docs/PERFORMANCE.md + .zh-TW.md subsection "v3.7.0 Plugin agents and model routing" explaining model: inherit semantics and the current 3-agent scope boundary.
  • docs/ARCHITECTURE.md Evolution Timeline extended with v3.6.7 / v3.6.8 / v3.7.0 entries.
  • README + README.zh-TW version badge bumped to v3.7.0; Pipeline section heading bumped to v3.7; CHANGELOG entry added.

Deferred (future release)

  • SubagentStop → run_codex_audit.sh codex audit hook (Phase 2.2 scope reduction). Two compounding reasons: (a) wrong invoker class — run_codex_audit.sh lines 47 forbid same-session in-LLM invocation (Pattern C3 attack surface), and the original PostToolUse Write|Edit matcher would fire from inside the producing session; (b) contract gap — the SubagentStop hook payload carries no stage/deliverable info, so a wrapper would have to half-infer those required arguments. Real audit-hook integration deferred to a future release when ARS gains a stage/deliverable propagation contract. See docs/design/2026-04-30-ars-v3.7.0-plugin-packaging-roadmap.md Update note 2026-05-05 (Phase 2.2 scope reduction).

Changed

  • academic-pipeline/SKILL.md frontmatter version: "3.7.0" + H1 + Version Info table.
  • MODE_REGISTRY.md Last updated bumped to v3.7.0 (2026-05-05).
  • .claude/CLAUDE.md Skills Overview row + Suite version footer bumped to 3.7.0.
  • scripts/check_spec_consistency.py lint pins (Suite version, README badge, MODE_REGISTRY heading, CHANGELOG section heading) bumped to v3.7.0.

Unchanged

The four skill directories, all 25 modes, agent prompts, schema files, and lint contracts. Plugin packaging only adds new top-level surface (commands/, agents/, hooks/, .claude-plugin/, skills/ symlink dir, three plugin-agent model: inherit frontmatter additions). Existing 4.3k clone-install users see no breaking change.

Codex review chain

8 inline iterative rounds + 3 fresh PR-level rounds across the three PRs (#68 / #69 / #70), all converging to 0 P0/P1/P2 findings before merge. The Phase 2.2 fresh PR review caught one P2 (unquoted ${CLAUDE_PLUGIN_ROOT} breaking install paths with spaces) that the inline rounds missed — confirms the value of separating implementation review (inline) from contract / install-time review (fresh). Reference: feedback_codex_review_vs_resume_audit_scope.md.

[3.6.8] - 2026-05-03

Naming note: this release ships the v3.6.6 generator-evaluator contract spec (docs/design/2026-04-27-ars-v3.6.6-generator-evaluator-contract-design.md) and its implementation. The v3.6.6 work landed after v3.6.7 due to project sequencing; the design doc retains the v3.6.6 internal naming for the contract gate version (writer_full / evaluator_full mode, Schema 13.1, pre_commitment_artifacts + disagreement_handling schema fields), while the suite release is tagged v3.6.8 to keep the CHANGELOG monotonic.

Added

  • Schema 13.1 generator-evaluator contract gate for academic-paper full mode (shared/sprint_contract.schema.json, design doc §3): two new mode enum values (writer_full + evaluator_full); two new optional top-level fields (pre_commitment_artifacts writer-only with acceptance_criteria_paraphrase.minimum_dimensions; disagreement_handling evaluator-only with paraphrase_minimum_dimensions + scoring_plan + pre_commitment_check_protocol + disagreement_resolution); 12 allOf branches enforcing reviewer- / writer- / evaluator-conditional gates (existing 2 + 10 new per design doc §3.5 table).
  • Two new shipped contract templates: shared/contracts/writer/full.json (writer dimensions D1 section_completeness / D2 citation_density / D3 argument_blueprint_fidelity / D4 total_word_count / D5 per_section_word_count / D6 acknowledged_limitations / D7 register_consistency; F-conditions F1/F4/F2/F3/F0; no scoring_plan) and shared/contracts/evaluator/full.json (evaluator dimensions D1 originality / D2 methodological_rigor / D3 evidence_sufficiency / D4 argument_coherence / D5 writing_quality; F-conditions F1/F2/F3/F6/F4/F5/F0; full scoring_plan + disagreement_handling). Templates already shipped on the spec branch as design-time artefacts since 2026-04-28; this release promotes them to live status atomically with the Schema 13.1 upgrade.
  • Two-phase orchestration inside academic-paper full mode (design doc §5): Phase 4 splits into Phase 4a paper-blind writer pre-commitment + Phase 4b paper-visible drafting + self-scoring. Phase 6 splits into Phase 6a paper-blind evaluator pre-commitment + Phase 6b paper-visible scoring + decision. Phase-numbered <phase4a_output> / <phase6a_output> data delimiters mirror the v3.6.2 reviewer pattern. Lint counts: writer 3+4 / evaluator 5+5 / reviewer 5+6 (reviewer surfaces remain zero-touch per §3.6). [GENERATOR-PHASE-ABORTED] abort tag with 5% / three-month operational monitor.
  • academic-paper/SKILL.md ## v3.6.6 Generator-Evaluator Contract Protocol orchestration block (101 lines): four-call structure with system-vs-user content discipline, schema-vs-runtime emission distinction, per-phase lint, abort handling, two valid Stage 3 entry paths (standard F0/F4 + exceptional F5), cross-session resume scope. Plus a new ## Known limitations section carrying the graceful-degradation forward note (v3.6.7 candidate) + the cross-session resume pre_commitment_history[] forward note (v3.6.7+ candidate) + in-pair Phase 6 evaluator vs external academic-paper-reviewer tech debt.
  • academic-paper/agents/draft_writer_agent.md + peer_reviewer_agent.md each gain a verbatim ## v3.6.6 Generator-Evaluator Contract Protocol section with the system-prompt sub-sections for Phase 4a/4b (writer) and Phase 6a/6b (evaluator). The orchestrator includes the relevant sub-section verbatim in the system prompt for the corresponding call; user content carries contract JSON, paper metadata, delimiter blocks, and upstream artefacts per the SKILL.md discipline.
  • scripts/check_sprint_contract.py SC- mode-gating audit* (per §7.1 implementation requirement): SC-5 (measurement_procedure canonical outputs) and SC-11 (panel_size sanity) now mode-gated to mode.startswith("reviewer_") so they do not noise on clean writer / evaluator templates. SC-9 (paraphrase_minimum_dimensions exceeds dim count) extended across all three mode families: reviewer reads mp.paraphrase_minimum_dimensions, writer reads pre_commitment_artifacts.acceptance_criteria_paraphrase.minimum_dimensions, evaluator reads disagreement_handling.paraphrase_minimum_dimensions. Mode-agnostic warnings (SC-1 baseline lag, SC-2 single dimension, SC-3 no mandatory, SC-4 orphan dim ref, SC-7 conflicting actions, SC-10 unreferenced mandatory/high) unchanged.
  • 17 new validator tests (54 → 71 total): 4 writer/evaluator template positive tests; 5 schema-branch negative tests covering branches 11 / 12 / 4 / 5 / 6 hard-fail (cross-mode field leakage intentionally NOT a v3.6.6 hard-fail per §7.1 R1 settled — v3.7.x not-clause hardening is the long-term fix); 2 §3.6 reviewer regression tests (test_existing_reviewer_contracts_still_valid_under_13_1 + test_byte_equivalent_validation_for_reviewer_contracts); 6 SC-5/SC-9/SC-11 mode-gating tests.
  • scripts/check_v3_6_6_ab_manifest.py (new) implements the §7.5 manifest CI lint: schema-shape checks per §6.2 (top-level required fields with declared types; per-paper required fields; paper_id uniqueness; aggregate role counts 6+1; paper-A paper_type families 3 × 2; paper-A required judge_output_baseline; paper-C must-have known_failure_mode + failure_evidence; paper-C must-not-have judge / metrics fields); path-existence checks (mode-conditional + populated-optional); reverse-scan against fixture-orphans; exit-1-on-malformed-YAML mirrors check_sprint_contract.py convention.
  • .github/workflows/spec-consistency.yml extends the "Validate sprint contract templates" step to iterate writer + evaluator template directories alongside the existing reviewer loop, and adds a new "Validate v3.6.6 A/B fixture manifest" step running the new manifest CI lint script as an additional step inside the existing spec-consistency job.
  • tests/fixtures/v3.6.6-ab/ A/B evidence fixture stub (30 files): manifest.yaml + README.md + 6 paper-A inputs/baseline + 1 paper-C inputs/baseline + Stage 3 reviewer excerpt + 6 codex-judge baseline placeholders. manifest_lint_mode: spec_branch, fixture_version: 0.1.0. Each placeholder explains the expected populated content; real fixture data (existing deep-research synthesis reports for paper-A; v3.6.5 session log
    • Stage 3 reviewer excerpt for paper-C; codex gpt-5.5 + xhigh judge runs against paper-A baseline) populates in follow-up commits before the v3.6.6 implementation work fully completes.
  • academic-paper-reviewer/references/sprint_contract_protocol.md cross-reference noting Schema 13.1 since v3.6.6 + pointing readers at academic-paper/SKILL.md + design doc §5 for the parallel generator-evaluator protocol. The reviewer protocol itself is byte-equivalent across v3.6.2 → v3.6.8 (zero-touch promise per §3.6).

Changed

  • Suite version: v3.6.7 → v3.6.8 (per the naming note above; design doc retains v3.6.6 for the contract gate version).
  • academic-pipeline skill version bumped from v3.6.7 to v3.6.8 in the .claude/CLAUDE.md Skills Overview table.

Deferred

  • Real fixture data populate for tests/fixtures/v3.6.6-ab/ (30 placeholders → real paper-A inputs + baseline + paper-C session log + codex judge runs) lands in follow-up commits.
  • Treatment runs (writer Phase 4a/4b + evaluator Phase 6a/6b on the seven fixtures), codex judge against treatment, and **metrics computation
    • summary.md** require actual academic-paper full invocations + Semantic Scholar API + codex CLI runs; deferred to follow-up commits before the fixture-completeness work concludes.
  • manifest_lint_mode flip from spec_branch to implementation_pr co-lands with the treatment population in the same atomic merge state per §6.5 invariant 3.
  • ROADMAP §3.6.4 description correction per design doc §9.3 ("Extend v3.6.2 sprint contract pattern to the existing academic-paper writer/evaluator pair via contract-gated phase splits and Schema 13.1 conditional gates. No new agent files; existing draft_writer_agent and peer_reviewer_agent gain per-phase sub-section instructions") lands in the private ROADMAP.md (gitignored, maintained outside this public repo), not in this repo PR.

[3.6.7] - 2026-04-30

Added

  • Downstream-agent pattern protection layer (docs/design/2026-04-29-ars-v3.6.7-downstream-agent-pattern-protection-spec.md). Hardens three downstream agents against 17 hallucination/drift patterns documented in the spec: synthesis_agent (A1A5 narrative-side), the survey-designer mode of research_architect_agent (B1B5 instrument-side), and the abstract-only mode of report_compiler_agent (C1C3 publication- side), plus four cross-cutting patterns (D1D4). Patterns observed in production output across multiple chapter-length runs.
  • Four reference files in shared/references/ carrying the operational contracts that protection clauses cite:
    • irb_terminology_glossary.md — anonymity vs confidentiality vs de-identification vs pseudonymization (B1).
    • psychometric_terminology_glossary.md — true reverse-coded vs contrast item, with construct-equivalence rule (B2).
    • protected_hedging_phrases.md — five-rule contract for upstream-marked hedge protocol (conservative inclusion, anchor every entry, no duplicates, verbatim preservation, conflict reporting) (C1).
    • word_count_conventions.md — whitespace-split standard (body.split()), 35% buffer below hard cap, publisher conventions (C1).
  • Cross-model audit prompt template at shared/templates/codex_audit_multifile_template.md — seven audit dimensions (cross-ref, hallucination, primary-source integrity, internal coherence, instrument quality, Round-N framing, COI adequacy) plus a mandatory three-part Section 4(f) check for report_compiler_agent bundles (whitespace-split cap-minus-buffer, protected-hedge verbatim, abstract no less hedged than body — failure of any sub-check is P1).
  • Static lint at scripts/check_v3_6_7_pattern_protection.py enforcing protection-clause presence and obligation-phrase shape across the reference files, audit template, and three downstream agent prompts. Per-regex allow_prohibition flag scopes the prohibition exemption so prohibition-style obligations (DO NOT simulate, must not claim audit-passed state, does not paraphrase) do not leak the exemption to assertion-style obligations on the same Check. Span-restricted exemption rejects a second prohibition elsewhere in the bullet. Modal/advisory weakener coverage: may, should, can, will, would, ought to, ideally, preferably, We recommend that, is/are recommended, is/are allowed, is/are permitted, plus exception qualifiers (except, unless, save when).
  • Mutation test suite at scripts/test_check_v3_6_7_pattern_protection.py with 29 tests preserving codex review evidence (R2R6). Future checker regressions surface in CI rather than only in ad-hoc mutation runs.
  • CI wiring in .github/workflows/spec-consistency.yml runs both the static lint and the mutation suite on every push and pull request.

Changed

  • deep-research/agents/synthesis_agent.md carries a PATTERN PROTECTION (v3.6.7) block with five clauses covering effect-inventory cross-section consistency self-check, pending-verification hedge wrap, one-line anchor justification, verbatim phrase boundary on quotes, and the prohibition on declarative claims about un-provided documents (with conditional-language fallback).
  • deep-research/agents/research_architect_agent.md survey-designer mode carries a PATTERN PROTECTION (v3.6.7) block with five clauses covering IRB terminology pass-through, reverse-coded construct- equivalence justification, event-anchored retrospective default (calendar-anchored only when sample shares a common event date), neutral-balanced item phrasing with chapter argument vocabulary forbidden, and primary-source list enumerate-fully (no subsetting, no over-setting, no scope cross-contamination).
  • deep-research/agents/report_compiler_agent.md abstract-only mode carries a PATTERN PROTECTION (v3.6.7) block with three clauses covering whitespace-split word budget plus 35% buffer with budget- protected hedges, explicit-temporal-bounds reflexivity disclosure (year range / past-tense disambiguating verb / "former" prefix; deictic phrases forbidden), and the anti-fake-audit guard (DO NOT simulate any audit step; DO NOT claim to have run codex/external review; output metadata must not claim audit-passed state).

Notes

  • v3.6.7 ships in two stages. Step 1 + Step 2 (this entry) include the four reference files, the audit template, the static lint, the mutation test suite, the CI wiring, and the three agent-prompt protection blocks. Step 6 (orchestrator hooks for automatic per-agent audit and anti-fake-audit guard wiring) and Step 8 (synthetic evaluation case demonstrating all 17 patterns triggered + protected) ship in a follow-up PR. Step 6 is cross-agent runtime work that warrants its own design discussion and is intentionally decoupled from this prompt-and-lint PR.
  • Codex review history: seven rounds of gpt-5.5 + xhigh cross-model review reached SHIP-OK with zero P1 + P2 findings. R1 closed ten Step-1 findings; R2 closed four cascade gaps plus the per-Check allow_prohibition leak; R3 closed three P2 findings (span-restricted exemption, token→regex with imperative anchoring, except/unless/ save when weakeners); R4 closed three P2 findings (modal verb scope expansion, §6 sub-clause coverage, lint→CI wiring); R5 closed one P2 plus one P3 (should/can/permitted modals and the mutation test suite); R6 closed one P2 (will/would/ought to/ideally/preferably/ We-recommend-that weakeners) and explicitly deferred orchestrator runtime hooks to the Step 6 follow-up PR. R7 surfaced only one P3 add-counter signal (try to / generally / where relevant weakeners), which is non-blocking polish.
  • ARS pipeline ship-quality target updates from "each agent produces a clean v1" to "end-to-end deliverable set passes independent xhigh cross-model audit at 0 P1 + P2 finding within three rounds" (per spec §10).

[3.6.5.2] - 2026-04-27

Changed

  • docs/SETUP.md Method 4 (claude.ai) recommendation revised. Method 4b (Project + GitHub integration) is now presented first as the recommended claude.ai path, since it brings the repository into Project knowledge for reading and citation without losing fidelity. Method 4a (Custom Skill upload) is now explicitly marked as not recommended for this suite, with a rationale paragraph covering two compounding reasons:
    • ARS depends on Claude Code-only orchestration features. Each skill drives 12-13 specialised agents through Claude Code's Task / subagent tooling and Material Passport file handoffs that resume across sessions. claude.ai Custom Skills do support multi-file packages with scripts/ and code execution per Anthropic's documentation, but the Anthropic- documented scope of the claude.ai Custom Skill runtime does not include Claude Code's Task / subagent control surface or cross-session Material Passport handoffs. The recommendation is forward-looking based on those documented assumptions; we have not run a live upload to characterise the actual surfacing in claude.ai.
    • Trimming the four description fields below claude.ai's 200-character cap would weaken Claude Code and Cowork routing on the platforms the suite was actually built for. The Agent Skills specification and Claude Code Skills documentation both allow up to 1,024 characters; only claude.ai's upload UI enforces 200. Trading Claude Code and Cowork routing precision for partial functionality on the limited claude.ai path was judged not worth it.
  • Method 4a install commands kept in place for users who decide to try it anyway, framed as "if you want to try this path despite the limitations" rather than as a recommended flow. The upload UI's expected rejection on description-too-long is documented as deliberate, not an oversight to fix later.
  • docs/SETUP.zh-TW.md mirrors the English changes end-to-end.

Notes

  • Doc-only patch. No SKILL.md (frontmatter or body), no agent file, no schema, no script, no test, no workflow, and no version bump in any skill changed in this patch. The four current description fields stay at their Claude Code-native lengths (440-842 characters) so routing on Claude Code and Cowork remains intact.
  • This patch is a scope change from the v3.6.5.2 originally forecast in the v3.6.5.1 SETUP doc. The earlier plan was a description trim; on review, the trim direction was abandoned because it would have damaged Claude Code and Cowork routing to unblock a path that delivers an untested partial fit anyway. The v3.6.5.1 SETUP text's forward-promise of a description trim is removed here.
  • Issue #44 receives a single consolidated reply on this PR's merge, summarising both v3.6.5.1 (SETUP doc rewrite) and v3.6.5.2 (Method 4a recommendation), and closes there.

[3.6.5.1] - 2026-04-27

Fixed

  • docs/SETUP.md Method 3 install paths — Option A (symlink) and Option B (copy) now install each of the four skill folders separately into ~/.claude/skills/<skill-name>/, matching the <install-root>/<skill-name>/SKILL.md discovery convention. The previous text installed the whole repo under ~/.claude/skills/academic-research-skills/, which buried the four SKILL.md files one level too deep for Cowork / Claude Code discovery.
  • docs/SETUP.md Method 4 (claude.ai) restructured — split into Method 4a (Custom Skill upload via Settings → Capabilities → Skills, the standard claude.ai Skill install path) and Method 4b (Project + GitHub integration, fallback knowledge mode and not a Skill install). The previous text framed GitHub integration as a Skill install path, which conflated content retrieval with skill execution. Method 4a documents the current 200-character description cap blocker (this entry originally forecast a description trim in v3.6.5.2; see the v3.6.5.2 entry above for the actual decision — Method 4a is documented as not recommended for this suite, and descriptions remain at their Claude Code-native lengths).
  • Method 3 prerequisites — expanded from one sentence to a full prerequisites subsection covering Claude Desktop version, internet connectivity, Cowork process model, folder permissions, paid plan, and Team/Enterprise org-admin controls.
  • Method 4 prerequisites — split per sub-method. 4a documents zip structure + description cap surfacing as upload-time errors; 4b documents GitHub authentication via the Anthropic connector, private-repo App authorization, and Team/Enterprise owner-level connector enablement.
  • Cowork UI terminology — replaced "Cowork tab" / "working directory" with current Cowork UI labels: mode selector (Chat / Cowork), Tasks view, "Use an existing folder" in the left navigation panel, and Cowork Project as the canonical term.
  • Skill invocation framing — clarified that Claude uses each skill's description for relevance routing rather than literal trigger-phrase matching, and documented the Cowork / command palette and + capability picker as explicit invocation surfaces.
  • Method 4 directory table — added the scripts/ row (required for Material Passport literature_corpus[] adapters and schema validators) and refreshed the project-capacity guidance against current Anthropic Project file limits (per-file 30 MB; file count is not artificially capped at 200).
  • docs/SETUP.zh-TW.md — mirrored the English rewrite end-to-end so Traditional Chinese readers see the same structure and content for Methods 1-4.
  • QUICKSTART.md Step 1 — install commands aligned with the new Method 3 four-symlink approach.

Notes

  • Doc-only patch. No skill content (SKILL.md), no agent file, no schema, no script, and no test changed in this patch.
  • Issue #44 (philpav) reports SETUP problems on Cowork and claude.ai. v3.6.5.1 fixes the SETUP doc; this entry originally forecast a SKILL.md description-length fix in v3.6.5.2, but v3.6.5.2 instead documents Method 4a as not recommended for this suite (see the v3.6.5.2 entry above for the actual decision). Issue #44 receives a single consolidated reply and closes on v3.6.5.2 ship.

[3.6.5] - 2026-04-27

Added

  • Material Passport literature_corpus[] consumer integration in Phase 1 (deep-research/bibliography_agent + academic-paper/literature_strategist_agent). Corpus-first, search-fills-gap flow with PRE-SCREENED reproducibility block. Reproducibility for systematic-review use is preserved through Iron Rule 1 same-criteria parity plus Step 2 case C (standard external search runs even when corpus fully covers RQ subtopics).
  • academic-pipeline/references/literature_corpus_consumers.md — consumer protocol reference with four Iron Rules (Same criteria / No silent skip / No corpus mutation / Graceful fallback on parse failure) and per-consumer reading instructions.
  • scripts/check_corpus_consumer_protocol.py — CI lint enforcing nine protocol invariants with manifest-driven consumer list and stub-block opt-out.
  • scripts/corpus_consumer_manifest.json — supported-consumer manifest.

Changed

  • shared/handoff_schemas.md Schema 9 — retired the v3.6.4 "Consumer-side integration deferred to v3.6.5+" caveat; replaced with backpointer to the consumer protocol.
  • deep-research/SKILL.md 2.9.1 → 2.9.2 — bibliography_agent corpus-first flow (also syncs Version Info footer that lagged at 2.9.0).
  • academic-paper/SKILL.md 3.1.0 → 3.1.1 — literature_strategist_agent corpus-first flow.
  • academic-pipeline/SKILL.md 3.6.4 → 3.6.5 — suite version invariant.
  • .claude/CLAUDE.md, MODE_REGISTRY.md, README.md, README.zh-TW.md, scripts/check_spec_consistency.py updated for the version bump (suite version, badge, tag, changelog heading).

Notes

  • Consumer integration is presence-based: auto-engages when passport carries a non-empty literature_corpus[] and parses cleanly. Parse failures fall back to external-DB-only flow with a [CORPUS PARSE FAILURE] surface. No new env flag introduced.
  • Schema is unchanged from v3.6.4. Existing user adapters work without modification.
  • citation_compliance_agent corpus integration deferred to v3.6.6+.
  • source_pointer is not dereferenced by consumers; URI resolution remains a future source_verification_agent concern.

[3.6.4] - 2026-04-25

Added

  • Material Passport literature_corpus[] input port. Schema 9 gains an optional literature_corpus[] field defined by shared/contracts/passport/literature_corpus_entry.schema.json. Each entry carries citation_key, CSL-JSON authors, year, title, and a source_pointer back to the user's own KB. abstract and user_notes are private optional fields with copyright caveats.
  • Adapter contract (academic-pipeline/references/adapters/overview.md): language-neutral specification for producing literature_corpus entries from user-owned corpus sources. Covers fail-soft entry-level error handling, mandatory rejection_log.yaml output, deterministic ordering (sort by citation_key / source), and extension points for user-written adapters.
  • Three reference Python adapters (scripts/adapters/): folder_scan.py (filesystem of PDFs), zotero.py (Better BibTeX JSON export), obsidian.py (vault frontmatter, BibTeX-style or literature-note convention). Each ships with pytest tests, fixtures, and golden expected outputs.
  • Rejection log contract (shared/contracts/passport/rejection_log.schema.json). Always emitted; empty when no rejections; closed enum of categorical reason values.
  • CI lint + pytest job: scripts/check_literature_corpus_schema.py (schema + adapter example validation), scripts/sync_adapter_docs.py --check (schema→docs drift detector with auto-regen mode), and a new .github/workflows/pytest.yml running scripts/adapters/tests/ on path-filtered triggers.
  • _common.ensure_unique_citekey(key, existing) helper for adapters whose source already supplies a citekey (zotero, obsidian frontmatter), with sanitization to satisfy the schema pattern and a/b/...zz alpha-suffix collision disambiguation.
  • _common.path_to_file_uri(path) helper that delegates to Path.as_uri() so spaces and reserved characters in filenames are properly percent-encoded.

Changed

  • academic-pipeline/references/passport_as_reset_boundary.md: "deferred to v3.6.4, PR-B" placeholders replaced with forward references to adapters/overview.md and literature_corpus_entry.schema.json.
  • shared/handoff_schemas.md: Schema 9 optional fields table adds literature_corpus; new "Literature Corpus Input Port (v3.6.4)" subsection appended after Reset Boundary Extension.
  • academic-pipeline/SKILL.md bumped 3.6.3 → 3.6.4 (suite version invariant). Other skills retain independent semver.
  • .claude/CLAUDE.md, MODE_REGISTRY.md, README.md, README.zh-TW.md, scripts/check_spec_consistency.py updated for the version bump (suite version, badge, tag, changelog heading).

Not changed (explicit non-goals)

  • No ARS agent consumes literature_corpus[] yet. Consumer-side integration is deferred to v3.6.5+. v3.6.4 defines the input port only.
  • No PDF parsing, no text extraction, no live API clients, no authenticated library crawling. The reference adapters read filenames or local export files and never make network calls.

[3.6.3] - 2026-04-23

Added

  • Opt-in passport reset boundary via ARS_PASSPORT_RESET=1. Every FULL checkpoint becomes a context-reset boundary when the flag is set. systematic-review mode with the flag ON makes reset mandatory; other modes treat reset as the flag-gated default.
  • resume_from_passport=<hash> mode in academic-pipeline. Lets users resume a pipeline run in a fresh Claude Code session from the Material Passport ledger alone.
  • Schema 9 reset_boundary[] optional append-only field with two entry kinds (boundary, resume). Entry shape in shared/contracts/passport/reset_ledger_entry.schema.json (oneOf split with kind discriminator). Hash computed via JSON Canonical Form + SHA-256 with "000000000000" placeholder for self-reference safety. Optional pending_decision field handles MANDATORY branch choices (Stage 3 reject/restructure/abort, Stage 5 finalization) that survive the reset boundary.
  • Protocol doc: academic-pipeline/references/passport_as_reset_boundary.md (authoritative; every file mentioning ARS_PASSPORT_RESET must co-locate a reference).
  • CI lint: scripts/check_passport_reset_contract.py + unittest suite. Wired into .github/workflows/spec-consistency.yml.
  • docs/PERFORMANCE.md + docs/PERFORMANCE.zh-TW.md long-running-session subsection documenting when reset beats continuation, passport file-location convention, and empirical-measurement disclaimer.

Changed

  • academic-pipeline/agents/pipeline_orchestrator_agent.md adds §"Passport Reset Boundary (v3.6.3+)" and §"Resume Mode: resume_from_passport". FULL Checkpoint Template includes conditional reset-handoff tag slot.
  • academic-pipeline/references/pipeline_state_machine.md documents awaiting_resume transitions derived from the ledger (no out-of-band state).
  • academic-pipeline/SKILL.md adds resume_from_passport to the mode table and bumps version 3.6.2 → 3.6.3.
  • shared/handoff_schemas.md Schema 9 gains reset_boundary row + "Reset Boundary Extension (v3.6.3)" subsection with full YAML example showing both kinds.

Changed (post-P1 fixes)

  • pending_decision.options[] now carries per-branch routing ({value, next_stage, next_mode}); value uniqueness within one options array is enforced by CI lint (scripts/check_passport_reset_contract.py). The matched option's next_stage supersedes the boundary entry's advisory next field. next MAY be null when all branches terminate or no sensible default exists.
  • Exclusive advisory lock (POSIX fcntl.flock LOCK_EX, bounded timeout not exceeding 60 s, 30 s recommended) is required for the resume read-check-append sequence. Non-POSIX implementations MUST refuse to resume rather than degrade silently.

Notes

  • Flag OFF is the default. Pre-v3.6.3 behavior is preserved byte-for-byte when ARS_PASSPORT_RESET is unset or =0.
  • Out of scope (deferred to v3.6.4): examples/adapters/{folder_scan, zotero, obsidian}/ reference adapters and the literature_corpus entry shape on Schema 9.
  • No breaking changes. No existing mode behavior changes when the flag is OFF.

[3.6.2] - 2026-04-23

Added

  • Sprint Contract (Schema 13) — reviewer hard gate. shared/sprint_contract.schema.json defines machine-checkable acceptance criteria (panel_size, acceptance_dimensions, failure_conditions with severity + cross_reviewer_quantifier, measurement_procedure, optional override_ladder, bounded agent_amendments). Validator scripts/check_sprint_contract.py (schema validation + check_structural_invariants() hard check + nine soft warnings SC-1..SC-11 with SC-6 documented as dead path and SC-8 promoted to hard check). Two templates ship: shared/contracts/reviewer/full.json (panel 5) and shared/contracts/reviewer/methodology_focus.json (panel 2). Reviewer orchestration reshaped into paper-content-blind Phase 1 + paper-visible Phase 2 hard gate. Synthesizer runs three-step mechanical protocol (build matrix → evaluate with quantifier → resolve precedence). See docs/design/2026-04-23-ars-v3.6.2-sprint-contract-design.md.
  • Token cost note. Reviewer total calls under sprint contract = 2 × panel_size. For reviewer_full: 5 → 10 calls. Phase 1 input is metadata-only and output short, so real token bound is well below 2x.

Changed

  • academic-paper-reviewer v1.8.1 → v1.9.0. Five reviewer agent markdown files (EIC + methodology + domain + perspective + DA) gain Phase 1/2 protocol sections; editorial_synthesizer_agent.md gains the three-step synthesizer protocol + forbidden-operations list.
  • Harness retirement notes folded in. The prior [Unreleased] harness-retirement pass (Task A per project_ars_v3.6_execution_order.md) ships with this release — 7 negative-framing blocks rewritten to positive / split form across 7 files, no behaviour change:
    • academic-paper/agents/socratic_mentor_agent.md — Core Principles items 1, 6 (F-001)
    • deep-research/agents/socratic_mentor_agent.md — Quality Standards items 2, 3, 4 (F-002)
    • academic-paper/agents/draft_writer_agent.md — quick style check, paragraph variation, colloquialisms, transition-word usage (F-003, 4 spots)
    • academic-pipeline/agents/pipeline_orchestrator_agent.mdsplit "Prohibited Actions" (9 items, all negative) into "Scope (delegate, don't perform)" (items 1-6, positive delegation) + "Hard boundaries (never violate)" (items 7-9, kept negative as intentional safety directives for silent-failure modes: fabrication, skipped checkpoints, skipped integrity gates) (F-004)
    • academic-pipeline/agents/collaboration_depth_agent.md — Agent-specific boundaries 4 bullets (F-005)
    • academic-pipeline/SKILL.md — single-line UX guidance (F-006)
    • academic-paper/references/academic_writing_style.md — §4 Formality 3 items (F-007, discovered during apply)

Notes

  • reviewer_re_review, reviewer_calibration, reviewer_guided are reserved in the Schema 13 mode enum but ship without contract templates in v3.6.2. Those modes continue pre-v3.6.2 behaviour until a follow-up patch adds their templates.
  • reviewer_quick is intentionally excluded from the Schema 13 mode enum (Q3-A' boundary).
  • CI gate: validate-sprint-contracts step in .github/workflows/spec-consistency.yml runs the full unit test suite and validates every template under shared/contracts/reviewer/*.json against the current ARS version.
  • Kept-as-debt from harness retirement: ~50 anti-hallucination references across deep-research/, academic-paper/references/anti_leakage_protocol.md, academic-pipeline/references/ai_research_failure_modes.md, shared/agents/compliance_agent.md, shared/compliance_checkpoint_protocol.md — load-bearing integrity architecture (Lu 2026 7-mode; S2 API Tier-0; [MATERIAL GAP] taxonomy). Not retired under the iron rule clause for silent-failure domains.

[3.5.1] - 2026-04-22

Added

  • Opt-in Socratic reading-check probe. When ARS_SOCRATIC_READING_PROBE=1 is set, the Socratic Mentor fires a one-time honesty probe during goal-oriented sessions where the user has cited a specific paper. The probe asks the user to paraphrase one passage. Decline is logged without penalty. Outcome is recorded in the Research Plan Summary and flows into the Stage 6 AI Self-Reflection Report when the pipeline continues. Default OFF. Roadmap slot: v3.7.3. See deep-research/agents/socratic_mentor_agent.md §"Optional Reading Probe Layer".

Changed

  • deep-research/SKILL.md, deep-research/references/socratic_mode_protocol.md, academic-pipeline/references/process_summary_protocol.md — aligned text updates for the new probe section. No behaviour change when the env var is unset.

Version

  • Suite: 3.5.0 → 3.5.1 (patch; opt-in, default OFF, no breaking change)
  • deep-research skill: 2.9.0 → 2.9.1
  • academic-pipeline skill: 3.5.0 → 3.5.1 (tracks suite version per check_version_consistency.py invariant)

[3.5.0] - 2026-04-21

Added

  • shared/collaboration_depth_rubric.md v1.0 — canonical 4-dimension rubric (Delegation Intensity, Cognitive Vigilance, Cognitive Reallocation, Zone Classification). Based on Wang, S., & Zhang, H. (2026). "Pedagogical partnerships with generative AI in higher education: how dual cognitive pathways paradoxically enable transformative learning." International Journal of Educational Technology in Higher Education, 23:11. DOI 10.1186/s41239-026-00585-x. Licensed CC-BY-NC 4.0.
  • academic-pipeline/agents/collaboration_depth_agent.md — observer agent (Agent Team grows 3 → 4). Invoked at every FULL/SLIM checkpoint and at pipeline completion; scores user-AI collaboration pattern against the canonical rubric. Advisory only — never blocks progression. Frontmatter declares blocking: false, measures: collaboration_depth, rubric_ref: shared/collaboration_depth_rubric.md.
  • scripts/check_collaboration_depth_rubric.py + scripts/test_check_collaboration_depth_rubric.py — new lint enforces: (1) rubric file exists; (2) rubric cites Wang & Zhang 2026 with DOI; (3) rubric_version frontmatter field; (4) four canonical dimension headings; (5)/(6) any agent claiming measures: collaboration_depth references the canonical rubric path and declares blocking: false; (7)/(8) orchestrator and SKILL.md mention observer with non-blocking semantics. 10 unit tests, all green.
  • academic-pipeline/references/changelog.md row v2.8.
  • academic-pipeline/references/reinforcement_content.md row for FULL/SLIM checkpoint — IRON RULE: observer is advisory only, never blocks, never a leaderboard.

Changed

  • academic-pipeline/SKILL.md — version bump 3.3.0 → 3.4.0. Agent Team table grows to 4 rows. New "Collaboration Depth Observer" section with explicit non-blocking guarantees and distinction from integrity verification and Stage 6 self-reflection. Reference Files table adds rubric entry.
  • academic-pipeline/agents/pipeline_orchestrator_agent.md — checkpoint Steps flow amended: after state_tracker update the orchestrator invokes collaboration_depth_agent on the just-completed stage's dialogue range (FULL/SLIM only; MANDATORY integrity gates explicitly skip) and injects its output into checkpoint templates as a named "Collaboration Depth" section. FULL checkpoint template expanded with the observer block; SLIM template gains a one-line compact observer summary; MANDATORY template unchanged (integrity gates never dilute). New "Collaboration Depth Observer" subsection under §3 Checkpoint Management covers invocation, cross-model behaviour, short-stage guard, and non-blocking IRON RULE.
  • academic-pipeline/agents/state_tracker_agent.md — Write Access Control adds collaboration_depth_agent (append-only collaboration_depth_history[]). New dialogue_log_ref turn-range pointer per stage; new collaboration_depth_history[] root-level array; new append_observer_report() function (only function that writes the history; preconditions block any attempt to turn observer output into a blocking condition).
  • scripts/_skill_lint.py — new shared split_frontmatter(text) -> (dict|None, str) lenient helper, reused by the new lint.
  • Suite version bumped to 3.5.0 across README.md, README.zh-TW.md, MODE_REGISTRY.md, .claude/CLAUDE.md; new ### v3.5.0 (2026-04-21) section in both READMEs; new ## v3.5 Key Additions block in .claude/CLAUDE.md.
  • scripts/check_spec_consistency.py — README version expectations bumped to v3.5.0; MODE_REGISTRY.md last-updated expectation updated; .claude/CLAUDE.md suite version expectation updated. New embedded-changelog regression checks for ### v3.5.0 (2026-04-21) entries.

Notes

  • MANDATORY integrity checkpoints (Stages 2.5, 4.5) are not instrumented by the observer. The observer never appears in the "Flagged" line of any checkpoint. blocked_by: collaboration_depth_agent is never a legal state. The orchestrator's numbered Step 3 explicitly branches on checkpoint_type.
  • Cross-model behaviour (ARS_CROSS_MODEL): observer runs on both models; dimension disagreement > 2 points is flagged explicitly, never silently averaged. ARS_CROSS_MODEL_SAMPLE_INTERVAL escape hatch documented.
  • Short-stage guard: if the completed stage has fewer than 5 user turns, a static insufficient_evidence block is injected and the full-model observer call is skipped.
  • Credit: Wang & Zhang (2026) introduced the dual-pathway SEM and three-zone (Zone 1 / Zone 2 / Zone 3) framework that anchors the rubric's dimension operationalisation and synthesis rule.

[3.4.0] - 2026-04-20

Added

  • shared/agents/compliance_agent.md — single mode-aware agent for PRISMA-trAIce + RAISE compliance. Dispatches on compliance_mode ∈ {systematic_review, primary_research, other_evidence_synthesis}. See design spec docs/design/2026-04-20-v3.4-prisma-trAIce-raise-readcheck-design.md.
  • shared/prisma_trAIce_protocol.md — verbatim 17-item snapshot from cqh4046/PRISMA-trAIce (2025-12-10) + per-item ARS check procedure + 4-tier behaviour table. Citation: Holst et al. 2025, JMIR AI, doi:10.2196/80247.
  • shared/raise_framework.md — 4 principles (human oversight / transparency / reproducibility / fit-for-purpose) + 8-role matrix + mandatory scope disclaimer. Citation: Thomas et al. 2025, NIHR ESG Best Practice Working Group, 17 July 2025.
  • shared/compliance_checkpoint_protocol.md — Stage 2.5 / 4.5 dual-gate behaviour spec, decision precedence, override ladder, fail-loop integration, boundary behaviour for non-pipeline invocation.
  • shared/compliance_report.schema.json — Schema 12 validator (Draft 2020-12).
  • examples/compliance/fixture_sr_full_compliant.yaml, fixture_sr_missing_M4.yaml, fixture_primary_raise_weak.yaml — regression fixtures + user reference templates.
  • scripts/check_compliance_report.py + tests — Schema 12 CLI validator.
  • scripts/validate_compliance_fixtures.py + tests — YAML→JSON fixture loop used by CI.
  • scripts/check_prisma_trAIce_freshness.py + tests — non-blocking upstream-drift warning (180-day threshold).
  • .github/workflows/freshness-check.yml — weekly cron (Monday 09:00 UTC) + path-filtered push trigger for freshness check.
  • docs/PERFORMANCE.md + .zh-TW.md: new "Long-running session management" section + v3.4.0 token-cost deltas.

Changed

  • shared/handoff_schemas.md: Schema 12 pointer + Material Passport compliance_history[] (append-only audit trail).
  • academic-pipeline/SKILL.md (v3.2.2 → v3.3.0): Stage 2.5 / 4.5 extended with compliance payload; checkpoint dashboard gains compliance row.
  • deep-research/SKILL.md (v2.8.1 → v2.9.0): systematic-review mode now triggers compliance_agent at both gates.
  • academic-paper/SKILL.md (v3.0.2 → v3.1.0): full mode adds pre-finalize RAISE principles-only check (warn-only). disclosure mode unchanged and complementary.
  • .github/workflows/spec-consistency.yml: added compliance validator + unit test runner steps.
  • scripts/check_spec_consistency.py: version pins bumped.
  • README.md, README.zh-TW.md, .claude/CLAUDE.md, MODE_REGISTRY.md: suite version → 3.4.0.

Notes

  • Calibration philosophy: compliance_agent ships with transparent reporting, no hard FNR/FPR threshold. This is self-consistent with ARS's v3.3.2 task_type: open-ended truth-in-advertising annotation — publishing a hard gate would contradict the "not a benchmark task" declaration.
  • Compliance Mandatory failures in SR mode are blocking, but the 3-round override ladder preserves human-in-the-loop authority. Overrides auto-inject disclosure_addendum into the final manuscript — no detection evasion.
  • The v3.2 Failure Mode Checklist and the v3.4.0 compliance agent run in parallel at the same gates. Their scopes are non-overlapping: failure-mode checks research validity; compliance checks reporting transparency.
  • Internal numbering: compliance_report is Schema 12 (not 10). Schema 10 is Style Profile (v2.7+); Schema 11 is R&R Traceability Matrix. The plan's initial Schema 10 assignment was corrected mid-branch before Task 9.

[3.3.6] - 2026-04-15

Added

  • docs/ARCHITECTURE.md — single source of truth for pipeline structure (flow, stage × dimension matrix, data-access flow, skill dependency graph, quality gates, modes). Merged into main via PR #18.
  • docs/SETUP.md + docs/SETUP.zh-TW.md — prerequisites, API keys, Pandoc / tectonic setup, cross-model verification (ARS_CROSS_MODEL), and four installation methods.
  • docs/PERFORMANCE.md + docs/PERFORMANCE.zh-TW.md — per-mode token budgets, full-pipeline cost estimate, and recommended Claude Code settings (Agent Team, Ralph Loop, Skip Permissions).

Changed

  • README.md and README.zh-TW.md streamlined: removed the ASCII pipeline diagram and the 16-point key-feature list (superseded by docs/ARCHITECTURE.md). Setup, performance, and installation sections relocated to docs/. Skill Details now anchors version numbers and routes readers to ARCHITECTURE.md §3 for per-agent rosters.
  • scripts/check_spec_consistency.py — bumped README version expectations to v3.3.6; DOCX contract expectations (both EN and zh-TW) moved from READMEs to the new docs/SETUP.* docs; added check_setup_docs() step.
  • Suite version bumped to 3.3.6 across README.md, README.zh-TW.md, .claude/CLAUDE.md, and MODE_REGISTRY.md.

Notes

  • No functional change to any skill. Pure documentation reorganization.

[3.3.5] - 2026-04-15

Added

  • shared/benchmark_report.schema.json — JSON Schema (draft-2020-12) defining required fields for ARS benchmark reports. Catches the "n=2 author-conducted baseline" failure mode from Anthropic's automated-w2s-researcher paper.
  • shared/benchmark_report_pattern.md — narrative hub doc explaining the schema.
  • scripts/check_benchmark_report.py + tests — validator with self-scored and small-sample warnings.
  • examples/benchmark_report_template.json — fillable template.
  • repro_lock optional sub-block added to Material Passport (Schema 9 in shared/handoff_schemas.md). Configuration lockfile; NOT a deterministic replay guarantee.
  • shared/artifact_reproducibility_pattern.md — hub doc with mandatory "not a replay guarantee" disclaimer section and required stochasticity_declaration field.
  • scripts/check_repro_lock.py + tests — passport validator.
  • examples/passport_with_repro_lock.yaml — example.
  • requirements-dev.txt — formal Python dev dep manifest (pyyaml + jsonschema).

Changed

  • .github/workflows/spec-consistency.yml installs via pip install -r requirements-dev.txt instead of ad-hoc pip install.
  • academic-pipeline/references/reproducibility_audit.md cross-links to new artifact-reproducibility pattern.

[3.3.4] - 2026-04-15

Fixed

  • Embedded changelog sections in README.md and README.zh-TW.md now include the missing v3.3.3 and v3.3.2 summaries, so the README history matches the published releases.
  • scripts/check_spec_consistency.py now verifies that the README changelog summaries include the latest release entries, so future drift fails CI.

Changed

  • Suite version bumped to 3.3.4 across release-facing docs after the README changelog sync patch release.

[3.3.3] - 2026-04-15

Fixed

  • scripts/_skill_lint.py now rejects SKILL frontmatter that is missing a closing --- fence instead of silently treating the rest of the file as YAML.
  • scripts/_skill_lint.py now reports a readable error when frontmatter parses as valid YAML but not as a mapping object, instead of crashing with AttributeError.
  • Broken showcase link for the post-publication audit report corrected in both README.md and README.zh-TW.md.
  • scripts/check_spec_consistency.py now validates README relative Markdown links so future dead links fail CI.

Changed

  • DOCX generation contract aligned across README, academic-paper/SKILL.md, academic-paper/agents/formatter_agent.md, academic-pipeline/SKILL.md, and academic-pipeline/agents/pipeline_orchestrator_agent.md: direct .docx output is Pandoc-dependent, with Markdown + conversion instructions as the fallback.
  • Added regression tests covering missing closing fences and non-mapping YAML frontmatter in both lint test suites.
  • Suite version bumped to 3.3.3 across release-facing docs; academic-paper patch-bumped to 3.0.2 and academic-pipeline patch-bumped to 3.2.2.

[3.3.2] - 2026-04-15

Added

  • metadata.data_access_level field on every top-level SKILL.md. Three-tier vocabulary (raw | redacted | verified_only) declaring what kind of data each skill may consume. Inspired by the three-tier isolation pattern in Anthropic's automated-w2s-researcher (2026).
    • deep-research = raw
    • academic-paper = redacted
    • academic-paper-reviewer = verified_only
    • academic-pipeline = verified_only
  • scripts/check_data_access_level.py lint script with unit tests; wired into .github/workflows/spec-consistency.yml.
  • Pointer section in shared/handoff_schemas.md documenting the vocabulary for future skill authors.
  • metadata.task_type field on every top-level SKILL.md. Two-value vocabulary (open-ended | outcome-gradable) declaring whether the task has a scalar ground-truth metric. All current ARS skills are open-ended — the field is a truth-in-advertising signal that ARS targets domain-judgment work, not benchmark tasks.
  • scripts/check_task_type.py lint script with 4 unit tests; wired into the same CI workflow.
  • Pointer section in shared/handoff_schemas.md for the task_type vocabulary.
  • shared/ground_truth_isolation_pattern.md — narrative pattern doc explaining the three-layer model behind data_access_level and task_type. Cross-references existing protocols (S2 verification, anti-leakage, integrity gates, calibration mode). Linked from handoff_schemas.md and CONTRIBUTING.md.

Changed

  • Per-skill metadata.version patch-bumped on all 4 SKILL.md files; last_updated refreshed to 2026-04-15.
  • Suite version bumped to 3.3.2 across README.md, README.zh-TW.md, and .claude/CLAUDE.md.

[3.3.1] - 2026-04-14

Fixed

  • Public contract drift across README.md, README.zh-TW.md, .claude/CLAUDE.md, MODE_REGISTRY.md, and the affected SKILL.md files
  • Cross-model wording now matches the implemented scope: integrity sample verification and independent DA critique are shipped; sixth-reviewer peer review remains planned
  • academic-pipeline checkpoint docs now state that SLIM checkpoints still wait for explicit user confirmation
  • academic-pipeline integrity gate docs now consistently state that Stage 2.5 and Stage 4.5 cannot be skipped
  • academic-paper/SKILL.md mode-count heading and academic-paper-reviewer/SKILL.md Version Info block

Added

  • scripts/check_spec_consistency.py to catch mode-count, version-block, and forbidden-claim drift
  • .github/workflows/spec-consistency.yml to run the consistency check on pushes and pull requests

[3.3] - 2026-04-09

Added — PaperOrchestra-inspired enhancements

Integrates techniques from Song et al. (2026, arXiv:2604.05018) "PaperOrchestra: A Multi-Agent Framework for Automated AI Research Paper Writing."

  • Semantic Scholar API Verification (deep-research, academic-pipeline): Tier 0 programmatic reference verification via S2 API. Title search with Levenshtein >= 0.70 matching. DOI mismatch detection for Compound Deception Pattern #5. Bibliography deduplication via S2 IDs. Graceful degradation if API unavailable.
    • New file: deep-research/references/semantic_scholar_api_protocol.md
    • Modified: source_verification_agent, bibliography_agent, integrity_verification_agent
  • Anti-Leakage Protocol (academic-paper, deep-research): Knowledge Isolation Directive prioritizes session materials over LLM parametric memory for factual content. Flags [MATERIAL GAP] for missing content instead of silently filling from memory. Reduces Mode 5/6 failure risk.
    • New file: academic-paper/references/anti_leakage_protocol.md
    • Modified: draft_writer_agent, report_compiler_agent
  • VLM Figure Verification (academic-paper): Optional closed-loop verification of rendered figures using vision-capable LLM. 10-point checklist covering data accuracy, APA 7.0 compliance, and visual quality. Max 2 refinement iterations.
    • New file: academic-paper/references/vlm_figure_verification.md
    • Modified: visualization_agent
  • Score Trajectory Protocol (academic-pipeline): Per-dimension rubric score delta tracking across revision rounds. Detects regressions (delta < -3) and triggers mandatory checkpoint. Extends v3.2 early-stopping with dimension-level granularity.
    • New file: academic-pipeline/references/score_trajectory_protocol.md
    • Modified: integrity_review_protocol.md, handoff_schemas.md (Schema 5)
  • Stage 2 Parallelization Directive (academic-pipeline): Visualization and argument building can run in parallel after outline completion.
  • Handoff Schema Updates (shared): semantic_scholar_id field added to Bibliography source object. score_trajectory structure added to Integrity Report schema.

Version bumps: deep-research v2.8, academic-paper v3.0, academic-pipeline v3.2

[3.2] - 2026-04-09

Added — Lu 2026 integration

Integrates insights from Lu et al. (2026, Nature 651:914-919) — the first end-to-end autonomous AI research system to pass blind peer review.

  • AI Research Failure Mode Checklist (academic-pipeline): 7-mode taxonomy extending the existing 5-type citation hallucination taxonomy. Covers implementation-bug blindness, hallucinated experimental results, shortcut reliance, bug-as-insight, methodology fabrication, and pipeline-level frame-lock. Runs at Stage 2.5 and 4.5 with mandatory blocking behaviour. Reported at Stage 6 in the Failure Mode Audit Log subsection of the AI Self-Reflection Report.
    • New file: academic-pipeline/references/ai_research_failure_modes.md
  • Reviewer Calibration Mode (academic-paper-reviewer v1.8): opt-in mode that measures FNR / FPR / balanced accuracy / AUC against a user-supplied gold-standard set of 5-20 papers. Uses 5x ensembling with fresh context per run. Cross-model verification default-on. Session-scoped confidence disclosure.
    • New file: academic-paper-reviewer/references/calibration_mode_protocol.md
  • Disclosure Mode (academic-paper v2.9): venue-specific AI-usage disclosure statement generator. v1 database covers ICLR, NeurIPS, Nature, Science, ACL, EMNLP. Unknown venues halt and prompt user to paste policy.
    • New files: academic-paper/references/disclosure_mode_protocol.md, academic-paper/references/venue_disclosure_policies.md
  • Fidelity-Originality Mode Spectrum (all skills): classifies all modes on a fidelityoriginality axis per Lu 2026 Fig 1c. Quick Mode Selection Guides updated with Spectrum column.
    • New file: shared/mode_spectrum.md
  • Early-Stopping Criterion (academic-pipeline v3.1): convergence check (delta < 3 points + no P0) suggests stopping revision loop. Budget transparency estimate at pipeline start.
  • README Positioning Update: "Why human-in-the-loop, not full automation?" section citing Lu 2026 as external evidence for ARS's design thesis. Both EN and zh-TW updated.

Changed

  • .claude/CLAUDE.md: synced all skill versions and mode lists to reality (deep-research v2.7, academic-paper v2.9, academic-paper-reviewer v1.8, academic-pipeline v3.1)
  • quality_rubrics.md: added "Known error profile" preamble explaining rubric scores are ordinally but not cardinally interpretable without calibration

Version bumps: academic-paper v2.9, academic-paper-reviewer v1.8, academic-pipeline v3.1

[3.1.1] - 2026-04-09

Added

  • Information Systems — Senior Scholars' Basket of 11 (extending the Basket of 8 added in v2.9): Decision Support Systems, Information & Management, Information and Organization — completing the AIS College of Senior Scholars' official list of premier IS journals
  • Section heading updated from "Information Systems (Basket of 8)" to "Information Systems (Senior Scholars' Basket of 11)" in academic-paper-reviewer/references/top_journals_by_field.md
  • Original IS Basket of 8 proposed and drafted by @mchesbro1Issue #5. Extended to Basket of 11 by @cloudenochcsisIssue #7, PR #8. Source: AIS Senior Scholars' List of Premier Journals

[2.9.1] - 2026-04-03

Added

  • status and related_skills metadata to all 4 SKILL.md frontmatters
    • Enables skill discovery tools and cross-skill navigation for users with multiple skills installed
    • deep-researchacademic-paperacademic-paper-revieweracademic-pipeline

[2.9] - 2026-03-27

Added

  • Style Calibration — learn the author's writing voice from past papers (optional, intake Step 10)
  • Writing Quality Check — checklist catching overused AI-typical patterns (renamed from AI Writing Lint)
  • Information Systems Basket of 8 journals added to academic-paper reference list
  • Copilot philosophy tagline to README EN + zh-TW
  • Substack guide articles to both READMEs

Fixed

  • Skill Details section version numbers and agent descriptions updated
  • /simplify review — stale refs, lint sweep efficiency, schema fields
  • Removed last v4.0 reference in CHANGELOG

[2.8] - 2026-03-22

Added

  • SCR Loop Phase 1 — State-Challenge-Reflect mechanism integrated into Socratic Mentor Agent
    • Commitment gates at layer/chapter transitions (collect user predictions before presenting evidence)
    • Certainty-triggered contradiction (probes high-confidence statements with counterpoints)
    • Adaptive intensity (tracks commitment accuracy, adjusts challenge frequency)
    • Self-calibration signal (S5) for convergence detection
    • SCR Switch — users can disable/re-enable predictions mid-dialogue
  • deep-research/agents/socratic_mentor_agent.md — SCR Protocol section with commitment gates, divergence reveal, and adaptive intensity
  • deep-research/references/socratic_questioning_framework.md — SCR Overlay Protocol mapping SCR phases to Socratic functions
  • academic-paper/agents/socratic_mentor_agent.md — Chapter-level SCR Protocol with per-chapter commitment questions and cross-chapter pattern tracking

[2.7.3] - 2026-03-10

Fixed

  • Version badge corrected in both EN and zh-TW READMEs

[2.7.2] - 2026-03-10

Added

  • Version, license, and sponsor badges to README
  • zh-TW README badges

[2.7.1] - 2026-03-10

Fixed

  • Buy Me a Coffee username corrected

[2.7] - 2026-03-09

Added

  • Integrity Verification v2.0: Anti-Hallucination Overhaul
  • Full academic research skills suite (4 skills, 116 files)
  • Deep Research v2.3 — 13-agent research team with 7 modes
  • Academic Paper v2.4 — 12-agent paper writing with LaTeX hardening
  • Academic Paper Reviewer v1.4 — Multi-perspective peer review with quality rubrics
  • Academic Pipeline v2.6 — 10-stage orchestrator with integrity verification