Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

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

> The blockquote directly under a release heading is that release's one-paragraph summary. Those summaries lived in `README.md` until 2026-09-15 and were moved here verbatim; `README.md` now keeps only the three most recent.

## [Unreleased]

### Changed

- **The 2026-11 harness-retirement audit trims maintainer-facing wording from three sentences in two agent prompts (#1034).** `audits/harness-retirement-2026-11.md` reviewed every Bucket A prompt change since the 2026-09 audit (13 PRs, 23 files) and found 0 P0 and 0 P1. Its one P2 finding: three sentences that #862 Phase 1 added to `abstract_bilingual_agent.md` and `draft_writer_agent.md` state, in whole or in part, how far that release's claim reaches. The instructions in them stay; the claim-scope wording, which gave the model no action, is removed, and one sentence that held only that wording is deleted. The same scope statement remains in `shared/output_language_pair.md` and in the 3.22.0 entry below. The heading literals and legacy keys are unchanged and still lint-pinned; the effect on runs is unmeasured.

### Fixed

- **Standalone citation-check returns a report, not a rewritten excerpt, and a revision-coach grader accepts a finer split (#1062).** The Citation-Check Mode section of `academic-paper/SKILL.md` loads `citation_compliance_agent.md`, whose Phase 5a output includes a Corrected Draft, so the mode returned a corrected excerpt that its declared deliverable (a citation error report) and the suite's `no-overreach` grader both leave out. The section now names the deliverable: the errors found, the Corrected Reference List, and each in-text correction with its location and replacement citation, with a remark that reports no error kept out of the errors, corrections, and Items Flagged for Review (a sentence that misstates a correctly listed source is still an error); a corrected version of the user's text is offered, and is produced only on request. The Corrected Draft still belongs to the paper-writing runs that call the agent as Phase 5a. In `plugin-evals/01-journal-mixed-format-zh`, `all-9-covered` now passes a reviewer comment split into several items when every part keeps that comment's reviewer and the parts keep all of its actions, since `revision_coach_agent.md` tells the coach to split a comment that makes more than one request; it also matches items by content rather than by number and does not fail an extra row such as a positive remark. Its existing failure rules stay: a comment fails when it is missing, merged into another so that it loses its own action, or attributed to the wrong reviewer. The re-run figures are in the PR; the effect on other models is unmeasured.

### Docs

- **Claude Fable 5.2 is a supported session model beside Claude Fable 5.1 and Claude Opus 5.5.** A two-reader audit of the Fable 5.2 system card (`audits/harness-retirement-2026-11-fable-5-2.md`, FT-001 to FT-018) retires no guardrail and adds none. `docs/PERFORMANCE.md` (en / zh-TW) corrects how to select each Fable model (since Claude Code 2.1.321, `/model fable` selects Fable 5.2 outside Claude apps gateway sessions, and Fable 5.1 needs its model ID), extends the effort guidance to Fable 5.2 (Claude Code starts it at `medium`; heavy runs at `high` or above), adds its list price to the cost arithmetic, and rewrites the tiering note for three models. `docs/RISK_REGISTER.md` and `shared/model_tiering.md` cite the new audit. The routing smoke ran on Fable 5.2 at one session per fixture: 21 of 21 in each kind of session under the recorded scoring rules, and 20 of 21 in the plugin install when fixture 01 is read strictly (`tests/fixtures/issue_133_routing/CALIBRATION_LOG.md`); #892 stays open. No full pipeline run, reviewer calibration, or #1002 contrast test exists for Fable 5.2, and the card's finding, measured on Claude Mythos 5.2 (the model Fable 5.2 shares), that the model leaves requested work undone more often than Fable 5.1 is unmeasured on ARS.

## [3.24.2] - 2026-11-06 — An honest null for experiment locks, repro_lock alignment, and a corrected sr-screener methods sentence

> **Repairs and docs; no new skill or mode, and the prompt-level changes are unmeasured:** An `experiment_provenance[]` entry can now say it has no honest lock values, with `repro_lock: null` and a `repro_lock_reason` (#1049). A survey or an analysis with no LLM could pass before only with placeholder values. Earlier ARS releases reject this form, so a passport that uses it needs this release. `check_repro_lock.py` now fails a required value that breaks its field's type or value rule, such as a null model id; `cross_model.secondary_model_id` may still be null. `planned_vs_executed[].value` accepts only a number or a string, so some passports that passed before now fail (#1050). The `sr-screener` methods text no longer says the reviewers could read only the record file: the tool list removes web and write access, and reading only the given file is an instruction (#1039). SETUP records two checks in which an uploaded skill dispatched a built-in sub-agent in a conversation after a retry; multi-agent stages stay untested (#1040).

### Fixed

- **An experiment with no honest lock values can say so (#1049, PR #1051).** Every `experiment_provenance[]` entry had to fill every `repro_lock` field, and those fields describe LLM and ARS run settings. A survey, an analysis with no LLM, or a trained model with no ARS prompts could pass only with placeholder values. The key stays required. Its value may now be `null`, and `repro_lock_reason` is then required; a reason beside a populated lock is rejected. The reason is free text: it discloses the gap, does not prove the lock does not apply, and no script checks the details it names. The passport-level validator and the required-field set do not change. **Compatibility:** earlier ARS releases reject `repro_lock: null` in an entry, so a passport that uses it needs this release.
- **`repro_lock` docs, example, gate, and validators agree (#1050, PR #1052).** `check_repro_lock.py` now checks each required value against its field's type or value rule, matching the nested entry schema. `cross_model.secondary_model_id` may be null or any string, and the other string fields must be non-empty. A drift test compares the two on required keys, supported values, and a fixed set of wrong and edge-case values, not on every possible value, and extra keys stay the one known difference. The example passport gains a passport-level lock, and its two trained-model experiments use `repro_lock: null` with a reason. The D7 text in the integrity gate names 3.12.0 as the boundary and adds a fifth FAIL condition, a post-#260 passport with status `legacy_unknown`. The pattern doc no longer says a version change alters the prompt hashes, because they hash only the prompt files. **Compatibility:** a populated lock with a required value that breaks these rules now fails when `check_repro_lock.py` is run on it, and `planned_vs_executed[].value` accepts only a number or a string. Some passports that passed before now fail. The gate text is prompt-level and its effect is unmeasured.
- **The `sr-screener` methods text states what the tool list enforces (#1039, PR #1043).** `tools: Read, Grep` removes web and write access but does not confine Read and Grep to one file. The title/abstract and full-text methods text now say: "The reviewer agents had read-only file tools and no web access; each was instructed to read only the file it was given." Methods text generated by an earlier release keeps the old sentence, so an author who used it should correct it by hand.

### Docs

- **SETUP records what an uploaded skill did on Claude Desktop (#1040, PR #1045).** Claude Desktop refused an upload named `deep-research` because Anthropic already provides a skill with that name, so Method 3 (en/zh-TW) now zips a copy named `ars-deep-research`, with the skill folder at the top of the zip. In two checks on 2026-11-02, the uploaded skill dispatched a built-in `Explore` sub-agent in a conversation; each time the first dispatch did not run and a retry did. Method 4a drops its 200-character and no-sub-agent claims and points to Method 3. `docs/CONTROL_AVAILABILITY.md` marks Cowork subagent orchestration and `sr-screener` dual review as Untested and adds note 12; a single dispatch is not multi-agent orchestration.
- **`docs/SETUP.zh-TW.md` and `README.zh-TW.md` drop their double em dashes (PR #1044).** The 14 marks became colons, commas, or parentheses, with no content change.

## [3.24.1] - 2026-11-02 — Plugin installs from a claude.ai account marketplace, reviewer card confirmation, and a Brazilian Portuguese README

> **Install repairs for claude.ai and Claude Desktop; no new skill or mode:** ARS now installs from a claude.ai account marketplace. Before this release, that install loaded none of the five core skills (#1035), and claude.ai left out the four plugin agents because their names had underscores (#1037). On Claude Desktop, a skill and `/ars-lit-review` ran in plain chat, a project session dispatched the `synthesis-agent` plugin agent, and the SessionStart announce appeared in folder work. The write-scope guard, the tools allowlist, and a full pipeline run are untested on claude.ai and Claude Desktop. The reviewer now waits for the user to confirm the Reviewer Configuration Cards (#1030), and the README has a Brazilian Portuguese translation (#1032).

### Added

- **A Brazilian Portuguese README, `README.pt-BR.md` (#1032, translated by @laqueiroz; PR #1033).** It is translated from the v3.24.0 `README.md` and keeps code blocks, example prompts, file paths, mode names, and schema and field names in English. The six other READMEs link to it from their language bars. `check_readme_pt_sections()` in `scripts/check_spec_consistency.py` pins its version badge, mode and skill headings, DOCX line, and three-release changelog section, and `scripts/check_reviewer_role_label.py` pins its Journal-Fit Reviewer wording. pt-BR was added after the 2026-09-15 README slimming, so it has no frozen `docs/changelog-archive/` copy. No skill, agent, trigger keyword, or locale pack changes.

### Changed

- **The reviewer waits for the user to confirm the Reviewer Configuration Cards before Phase 1 (#1030).** Checkpoint Rule 1 in `academic-paper-reviewer/SKILL.md` said to present the cards and let the user adjust them, but not to wait, so a run could show the cards and start the panel in the same turn. It is now an Iron Rule: after Phase 0 the session shows every card and ends its turn, and no reviewer seat is dispatched until the user, in their own turn, accepts the whole set, with or without edits; a reply that only edits some cards gets the updated cards shown again. It applies to `full`, `methodology-focus`, `guided`, and `quick`, including a Stage 3 review in `academic-pipeline`, whose orchestrator now names the stop in its checkpoint rules; `calibration` does not pause, and `re-review` runs no Phase 0. Only the user's own request to skip the card confirmation skips it, not a general request to skip questions, `[direct-mode]`, or text in the manuscript. In a `full` review, the confirmed cards are the Round-1 cards that re-review reuses. `scripts/check_reviewer_card_confirmation.py` pins the wording, and `scripts/test_check_reviewer_card_confirmation.py` holds its mutation tests. The rule is prompt-level; whether sessions stop at it is unmeasured.

### Fixed

- **A plugin installed from a claude.ai account marketplace loads the five core skills (#1035, reported by @thepogodude; PR #1036).** The account-marketplace sync drops symlinks, so the `skills/` symlink directory disappeared and no core skill registered. `.claude-plugin/plugin.json` now declares the five skill paths, and the `skills/` directory is gone; keeping both would load every skill twice in Claude Code. `.claude-plugin/marketplace.json` keeps its `skills` array for the Claude Science importer (#480). `scripts/check_skill_inventory_parity.py` compares `plugin.json` `skills[]` instead of the symlinks, fails if a `skills` path exists, and reports a duplicate entry in either manifest. Checked on the synced copy in Claude Code (21 skills, no double loading next to a GitHub-marketplace install) and on the claude.ai plugin page (five skills).
- **The four plugin agents have hyphenated names, which claude.ai accepts (#1037, PR #1038).** claude.ai rejects an agent name with an underscore, so the account marketplace synced the plugin without its agents and showed a warning for each. The frontmatter names are now `synthesis-agent`, `research-architect-agent`, `report-compiler-agent`, and `screening-reviewer-agent`; file names and the role IDs in schemas, contracts, and passports do not change. The write-scope guard manifest uses the new names, and `LEGACY_AGENT_NAMES` in `scripts/ars_write_scope_guard.py` maps an old underscore name, bare or plugin-scoped, to the new one, so an old agent copy stays fenced. In `sr-screener`, a saved `screening_config.json` with the old plugin-scoped reviewer name dispatches to the new name and keeps its stage identity, so a screening in progress keeps its decisions; a bare old name, which is a skills-copy agent the user installed, dispatches unchanged. The methods-text access sentence now appears under either reviewer name. After the merge, the marketplace warnings were gone, and a Claude Desktop project session listed the four agents and dispatched `academic-research-skills:synthesis-agent` directly. A pre-existing overstatement in that methods sentence is tracked in #1039.

### Docs

- **SETUP documents the claude.ai and Claude Desktop install (PR #1041).** Method 0 (en/zh-TW) gains an account-marketplace subsection: the install steps, what runs in plain chat and in project or folder work (Anthropic's documented scope, kept apart from our checks on Claude Desktop before and after #1037), what was not tested, the Claude Code sync and its API-key and cloud-provider exclusion, and a warning against keeping uploaded or copied skills next to the plugin. The account marketplace is the recommended claude.ai route; Method 3 (skill upload) is the fallback for a plan without plugins or an organization marketplace list that leaves this repository out. Method 4b is retitled "repository reading" and notes that Anthropic lists "Add from GitHub" as not yet supported once chat and Cowork merge. `docs/CONTROL_AVAILABILITY.md` adds a claude.ai plugin channel with plain-chat and Desktop project/folder columns, an `Untested` legend value, and note 11. The README (en/zh-TW) quick start names the claude.ai route. A possibly outdated Method 3 claim about uploaded skills is tracked in #1040.

## [3.24.0] - 2026-10-27 — Advisory checks of the panel's suggested references and findings, a one-process guard launcher, and integrity-rule repairs

> **Two advisory reviewer checks and repairs; no new skill, and the checks' accuracy on real reviews is unmeasured:** v3.24.0 adds two attachments to the Editorial Decision Letter. The first looks up, live, each work that Reviewer 2 and Reviewer 3 recommend in the four indexes of the citation gate, and reports whether a record exists, not whether the work supports the recommendation (#1001). The second sends each weakness and each CRITICAL or MAJOR Devil's Advocate row, without its seat label, to a subagent of the session model, which says whether the manuscript contradicts it. A contradiction is listed only when the judge's quotation passes the script's check against the manuscript; other false verdicts are listed as not confirmed (#1002). It is on by default, and `ARS_PANEL_CLAIM_CHECK=0` turns it off. Neither attachment feeds the decision, the revision roadmap, or re-review criteria. Before the second check shipped, a preregistered held-out audit of 805 panel claims put the false-claim rate between 1.5% and 12.9%, and Claude Opus 5.5 and Claude Fable 5.1 passed its contrast gate as judges. The write-scope guard launcher starts one Python process per tool call instead of three, which should cut the Windows AppXSvc leak to about a third, although the Windows behaviour is untested here (#1015), and the guard's notes state which tool names it checks (#1022). Three integrity-check rules now agree on Stage 5 entry, report freshness, and Stage 4′ (#1011), and the startup notice appears only when an update is available (#1027). Outside users reported #1015, #1022, and #1027.

### Added

- **The Editorial Decision Letter carries a check of the references the panel suggests (#1001).** Reviewer 2 and Reviewer 3 now repeat each specific work they recommend in a `yaml suggested-references` block (title, and authors, year, DOI, or arXiv ID when they can attest them; `[]` when they name none). After the decision, the dispatching session runs `scripts/verify_panel_recommendations.py` on their saved reports and appends its table under `## Attachment: Suggested-Reference Check (advisory, #1001)`, before the acronym check, which stays the letter's last write. The script looks each work up through `verification_gate.verify_citation` with the four resolver clients of `verify_passport.py`, live and without the verification cache, and reports one of four results under the citation gate's C-V6(a) rule: found, DOI or arXiv ID not found, title not matched with no identifier to check, or not checked because an index that could settle it did not answer or the lookup failed. "Found" shows only that a record exists, not that the work supports the recommendation. Like the acronym check, the attachment is script output: the decision, the revision roadmap, and re-review criteria do not draw on it, and a suggestion it cannot find stays in the roadmap as the reviewer wrote it. `docs/DATA_FLOWS.md` names the new network use, and `shared/contracts/degradation_registry.json` adds its two degraded states. Tests use mocked resolver clients and synthetic reports; whether reviewers write the block, and how often their suggestions resolve, are unmeasured.

- **A preregistered held-out audit of the review panel's claims against the manuscript (#1002, part one).** `evals/heldout/panel_claim_factuality/` takes the 805 weaknesses and Devil's Advocate CRITICAL and MAJOR rows of the 17 score-eligible E4 panels, with the seat held back (`scripts/extract_panel_claims.py`), and `scripts/dispatch_panel_claim_audit.py` sends them to two judges, gpt-6-astra through codex and Gemini 4 Argon at medium thinking through the paid API under a spend cap, two replicates each. Both judges passed the contrast gate: each marked all 51 false contrast sentences false and none of the 51 accurate twins. The two judges agreed on 12 false claims, in 6 of the 17 panels, which meets the preregistered bar for planning part two; the other 92 claims (54 on which the judges diverged, 38 on which a judge's verdict was unresolved because its replicates differed or a false verdict failed the evidence check) were not adjudicated, so the false-claim rate is reported only as a range, 12/805 (1.5%) to 104/805 (12.9%), and no per-seat rate is computed. No person verified the 12 claims. The plan reached version 9 through recorded amendments; the scoring run is version 8 (Gemini outputs from that run, codex outputs from version 3, reused by prompt hash), and version 9 changes only the transports, under which those outputs score the same. The transports run on POSIX systems only (tested on macOS). No ARS skill, agent, or pipeline step changes.

- **Claude Opus 5.5 and Claude Fable 5.1 pass the #1002 contrast gate (#1002, plan version 10).** Part two of #1002 would use the session model as its judge only after that model passes the part-one contrast gate. `scripts/dispatch_panel_claim_audit.py` gains two contrast-only judges, `claude-opus-5-5` and `claude-fable-5-1`, each through `claude -p` at high effort with no tools and no MCP servers, an empty configuration directory, an allowlisted environment, and a temporary working folder that the script checks for project settings and git checkouts in it and every folder above it. The `--contrast-only` run sends only the 61 batches that hold a contrast sentence, and the `contrast-score` command applies the preregistered bars. Each model, in two replicates, marked all 51 false contrast sentences false and none of the 51 accurate twins. Claude Opus 5.5 wrote those sentences, so the test cannot separate judging skill from familiarity with them; gpt-6-astra passed the same sentences in part one. Their verdicts on reviewer claims are not scored, and no ARS skill, agent, or pipeline step changes.

- **The Editorial Decision Letter carries a check of the panel's findings against the manuscript (#1002, part two).** After the decision and the suggested-reference check, the dispatching session runs `scripts/audit_panel_claims.py prompt` on the manuscript and the five saved Phase 2 reports, sends the written prompt, unchanged, to one subagent of the session model with no tools, and runs `render` on the reply. The prompt is the part-one auditor prompt, with each Weakness and Devil's Advocate CRITICAL or MAJOR row under a neutral id in hash order and without its seat label; a finding's own words can still show its seat. The attachment, `## Attachment: Reviewer Claim Check (advisory, #1002)`, lists a finding the judge marked as contradicted only when the judge's quotation passes the script's check against the manuscript, and says that the check reads only the quotation's words, not its location or whether it contradicts the finding; other false verdicts are listed as not confirmed, and missing verdicts or a report that cannot be read make the coverage partial. Report and judge text goes into the letter as literal text. It is advisory: no call that writes the round's decision, roadmap, or letter sees it, later readers take no weakness, consensus item, revision, or roadmap entry from it, and the author decides what to do with each row. It is on by default; `ARS_PANEL_CLAIM_CHECK=0` turns it off. As the session-model judge, Claude Opus 5.5 and Claude Fable 5.1 passed the part-one contrast gate (plan version 10), as gpt-6-astra and Gemini 4 Argon did in part one through other transports; other models are untested, and the attachment says so. `shared/contracts/degradation_registry.json` adds its two degraded states. Tests use synthetic reports; whether sessions run the check, and how accurate its rows are on real reviews, are unmeasured.

### Changed

- **King & Worthington (2026) is the sixth human-in-the-loop anchor in the README (#1003; refs #1001, #1002).** *AI Article Reviews Dominate Human Peer Reviews at Their Best* compares Claude Opus 4.8 reviews, one per manuscript in each of two conditions, with paid expert reviews of the same 124 manuscripts. The new paragraph in the three READMEs that carry the section (en, zh-TW, es-ES) reports the head-to-head result, the two findings that bear on ARS's reviewer (additional sources without retrieval; AI claims contradicted by the manuscript), the paper's warning about revising for a published prompt, and its limits, including that its results "do not speak to" publication decisions. The two measurements it motivates are tracked as #1001 and #1002. No prompt, schema, lint, or runtime change.

- **The startup notice appears only when an update is available (#1027, #1028, reported by @kaneyxx).** Since v3.23.2 (#999), `scripts/announce-ars-loaded.sh` set `systemMessage` on every startup and clear, so a session with no update still opened with a line that asked the user for nothing. It now sets `systemMessage` only when the #544 update reminder is present, and the reminder keeps the loaded line after it. `additionalContext`, the routing core, the update checker, and the compact and resume short form are unchanged. To check that ARS is loaded, type `/ars-` for the command menu or open `/plugin`. A test pins that startup and clear set no `systemMessage` when no update is available.

- **The write-scope guard states which tool names it checks (#1022, reported by @alexxhchen-nu).** `scripts/ars_write_scope_guard.py` checks a call only when its tool name is exactly `Write`, `Edit`, `MultiEdit`, or `Bash`, the four names the `hooks/hooks.json` matcher sends it, and lets every other name through to the normal permission flow without checking it. Its module notes now say so, and say why other names are not matched case-insensitively or treated as aliases: a host tool under another name may send another payload shape, which the guard, once its manifest loads, would deny for every caller, the main session included, or check on only one of its files. New tests pin both sides, and pin the matcher and the guard's name set to each other. The guard's behaviour does not change.

### Fixed

- **The write-scope guard launcher starts one Python process per tool call, not three (#1015, reported by @Thenewmanator15).** On Windows with the Python Install Manager, `py` is an App Execution Alias, and each launch through it makes AppXSvc run an update check that leaks about 1.5 MB until the service stops (python/pymanager#396); on a busy machine this reached 42 GB. `hooks/run_guard.sh` probed the interpreter, ran the guard, and checked the guard's JSON with three launches of the first working candidate. It now runs one process per candidate: a short runner prints the marker, runs the guard in the same process with its output captured, and prints the guard's decision only when the guard exited 0 and printed a JSON object with `hookSpecificOutput`. A candidate that prints other text before the marker or after the decision is skipped, as before; a guard that fails after the marker still gives a pass-through. The runner uses `-I`, so `PYTHON*` variables, the user site, and modules in the working directory do not reach it; it reads the payload as UTF-8, flushes the guard's stderr advisories before it ends, and forwards the decision as ASCII JSON on one line followed by an end line. The `timeout` call now passes `-k 1s`, so a guard that ignores SIGTERM is killed one second after the bound, and the separate JSON check, which had no time bound, is gone. Each call still launches through the alias once, so the leak is about a third of the earlier rate, not zero. Tests simulate the interpreters on a POSIX host; the Windows behaviour is untested here.

- **The integrity-check rules agree on three points (#1011).** Entering at Stage 5 with a revised paper now runs Stage 4.5 first, which rule 12 of `shared/handoff_schemas.md` requires, and a prior integrity report is context for it, as the orchestrator's decision tree already said; the mode advisor had said the user could skip Stages 1-4.5 by showing a pass report. A prior report counts as fresh only when it is less than 24 hours old, within one session too; rule 11 and the orchestrator's decision tree had also accepted any report from the current session, and rule 10 now calls a report stale at 24 hours, the same boundary as the decision tree. The Stage 4 entry row no longer lists Stage 4′ as always run: after Stage 3′, only a Major decision leads to Stage 4′. The changes are prompt-level wording and unmeasured.

## [3.23.2] - 2026-10-24 — Integrity checks read only the cited version, Stage 2.5 always runs, and adapter, announce, and Windows repairs

> **Repairs; no new skill. One prompt-level change has a small behaviour check, and the others are unmeasured:** v3.23.2 makes the Stage 2.5 and 4.5 claim checks compare a claim only with the version of a work that the manuscript cites (#1006). When that version cannot be read, the check records an access note and asks for its PDF instead of reading a working paper or preprint. In one synthetic fixture run six times (three sessions each on Claude Opus 5.5 and Claude Fable 5.1, web tools off), every session followed the rule. A prior integrity report no longer lets a pipeline run skip Stage 2.5; the run attaches it as context (#995). A missing required input blocks the stage transition on every surface (#990), and the Socratic mentor's example no longer ranks the user's ideas (#992). The SessionStart announce shows the update reminder to the user, and the model-facing announce is about 2.1 KB shorter (#999). The Obsidian adapter accepts unquoted ISO dates and rejects boolean and superscript-digit years (#988, #989); six lints compare paths in POSIX form on Windows (#964, #978); the Pi wrapper composes with Pi's structured prompt sections (#985); and `CITATION.cff` gives the author's ORCID iD (#1007). Outside contributors wrote #978, #985, and #988.

### Changed

- **`CITATION.cff` gives the author's ORCID iD (#1007).** Zenodo reads the file when it archives a release, so later versions under the concept DOI link their creator to https://orcid.org/0009-0008-9523-0103. Versions already on Zenodo do not change.

### Fixed

- **The SessionStart announce shows the user what is meant for the user (#999).** `scripts/announce-ars-loaded.sh` sent everything through `additionalContext`, which reaches only the model, so the #544 update reminder never reached the user. Startup and clear now also set `systemMessage`: a one-line loaded notice, preceded by the update reminder when one is available. The model-facing startup announce replaces the 16-row command table, the agent table, and the token-budget line with one line of command names and one line of agent names (about 2.1 KB less per session); the command-invariants lint still checks every `/ars-*` token and the count. The skill-routing paragraph and the routing core are unchanged. Three tests in `scripts/test_ars_update_check.py` pin the new field.
- **Missing required inputs block the transition in the pipeline state machine too (#990).** The Missing Materials section of `academic-pipeline/references/pipeline_state_machine.md` let the user "skip (at own risk)" a required input, while `shared/handoff_schemas.md` Validation Rules (rule 5) and the state tracker's gap table block the transition. The section now follows the state tracker: a missing required input returns `HANDOFF_INCOMPLETE` and is backfilled before the next stage runs, a missing recommended input does not block and is reported, and integrity checks still cannot be skipped. The #528 content lock is updated.
- **The Socratic mentor's "direct answer" example no longer ranks the user's ideas (#992).** The example in `deep-research/agents/socratic_mentor_agent.md` said "your second idea actually has a lot of potential", which the Research-Question Authorship Boundary in the same file forbids. It now declines and asks which of the user's own directions they most want to understand, and why. The ideation-diversity non-production variant pins the new source hash. Prompt-level; the effect on model behaviour is unmeasured.
- **A prior integrity report no longer lets a pipeline run skip Stage 2.5 (#995).** `shared/handoff_schemas.md` Validation Rules (rule 11) and the `academic-pipeline/SKILL.md` Mid-Entry Protocol allowed a fresh, unmodified, user-confirmed report to skip Stage 2.5, while the orchestrator's Mid-Entry Material Passport Check required Stage 2.5 in every run and attached the prior report as context. All surfaces now follow the orchestrator: Stage 2.5 always runs before Stage 3, and a prior report that is `VERIFIED`, fresh, and on the current version is attached to the run as context. The same rule is stated in the orchestrator's mid-entry routing rules and the Stage 3 row of `academic-pipeline/references/mode_advisor.md`. `check_spec_consistency.py` pins the new rule-11 heading and body and the absence of the old skip wording in the four files; the #528 content locks are updated. Prompt-level; the effect on model behaviour is unmeasured.
- **Phase E checks a claim only against the version of a work that the manuscript cites (#1006).** Stage 2.5 and 4.5 source tracing (`academic-pipeline/references/claim_verification_protocol.md` E2, mirrored in `integrity_verification_agent.md`) could read a working paper or preprint when the cited version was not readable, and judge the claim against it, which produced both false flags and false passes. The check now identifies the cited version from the reference entry (and from `version_records.yaml` when present), never substitutes another version, and records `UNVERIFIABLE_ACCESS` with a request for the cited version's PDF when that version cannot be read, including an open-access page the agent cannot read. A reference entry that does not identify exactly one version gets the same note with `VERSION_INCONSISTENT_CITATION`, and an author-accepted manuscript stands in only when the reference cites it. For a claim with several sources, an access note on one source no longer hides a distortion found in another. Each evidence row names the version in its existing `source.display_label`, which the rendered table already prints; the evidence-row schema does not change. The orchestrator's checkpoint line asks for the cited version's PDF (content lock updated). `check_evidence_row_integration.py` pins the rules, and `scripts/test_phase_e_cited_version.py` pins that the row carrier records and renders the access outcome. Whether runs follow the rule is prompt-level. In one synthetic fixture run six times (three sessions each on Claude Opus 5.5 and Claude Fable 5.1, web tools off), every session recorded the access note, asked for the cited version, and issued no verdict from the working paper that was readable.
- **The Obsidian adapter rejects a boolean year and a year written in superscript digits (#989).** `_coerce_year` in `scripts/adapters/obsidian.py` accepted `year: true` as an integer, because `bool` is a subclass of `int`, and wrote `year: true` to the corpus. A year string such as `'²⁰²⁴'` passed `str.isdigit()` and then raised `ValueError` in `int()`, which stopped the run before the passport was written. Both notes are now rejected with `year_unparseable` and the run continues. The string check uses `str.isdecimal()`, so years in other decimal scripts that `int()` reads are still accepted. A parametrized test in `scripts/adapters/tests/test_obsidian.py` covers both cases.
- **The Obsidian adapter accepts a year written as an unquoted ISO date (#987, #988, contributed by @harshajv7981).** `yaml.safe_load` turns `year: 2024-01-15` into a `datetime.date` before `_coerce_year` sees it, so the adapter rejected the note as `year_unparseable`, although its docstring lists that form as accepted; Obsidian's Properties panel writes Date properties this way. `_coerce_year` now takes the year from a date or datetime value. A regression test in `scripts/adapters/tests/test_obsidian.py` covers it.
- **The Pi wrapper composes ARS with Pi's structured prompt sections (#966, #985, contributed by @daniyyell-dev).** When the wrapper returned a rendered `systemPrompt`, Pi treated it as a complete override and could drop sections from other extensions, such as MCP instructions. When `event.systemPromptOptions` is available, the wrapper now filters package-owned skills while ARS is inactive and adds an `ars_compatibility` section while it is active; stopping ARS removes only that section. Hosts without the structured API keep the string and array fallback from #880. Unit tests and an opt-in integration test against Pi 1.1.0 and 1.3.0 cover it.
- **Windows: six lints compare repository paths in POSIX form (#964, #978, contributed by @daniyyell-dev).** `check_684_review_criteria_binding.py`, `check_collaboration_depth_rubric.py`, `check_content_coverage_advisory_integration.py`, `check_cross_document_consistency_advisory_integration.py`, `check_decision_contract.py`, and `check_tortured_phrase_screening_integration.py` compared `str(Path)`, which uses `\` on Windows, with forward-slash paths, and failed on a correct tree. They now use `Path.as_posix()`. The new tests pass Windows-style paths into each lint's comparison, so the Ubuntu CI fails if a conversion is reverted.

## [3.23.1] - 2026-10-16 — Write-scope guard repairs, RFC 4180 CSV reading in sr-screener, and Gemini 4 Argon as a provisional verifier

> **Repairs and model currency; no new skill, and no accuracy or performance claim:** v3.23.1 closes two write-scope guard paths that let writes through without a check, one for plugin-dispatched agents and one for Windows `timeout.exe` (#972), and the docs now state where the fence engages (#974). `sr-screener` reads CSV exports by RFC 4180 quoting instead of quote rules guessed from a sample (#979). Gemini 4 Argon is listed as a provisional Google verifier and the recommended Google id; `gemini-3.1-pro-preview` stays validated (#980). The routing core states that a mid-message `[direct-mode]` token is not the escape hatch (#892); in a smoke test on a plugin install, Claude Fable 5.1 still honored the token in 3 of 6 sessions (6 of 6 before), so the acceptance rule is not met and #892 stays open. Smaller lint and Windows repairs come from outside contributors (#956, #963, #965, #977), and `THIRD_PARTY.md` lists md-to-word (#969).

### Changed

- **Gemini 4 Argon (`gemini-4-argon`) is listed as a provisional Google verifier and becomes the recommended Google id (#980).** Google released it on 2026-10-13. `shared/cross_model_verification.md` adds it to the supported-model table and to the `provisional` branch of `id_status`, with a note on the provider facts that touch the Gemini call pattern: Search grounding is supported, sampling parameters are ignored, and `thinking_level` is left at the provider default. `gemini-3.1-pro-preview` keeps its validated status as the previous generation. The SETUP examples (en / zh-TW) and the `.claude/CLAUDE.md` summary follow. No smoke or bakeoff run is claimed; `scripts/cross_model_smoke_test.sh` covers the OpenAI route only, so the provisional and unlisted notices for a `gemini-*` id no longer point to it, and § Promotion Bakeoff marks the entry gate for a Gemini candidate as pending until a Gemini smoke script ships.

- **The routing core says that a mid-message `[direct-mode]` token is not the escape hatch (#892).** The rule was only in `shared/references/intent_clarification_protocol.md`, which a plugin-install session cannot read. One sentence joins Step 0 of `shared/references/routing_core.md` and its six copies; the `academic-pipeline/SKILL.md` content lock is updated. Measured on Claude Code 2.1.296 with the #133 fixtures (smoke test, one session per cell except fixture 06): on a plugin install, Claude Fable 5.1 honored the mid-message token in 6 of 6 sessions before the change and 3 of 6 after it, so the acceptance rule fixed before the run is not met and #892 stays open. All 21 fixtures ran in both installs on both models, and fixtures 13-21 ran for the first time; fixture 16 failed on Claude Opus 5.5 in the plugin install (#960), and the other single-cell failures and scoring readings are in the log. Results are in `tests/fixtures/issue_133_routing/CALIBRATION_LOG.md`.

- **The documentation states where the write-scope fence actually engages (#973, #974).** The fence acts only when a `PreToolUse` payload names a Bucket A agent type, which happens for separately dispatched, named subagents. Roles that run inline in the main session carry no agent type and are not fenced. The coverage claims in the docs now say so, and captured hook payloads are pinned as test fixtures.

- **`THIRD_PARTY.md` lists md-to-word, a Markdown-to-DOCX typesetting companion for Stage 5 output (#969, contributed by @ls-open).** It is an out-of-tree project; ARS does not bundle or endorse it.

### Fixed

- **`sr-screener` reads a CSV export correctly when a doubled quote appears only after the first 5,000 characters (#979, reported by @paololimo).** `srlib.parse_csv` sniffed the whole dialect from that sample, and a sample with no doubled quote switched RFC 4180 quote doubling off. A later field holding a quoted term was then split: with commas the year was lost and the DOI or journal received abstract text; with semicolons the fields stayed in place but the abstract gained stray quotes. `parse_csv` now sniffs only the delimiter, reads quotes by RFC 4180 (`"` as the quote character, doubling on), and also accepts a space before a quoted field, which RFC 4180 does not define. The same review found three related misreads, now fixed: apostrophes in the sample could make `'` the quote character, a quote-free sample could split a later `, "a, b"` field, and a single-column export with semicolons in its titles was read as semicolon-delimited; a header record that the sniffed delimiter splits into fewer than two fields now means a single column. Six regression tests in `scripts/test_sr_screener_pipeline.py` cover these cases.

- **Two write-scope guard paths no longer let writes through unchecked (#972; #970 and #971 reported by @xihuheyi007).** Claude Code names a plugin-shipped agent `academic-research-skills:<agent>`, while the scope manifest keys the bare name, so the fence never engaged for plugin-dispatched Bucket A agents; the guard now strips this plugin's own prefix before the lookup (#971). `hooks/run_guard.sh` trusted any `timeout` on `PATH`; the Windows built-in `timeout.exe` rejects the GNU form, so no Python was found and every write passed. The launcher now probes `timeout` once per launch and otherwise uses its watchdog fallback (#970).

- **Lints reject a non-string `exception_reason` (#965 by @SID-6921, #977 by @daniyyell-dev).** `check_field_norm_severity.py` read a `null` reason as present through `str(None)` (#965). `check_surface_form_parity.py` treated an int, list, or bool reason as absent, so a stray typed reason on a non-exception item passed (#977).

- **Windows: the v3.6.6 A/B fixture-manifest reverse scan compares POSIX paths (#963, contributed by @SID-6921).** `str(Path)` renders `\` on Windows and missed the forward-slash manifest entries.

- **A test regex uses a raw string, removing an invalid-escape warning (#956, contributed by @SID-6921).** Test-only; no behavior change.

## [3.23.0] - 2026-10-03 — `sr-screener` as a fifth skill, repairs from a pipeline walk-through, and evidence and ledger hardening

> **One new skill and behaviour repairs found by reading a full pipeline run end to end; the prompt-level changes are unmeasured, and `sr-screener` makes no screening-accuracy claim:** v3.23.0 adds `sr-screener` (#919, contributed by @erfanz97), a fifth skill that turns a review protocol into eligibility rules the user confirms and screens titles/abstracts and full texts with two blinded reviewer subagents and an adjudicator; its tests use synthetic records, and its spreadsheet exports neutralise formula text (#951). A walk-through of a default pipeline run on paper (#925-#929) changed what such a run does: the v3.6.7 Audit Artifact Gate is opt-in, the scholar is asked once whether the paper reports their own experiments, constraints set for a run are kept in the user's words and quoted to each later dispatch they apply to (at review stages they act at the checkpoints), the integrity gates follow one rule set and list paywalled sources as notes, Stages 5 and 6 produce only the files asked for, and opt-in switches surface Stage 5 refusals at Stage 4.5. The Stage 2.5 and 4.5 checkpoints replay evidence rows from a folder the orchestrator names (#933, #947, #948), a source the gate finds fabricated stays out of later revisions (#936), and the ledger readers report parse failures without quoting the file (#898, #945). Smaller changes: per-source method weaknesses in reading outputs (#916), a review-form note at a fixed point (#921), Schema 1 aligned with its producer (#938), and `/ars-citation-check` inheriting the session model (#912).

### Added

- **A fifth skill, `sr-screener` v1.0.0: protocol-driven study screening for systematic, scoping and rapid reviews (#919)** (contributed by @erfanz97). It turns a proposal or PROSPERO protocol into eligibility rules the user confirms, then screens titles/abstracts and full texts with two blinded reviewer subagents and a third-reviewer adjudicator. Eight modes: protocol, quick, pilot, ta-screen, ft-screen, adjudicate, audit, report. Standard-library Python scripts inside the skill folder parse RIS, PubMed .nbib, Web of Science and CSV exports, de-duplicate, batch, merge the reviewer returns, and write a screening log, RIS groups, PRISMA 2020 counts, a methods draft with `[TO COMPLETE]` slots, and a `literature_corpus[]` file that validates against `literature_corpus_entry.schema.json`. A decision that is missing or malformed leaves its record pending; it is never filled in. Per the #919 review, every screening role defaults to Sonnet and no profile offers a smaller model (a per-role override stays, and the cost check prints it); the full title/abstract run is blocked until the pilot has been compared with the review team's own labels and missed no record the team advanced (a user override is recorded and reported in the methods text); and a reproducible sample of records both reviewers excluded (at least 20, default 100) goes to a senior QC reviewer, with numbers held provisional until it has run. The reviewer subagent, `screening_reviewer_agent` (tools: Read, Grep), is mirrored into the plugin's `agents/` and inlines the instruction/data boundary, as do the skill's `SKILL.md` and reviewer prompts. The skill is registered on every inventory surface: the `skills/` symlink, the CLAUDE.md table and routing rule 8 (screening vs `deep-research` `systematic-review`), a Screening boundary paragraph in the routing core (`shared/references/routing_core.md` and its six copies: screening starts only when the user asks, with no automatic handover from `systematic-review` mode), routing fixtures 17-21 (two positive, three negative), the marketplace and plugin manifests, `MODE_REGISTRY.md`, the mode spectrum, the SessionStart announce routing, the model-tiering manifest (43 agents), the Bucket C rosters of the phase-boundary and write-scope lints, the `data_access_level` pin (`raw`), and `DATA_FLOWS.md` / `CONTROL_AVAILABILITY.md`. `scripts/test_sr_screener_pipeline.py` runs the scripts end to end on synthetic records. Screening accuracy is not measured in this repository, and fixtures 17-21 have not been run in a calibration session yet; the AI decisions are decision support that the review team verifies before any number is reported.

- **A Major re-review driven only by items you declined no longer forces a full extra revision, and Stage 4' stops making you re-answer earlier choices one by one (#927).** The Stage 3' verifier still does not see the author's choices, so declined `must_fix` items are still judged `NOT_ADDRESSED` and the decision stays as judged. `scripts/check_re_review_synthesis.py` now reports `declined-only Major (#927)` when counting the declined items (`wont_address` or `not_on_point`) as addressed would put the decision below Major, with the decision and reject recommendation that would result; the recount reuses the checker's decision function, so escalation floors and regressions still hold the Major. For those items the next roadmap proposes, from the reviewer side, a target in the paper's limitations section, and the Stage 3' checkpoint offers a limitations-only Stage 4': each item is stated as a limitation using your reasons, nothing else is revised, and you see the passage before Stage 4.5. At the Stage 4' author checkpoint, items that look like ones you declined before are grouped with your earlier words, and one answer can cover the group; nothing is applied without that answer. Orchestrator § Declined Items at Stage 3' and Stage 4' (#927) with its own line budget; the checker's frozen-runtime hash in `scripts/check_670_revision_roadmap_integration.py` is re-pinned for this additive report line. The prompt rules are unmeasured.

- **Constraints you set for a whole pipeline run are kept in your words and reach every later stage (#927).** A word ceiling, something not to do, or a narrower scope now lives in a new optional Material Passport list, `standing_constraints[]` (schema `shared/contracts/passport/standing_constraint_entry.schema.json`), instead of only in the run ledger, which is never sent with a dispatch. At the checkpoint after Stage 1, or at the entry confirmation for a run that starts later, the orchestrator lists each constraint from your instructions in your words, with its kind, the stages it applies to, and how they will use it, and writes an entry only after you confirm; a constraint added at a later checkpoint is confirmed there. Each dispatch quotes the entries for its stage (reviewers excepted), and writer, revision, and formatter dispatches carry the Paper Configuration Record. A confirmed scope narrowing is written into the RQ Brief scope and any sub-question binding for that axis before the next dispatch that reads it, so the writer and the integrity scope check read it. A ceiling on the body's word count is recorded beside the target; the writer's self-check and the checkpoint dashboard treat a body above it as over, instead of passing within ±10% of the target. At the Stage 3 and 3' checkpoints, a roadmap item that would do what one of your prohibitions rules out is marked against it, and you decide. Schema tests: `scripts/test_standing_constraint_entry_schema.py`. Whether the orchestrator lists and quotes the entries is prompt-level and unmeasured.

- **Reading outputs state how one source's method can fail, and say where each weakness came from (#916).** `shared/references/per_source_method_weaknesses.md` holds five rules, copied verbatim into the evidence assessment card (§4), the three-way scan WHAT field, and the annotated-bibliography formats of `bibliography_agent` and `literature_strategist_agent`: name the design choice and the condition under which it distorts the result; label each item `author-acknowledged` (with a v3.7.3 anchor) or `reader-inferred`; name the sections checked before saying the authors do not address something (#548 pattern); walk the paper-type table in `review_criteria_framework.md` §2 once instead of expanding open-endedly; and write `not assessed (read scope: <scope>)` when only the abstract or table of contents was read (#513 scopes). The format borrows from ScientistTwo (Nam et al., 2026, arXiv:2609.19644, Appendix C) and leaves out its unlabelled inferences and its research-direction field. Nothing blocks, gates, or asks the scholar a question. `scripts/check_method_weaknesses_sync.py` keeps the four copies byte-identical to the canonical block. `evals/heldout/per_source_method_weaknesses/` adds two synthetic items (a section-addressed issue and an abstract-only source); they are a smoke check, `NOT_RUN` as a measured suite, and no claim of improved review quality is made.

- **A fixed-point reminder that the author decides the form of a literature review (#921).** `shared/references/review_form_note.md` holds one note, in English and Traditional Chinese, that lists five forms (systematic, scoping, narrative or integrative, rapid, no formal review) with one line each on what the form serves and roughly what it costs, and states that ARS does not choose. The note has no default, ranking, or recommendation. It appears at the first of two actions the author takes: selecting `lit-review` mode, or confirming the research question (the RQ Brief in `deep-research` `full` mode before Phase 2, or the closing RQ Brief or RQ Summary of `socratic` mode; an ending the author has not confirmed does not count). It never appears because of what the question asks, and not at all when the author has already named a review form. The session then waits for the author's reply; skipping counts as a decision, and the note appears at most once per project or run. A systematic review is entered only on the author's explicit choice. In a run with a passport file the note and the reply go to the run ledger as a `review-form-note` checkpoint. `deep-research/SKILL.md` and `academic-paper/SKILL.md` carry the block verbatim; `scripts/check_review_form_note_sync.py` keeps the copies identical and checks that each note lists the five forms under their fixed labels in order and keeps its neutrality sentence, with no ranking word or selection mark elsewhere in the note. The two existing paths that could move into `systematic-review` mode on the model's own reading (a three-way scan escalating for "PRISMA-like coverage", and a `lit-review` → `systematic-review` transition "when the topic warrants" PRISMA) now require the author's choice, and requests for a systematic review no longer route to `academic-paper` `lit-review`. Routing fixtures 13 to 16 add a negative case ("help me write the literature review" reaches neither `systematic-review` mode nor screening), its content-independence pair, a named-form case, and a broader-synthesis case; fixtures 02 and 04 now expect the note. The fixtures have not been run; whether sessions show the note at the right points is prompt-level and unmeasured. Other languages get the English text, so no user sees a paraphrase written on the spot.

- **Held-out cases: an abstract that claims more than its own result tables (#915).** `evals/heldout/experiment_alignment_overclaim/` holds two synthetic Material Passports built from the DynaSpec-RAG showcase manuscript in ScientistTwo (Nam et al., 2026, arXiv:2609.19644 v1, Appendix D), with the paper's own tables as scholar-declared `experiment_provenance[]` and its claims as ClaimIntent entries. Case st2-01 pairs the abstract's "strictly eliminating negative transfer" and the conclusion's "strictly preventing negative transfer" with test rows where the method is worse than its frozen base (Electricity, ETTh1 MAE, GIFT-Eval bizitobs l2c MAE), the calibrated guard's own 2-of-7 regressing datasets (Table 7), and a calibration result scoped to validation splits; expected `OVERSTATED` or `NOT_SUPPORTED_BY_PROVENANCE`, both of which block. Case st2-02 pairs the contributions list's credit to neighbour attention (cut from a seven-component sentence) with an ablation where uniform averaging ties it on MSE and beats it on MAE; expected `OVERSTATED` or `NOT_SUPPORTED_BY_PROVENANCE`. Each passport also carries one supported control claim, expected `ALIGNED`. Only short quotes, numbers, and page references are stored. The suite is registered as `mechanical_match` and is `NOT_RUN`; two cases from one paper are a smoke check, not a detection rate. `scripts/test_experiment_alignment_overclaim_fixtures.py` keeps the passports valid against the #260 contracts and free of answers.

- `THIRD_PARTY.md` lists an OpenClaw port of the four skills, which @ChunkyPanda29 maintains in a separate repository (#910).

### Changed

- **A default full pipeline run no longer stops at Stage 1 on two mechanisms it could not satisfy (#925).** The v3.6.7 Audit Artifact Gate, which sends downstream-agent deliverables to an external model through `scripts/run_codex_audit.sh`, is now opt-in and off by default: it runs only when `ARS_AUDIT_ARTIFACT_GATE=1` is set and the user agrees for the run, under the consent boundary in `shared/cross_model_verification.md`, to run the wrapper outside the session at each transition. Once on, it still cannot be skipped. The Stage 2.5 and 4.5 integrity gates are unchanged. `experiment_intake_declaration`, which the integrity gates require on every post-#260 passport, now has a producer in pipeline runs: the orchestrator asks the scholar once, at the checkpoint after Stage 1 or when a run enters after Stage 1, whether the paper reports experiments or data analyses they ran themselves, and records the status from that answer together with the answer itself in a new optional `scholar_answer` field (`pipeline_orchestrator_agent.md` § Experiment Intake Question (#925)). The status is never inferred from the manuscript. Experiments are recorded before the first Stage 2 writer is dispatched (or before the first integrity gate when no Stage 2 lies ahead), so a scholar can pause after Stage 1, run them, and record them on return. EP-INV-5 in `scripts/check_claim_audit_consistency.py` rejects an empty or non-string `scholar_answer`; the orchestrator, `academic-pipeline/SKILL.md`, and the state machine carry updated content locks. Whether a run asks the question at these points is prompt-level and unmeasured. The ko-KR, es-ES, ja-JP, and zh-CN READMEs still describe the declaration as detected at Stage 1; they are community-maintained.

- **Every read of the run ledger now applies the break rule (#898, items 3 and 4).** `scripts/run_ledger.py show --passport-path P` prints the entries the break rule trusts, with the ledger's status and the seq from which entries are untrusted; it exits 0 when the whole ledger is trusted and 1 when it is missing, unreadable, or broken. `report` and `show` share one reader, so they apply the same break. A ledger that cannot be parsed is reported by error kind and position, without quoting its text, so neither command prints words the break rule does not trust. The orchestrator now reads the ledger only through `report` or `show` (the retries a receipt used, the recorded answers of a partly collected answer, a closed checkpoint another step looks for, the words the Stage 6 record quotes) and never opens the file; the review-form note looks for its closed checkpoint through `show`. The orchestrator carries an updated content lock. Behind it, `run_ledger` and `/ars-mark-read` share one atomic write, `ars_mark_read.atomic_replace`, and import the peer lock and the duplicate-key YAML loader by public names (`ledger_lock`, `UniqueKeySafeLoader`); the eight orchestrator sections measured from their H2 heading to the next heading share one measure helper and one table-driven budget test in `scripts/test_v3_6_7_phase_6_6.py`, with each section's measurement and budget unchanged. Whether a session model reads the ledger only through the script is not measured.

- **The human-read ledger readers no longer quote the ledger's text in their errors, and the claim-strength disposition writer uses the shared atomic write (#945, #946).** `human_read_attestation_resolver.py` and `/ars-mark-read` report a parse failure by error kind and position through `parse_error_where`, which moves from `run_ledger.py` (#898) to sit beside the shared duplicate-key loader. The duplicate-key message no longer names the key, and the unexpected-key message gives a count instead of the names, since a malformed log can put the user's words in either place. The three ledger readers (`run_ledger` included) now catch every failure from reading and parsing, not only YAML errors, because PyYAML's scalar constructors raise `ValueError` or `KeyError` quoting the value for a bad `!!int`, `!!float`, or `!!bool` tag. `claim_strength_drift_disposition.py` writes its sidecar through `ars_mark_read.atomic_replace`; its temporary file name now ends in `.tmp`.

- The three marker-block sync lints (`check_routing_core_sync.py`, `check_method_weaknesses_sync.py`, `check_review_form_note_sync.py`) share one copy of the marker grammar, `first_difference`, and the copy check, now in `scripts/_skill_lint.py` (#923). Error messages and exit codes are unchanged; the grammar is tested once in `scripts/test_skill_lint_marker_block.py`, and each lint's test file keeps its wiring tests.

- `/ars-citation-check` inherits the session model instead of pinning `sonnet` (#912). On the closed-book factuality benchmark in the Sonnet 5.5 system card (§6.3.2.1, AA-Omniscience), Sonnet 5.5 gives an incorrect answer on 27% of questions, against 17% for Opus 5.5 and 21% for Mythos 5.1, which shares Fable 5.1's weights. Citation checking depends on recalling authors, years, and venues correctly. The other twelve light commands keep the `sonnet` pin. A run on a Sonnet session still runs on Sonnet; a run on another session now runs, and is billed, on that session's model. The citation-check eval cases keep `model: sonnet`; that alias moves to each new Sonnet release, so eval runs are comparable only when the resolved versions match.

- **Schema 1 (RQ Brief) now matches what `research_question_agent` produces (#938).** `finer_scores` is typed 1-5 per criterion with the producer's threshold (average >= 3.0, none below 2), where it said 1-10. `methodology_type`, `theoretical_framework`, and `keywords` move to optional fields, since the RQ Brief producer does not emit them and no consumer reads them from the brief. The four `scope` axes stay, and the agent's Scope Boundaries now emit Domain, Timeframe, Geography, and Population lines, the keys that `sub_question_bindings` and the Phase E4 scope comparison use. Before this, a consumer that applied the file's `HANDOFF_INCOMPLETE` rule to missing required fields had to reject every brief the producer wrote.

### Fixed

- **`sr-screener` spreadsheet exports neutralise a formula even when a quote, space, or line break separates it from a `;` or tab (#951).** `srlib.spreadsheet_text` escaped a formula prefix only when it came right after `;` or a tab, so a title such as `Synthetic;"=1+2;tail` or one with `=1+2` on its own line still exposed a formula cell to a `;` or tab reader of the comma-separated exports. The escape now allows quotes and whitespace in between and treats line breaks as separators; text with no formula start is unchanged. Other delimiters such as `|` or `:` are out of scope.

- **The Stage 2.5 and 4.5 checkpoints can show the evidence rows when the integrity check ran as a subagent (#933).** The rows are checked against the source text the integrity agent read, and that text used to stay in the agent's own session, so a checkpoint in the orchestrator's session had nothing to check against and stopped with a render failure. The orchestrator now names a folder in each integrity dispatch (`<passport-stem>_evidence_sources/stage-2.5/` or `stage-4.5/` beside the passport, or run-local storage outside the repository without a passport file); the integrity agent writes the text of each source-bound row's source there under a name `source_file_name` derives from the ref_slug (distinct even on case-insensitive and Windows file systems), builds the row from that file (and, when it rewrites a file in a correction round, rebuilds every row that names that source), and names the folder in the new Integrity Report field `phases.E_claims.evidence_source_dir` (Schema 5; `null` when no row is source-bound). `scripts/evidence_rows.py validate` and `render` take `--source-dir` for these V1 rows, which reads only the files the source-bound rows name, as exact UTF-8 bytes with no newline translation, and refuses a link or a folder in their place; a missing or changed file still fails the render. `docs/DATA_FLOWS.md` lists the folder as a local store, since a file can hold a whole article; it is never sent with another dispatch or to the Codex audit wrapper. The orchestrator carries an updated content lock. Whether sessions write the folder and pass it is prompt-level and unmeasured.

- **The checkpoint reads evidence source text only from the folder the orchestrator named, and the evidence-row CLI refuses advisory rows by name (#948, #947).** The Stage 2.5 and 4.5 checkpoints render with `--source-dir` set to the folder named in that stage's integrity dispatch. An Integrity Report whose `phases.E_claims.evidence_source_dir` names another folder is a contract failure that does not advance the checkpoint. Replay already required each file to match its row's source hash, so this changes where the text is read from, not what can be shown. `scripts/evidence_rows.py validate` and `render` refuse `evidence-row/1.1` advisory rows with exit 2 and name `scripts/build_content_coverage_advisory.py`, where they used to report a missing `ref_slug` or an invalid source-map key. The orchestrator and `scripts/evidence_rows.py` carry updated content locks. The checkpoint rule is prompt-level and unmeasured.

- **A source the integrity gate found to be fabricated stays out of later revisions, and an abstract the revised body contradicts is caught before final output (#936).** When a Stage 2.5 or 4.5 integrity report judges a reference `NOT_FOUND`, the orchestrator now records it in a new optional Material Passport list, `excluded_sources[]` (schema `shared/contracts/passport/excluded_source_entry.schema.json`, tests `scripts/test_excluded_source_entry_schema.py`), and every later writer dispatch leaves it out of the corpus context and bibliography it carries (orchestrator § Integrity-Excluded Sources (#936)). Before, removing the citation did not reach the corpus context, so a later revision could cite the same source again and only the next gate caught it. A cross-model `NOT_FOUND` is recorded once the user confirms it at the checkpoint. `literature_corpus[]` is not mutated; only the user's own turn restores an entry, and the next gate verifies the source again. Revision mode still does not regenerate the bilingual abstract. Instead, the integrity agent's C2 internal-consistency check now compares each abstract with the body: a sentence the body contradicts, or a finding the body no longer contains, is a MEDIUM issue, and a correction that changes the body's findings, numbers, or scope re-runs the whole C2 check, not only the corrected items. A standalone revision always says the abstract was not regenerated, and quotes the abstract sentences the patch now contradicts. Both changes are prompt-level; whether runs follow them is unmeasured.

- **Opt-in switches no longer collide with the default flow once you turn them on (#929).** Under a strict terminal policy or `ARS_CLAIM_AUDIT=1`, items the formatter will refuse at Stage 5 used to surface only after the Stage 5 entry gate, where nothing can be revised. In pipeline runs the Cite-Time Provenance Finalizer and the claim audit now first run at Stage 4.5 (orchestrator § Final-Output Pre-Check at Stage 4.5 (#929)): each refusal-bound item is a Stage 4.5 issue that goes through the correction rounds, and the Integrity Check FAIL Loop's "continue with a warning" does not carry it; Stage 5 reuses the claim-audit results when the draft is unchanged. A strict submission-package finding that needs a content change is no longer sent to the formatter fix loop. `ARS_PASSPORT_RESET`: MANDATORY checkpoints are reset boundaries too (the protocol said they reset only "when also FULL", which no checkpoint is, so the pending-decision path for the Stage 3 and Stage 5 decisions never ran; the protocol, state machine, and docs now agree); an in-session `continue` asks a pending decision first; resume asks the user before taking the passport lock, then re-reads and re-checks under it; the Stage 6 record says which parts come from the passport and ledger after a reset. `ARS_INQUIRY_LEDGER` says when its required profile binding is missing and stays linear. The model-tiering summary in both `SKILL.md` files keeps "where the protocol permits". Cross-model disagreements and `NOT_SEARCHED` results are resolved at the checkpoint and become issues only when confirmed. `docs/SETUP` (en and zh-TW) describes `ARS_CROSS_MODEL_SAMPLE_INTERVAL` correctly and adds `ARS_CLAIM_AUDIT`, `ARS_SOCRATIC_ADJACENT_PROBE`, `ARS_RE_REVIEW_LEGACY`, and `ARS_INQUIRY_LEDGER`.

- **Stages 5 and 6 produce only the files you ask for, and some stages stop starting steps you did not ask for (#928).** Stage 5 asks which files you want (DOCX, PDF, the .tex source), starting from the Paper Configuration Record's output format, instead of "Need LaTeX?": a PDF is still compiled from LaTeX generated from the Markdown, and the .tex file is delivered only when asked for; if tectonic is unavailable, the stage says so. Stage 6 asks whether to add a PDF to the Markdown process record, and follows a standing constraint that already answers it. The formatter no longer writes a missing Limitations section: it reports it, and it checks the body word count against the target and any word ceiling without cutting text. A full-mode Stage 2 stops after Phase 6, since formatting belongs to Stage 5. After the Stage 3 decision, when your answer already says how to revise, revision coaching is offered instead of started. The checkpoint after Stage 1 (or entry) says when the corpus holds sources without their originals on file, since final output refuses those citations. The submission-package advisory path continues to the Stage 5 completion checkpoint instead of reading as the pipeline's end; the Stage 6 self-reflection no longer suggests cross-model verification you declined and counts SLIM checkpoints separately from skipped stages; `academic-pipeline/SKILL.md` describes the Collaboration Quality Evaluation as assessing the user.

- **The integrity gates follow one set of rules, and no longer block what a correction round cannot fix (#926).** The orchestrator's failure table now matches the state machine: after three correction rounds at Stage 2.5 or 4.5 the user decides on the listed items, instead of "cannot override" at 2.5 and an automatic abort after a second failure at 4.5 (which also pointed at a non-existent "Stage 5 (revision)"). A claim whose source is real but behind a paywall (`UNVERIFIABLE_ACCESS`) is a note, not a MEDIUM issue: it never blocks, and the checkpoint lists each one with an offer to re-verify against full text the user supplies. Stage 4.5 still needs zero issues, so a MINOR item goes to a correction round, while notes that are not issues (access notes and the existing advisory notes) do not hold it. The AI Research Failure Mode Checklist gets an owner (`integrity_verification_agent`, with a row on the MANDATORY integrity checkpoint) and the pre-filter promised since v3.2: Modes 1, 3, 5, and 6 are NOT APPLICABLE when the scholar declared no experiments (#925), unless the draft presents experiments or data analyses as run by the author (results, run settings, or Methods text; a literature search and screening procedure does not count), in which case Modes 3 and 6 are SUSPECTED. A mode flagged for that conflict resolves as NOT APPLICABLE once the corrected draft agrees with the declaration, and the Stage 6 record keeps NOT APPLICABLE apart from CLEAR. Mode 4 is flag-only at Stage 2.5 in the block rule as well as in its own description. Stage 3' is no longer listed as skippable, and the state machine marks the Stage 3 and 3' review decisions MANDATORY.

- `scripts/check_acronyms.py` reads two forms of one construction the same way (#906). Emphasis marks around the words inside a parenthetical are ignored, as they already were before it (`(*結構方程模型*,SEM)`). A restatement or naming lead set off as its own comma item is read as if absent, as it was at the start of the parenthetical, so `(a model, i.e., SEM)` is a use and the expansion of `(structural equation modeling, hereafter, SEM)` no longer holds the lead. A cross-reference with a target before the acronym is read as a restatement in both languages, so `randomized controlled trials (see Section 2, RCT)` defines `RCT` as `隨機對照試驗(見第二節,RCT)` already did, and the Chinese expansion is now the words before the parenthetical, not the cross-reference; `see` alone or before an example lead (`see for example`) stays an example lead, and a Chinese word that starts with `見` (`見習醫學生`) stays an expansion. Citations before the acronym are skipped as citations after it were (`(Smith, 2020; RCTs)`). After a label letter that is also a Roman numeral (C, I, L, V, X), a dash and a letter one to five after it are a range, so `Table C – D show` is prose, while `Table C – T cell counts` and `Table I – T cell counts` stay captions. `\\$RCT$` is math, because an escaped backslash leaves the dollar free. The Markdown report shows a control or bidirectional character in an unread-section heading as its `\uXXXX` escape; the JSON report keeps the heading as written. Synthetic tests pin each case and its counterpart; the pinned full-fixture reports are unchanged.

- The spectrum summary in `MODE_REGISTRY.md` counts Fidelity 17 and Originality 3 of the 27 modes it covered, matching the mode rows and `shared/mode_spectrum.md`; it said 16 and 4 (#944). The #919 merge later recounted the summary for 35 modes.

## [3.22.2] - 2026-09-25 — Run ledger and handoff check, acronym check, a wider instruction/data boundary, and routing and front-page repairs

> **Two deterministic checks pinned by synthetic tests; the prompt-level changes are unmeasured:** v3.22.2 adds a run ledger (#887). When a pipeline run has a passport file, the orchestrator appends the user's initial instructions, each checkpoint's question and the user's answer in their exact words, step receipts, and file hashes to a local ledger beside the passport. After compaction, resume, or a subagent return, `scripts/run_ledger.py report` compares the ledger with what the summary or report claims and lists the differences; it now prints that handoff check itself, in English or Traditional Chinese, and hashes the files an entry names when the entry is written (#898). The ledger holds the user's exact words, and `docs/DATA_FLOWS.md` lists it and how to delete it. The release also adds `scripts/check_acronyms.py` (#849, proposed by @reiropke), which reports acronyms that are undefined, defined after first use, or defined twice, without calling a model; the prompts have the calling session run it on saved drafts and abstracts, and a review appends its report to the Editorial Decision Letter as an advisory attachment that the decision, the revision roadmap, and re-review criteria do not draw on. Synthetic tests pin both scripts; whether runs write the ledger entries or call the check is unmeasured. The instruction/data boundary now reaches third-party text in dispatches and passport imports (#890), text a receiver reads through its own tool calls, and each skill's main session (#894); a lint pins every copy, and the effect is unmeasured (the opt-in claim-audit judge prompt changes with it, so cached verdicts from the old prompt are not reused). Repairs: the routing core reaches plugin and skills-copy installs (#892); an explicit request stays explicit when the mode's usual input is missing (#889); `/ars-lit-review` no longer points a run under way to another workflow (#897); the revision coach keeps peer review out of the committee-correspondence variant (#854); the orchestrator scopes "authoritative" skill output to deliverable ownership (#888); and the front page and showcase now match their sources (#908). The routing results come from one session per fixture, a smoke test rather than a rate. One new schema describes the ledger; no existing schema, command model, or effort setting changes.

### Added

- **A run ledger keeps what compaction and subagent returns can drop (#887).** When a pipeline run has a passport file, the orchestrator appends the user's initial instructions, each checkpoint's question and the user's answer in their exact words, partly collected multi-item answers, receipts for steps that report only on stdout or by exit status, progress counters, and hashes of transient input files to `<passport-stem>_run_ledger.yaml` beside the passport, through `scripts/run_ledger.py` (schema `shared/contracts/passport/run_ledger.schema.json`). Writes take the read log's peer lock and replace the whole file atomically; each entry carries its own hash and the previous entry's. After compaction, on resume, and after each subagent return, `run_ledger.py report` compares the ledger with what the summary or report claims and sorts the differences into four groups (awaiting your answer, cannot confirm, not run, missing). The orchestrator shows that handoff check only when it has items; a missing or unreadable ledger backs nothing, and a broken chain backs nothing from the break onward. After a compaction or resume, the SessionStart announce adds one sentence asking for the check. The pipeline state machine, orchestrator, and state tracker name the ledger (content locks updated in the same commit); `docs/RISK_REGISTER.md` adds R12 (handoff loss, cross-referencing R11); `docs/DATA_FLOWS.md` lists the new local store, which holds the user's exact words, and how to delete it. Six synthetic scenarios pin how the report reads a ledger (`scripts/test_run_ledger.py`); whether the orchestrator writes the entries is prompt-level and unmeasured, and the hashes detect accidental damage, not deliberate edits, a lost tail, or a restored older copy. Design: `docs/design/2026-09-23-887-handoff-integrity-design.md`.

- **A deterministic acronym check (#849, proposed by @reiropke in #848).** `scripts/check_acronyms.py` reads a Markdown or plain-text manuscript, calls no model, and never changes the file. In each scope it reports an acronym that is never defined, defined after its first use, or defined twice; the body, the English abstract, and the Chinese abstract are separate scopes, the Chinese form `全稱(English full form, ABBR)` counts as a definition, and a default list plus the author's own list exempt acronyms that need no definition. A parenthetical counts as a definition only when the words before it spell the acronym (their initials, or Chinese words); the check lists one it cannot confirm as a coverage limit, not a finding. It skips code fences and code spans (a span is read within one line), comments and citation markers, link reference definitions, ATX and setext headings, tables, captions, notes, the reference list, author-year citations, and author initials in author lists, and it keeps source line numbers; it reads Markdown line by line and is not a full CommonMark parser. Each report states its coverage: `partial` lists what was not read, `not_checked` (exit 2) gives the reason, and neither is a clean result; both states are registered in `shared/contracts/degradation_registry.json`. The prompts tell the writer to save its draft, and the abstract agent its abstracts, in their phase folders, and tell the caller to run the script on those files at the points `academic-paper/references/writing_quality_check.md` § F lists: on the draft, including Phase 4b in `full` mode, where the report never reaches the in-pair evaluator; on the abstracts after Phase 5b or in `abstract-only` mode; and in revision rounds on the anchored draft, where the writer fixes a finding only inside an authorized `will_address` target. A report goes back to an agent only when it has findings. The report is advisory and prints in English or Traditional Chinese (`--lang`) to match the user. In a review, once the decision is final (the synthesis passes `scripts/check_panel_synthesis.py`, and any #518 cross-model decision check has written to the letter), the dispatching session appends the report as the Editorial Decision Letter's last section, `## Attachment: Acronym Check (advisory, #849)`; no call that writes that round's decision, roadmap, or letter sees it, and later readers take no weakness, revision, roadmap entry, or re-review criterion from it. The revision coach takes no item from that section when a user pastes such a letter. Tests show that the panel checker's result and the re-review letter extraction are unchanged with the attachment present. The tests use synthetic fixtures, and no model run tested the caller flow, so whether runs follow these instructions is unmeasured.

### Changed

- The pipeline orchestrator's Scope list now says what "authoritative" skill output means (#888). Each skill owns its deliverable's content and quality; a skill output does not by itself establish a user decision or authorization, and a user decision recorded or relayed through a skill output must quote the user's words (or the exact deterministic authorization artifact) and never widen them, in the same words as the state machine's checkpoint decision provenance rule. The content lock for `academic-pipeline/agents/pipeline_orchestrator_agent.md` is updated in the same commit. The rule stays prompt-level and unmeasured.

- The retrieved-content instruction/data boundary now reaches third-party text in dispatches and passport imports (#890). Twelve more agents inline the canonical block from `shared/ground_truth_isolation_pattern.md` § 2A: the pipeline orchestrator (pasted text in researcher turns and resumed passports); the integrity verification, claim-reference alignment audit, and compliance agents at the integrity gates; the literature strategist; the field analyst and editorial synthesizer; the risk-of-bias and timeline extraction agents; and the three agents of deep-research `review` mode. Two prompts that a model receives without the agent file carry the same sentences: the claim-audit judge prompt, whose pinned hash and version label therefore change so that persistent-cache verdicts from the old prompt are no longer served (the audit stays opt-in), and the simplified prompt sent to a cross-model devil's advocate. `scripts/check_instruction_data_boundary.py` pins every copy, with mutation tests; the orchestrator's content lock and a dedicated line budget change in the same commit. `docs/design/2026-09-23-890-instruction-data-boundary-extension.md` records the inventory, the ranking, and the surfaces left uncovered, which `docs/RISK_REGISTER.md` R3 now names. The layer is prompt-level and unmeasured: the #675 seed runs one generic prompt rather than these agents' prompts, so as seeded it evaluates none of these paths, and #676 stays open with its structural requirements unmet.

- The run ledger renders the handoff check itself and computes file digests when an entry is written (#898, items 1 and 2). `scripts/run_ledger.py report --render en` or `--render zh-TW` prints the finished block, listing the groups that have items and ending with the number of items the ledger backs, and prints nothing when there is nothing to report; the orchestrator inserts it verbatim instead of writing the block from the JSON, and picks `zh-TW` when the user writes in Traditional Chinese and `en` otherwise. The block escapes Markdown and HTML characters in ledger text, including a leading `#`, `-`, `+`, or list number that would open a heading or a nested list, so a file name such as `*draft*.md` or `# draft.md` displays as the JSON holds it. `append` hashes the files an entry names (a `file_reference` path, and a receipt's `input_paths`, stored as `input_sha256`) and refuses a supplied digest that does not match its file, so a mistyped digest is refused when it is written instead of showing up later as a changed file. `report` re-checks every receipt's `input_sha256`, including one supplied directly or written before this change, and no longer counts a receipt as backing its step once one of those files changes or disappears. It lists the recorded outcome of every step whose receipt still backs it (`step_outcomes`), and the state tracker takes a step's outcome from there rather than from the receipt alone. The stored entry format is unchanged: `input_paths` is not stored, because `append` turns it into `input_sha256`, which maps each path to its digest. Items 3 and 4 of #898, a read command that returns only trusted entries and shared write helpers, remain open.

- The instruction/data boundary now reaches third-party text that a receiver reads through its own tool calls, and the main session of a skill run (#894). Five more agents inline the canonical block from `shared/ground_truth_isolation_pattern.md` § 2A: the formatter, which may read a journal's author guidelines; the citation compliance agent, which checks Retraction Watch and resolves DOIs; and the three citation emitters (`synthesis_agent`, `draft_writer_agent`, `report_compiler_agent`), which may read a source PDF to find the page for an anchor. The single-reference verification prompt that the integrity gates send to a cross-model verifier on the API route carries the same sentences, because the verifier's own web search returns third-party pages. The four `SKILL.md` files carry the block as the main session's home: a skill's `SKILL.md` loads on every install path, while `.claude/CLAUDE.md` loads only in a checkout. `scripts/check_instruction_data_boundary.py` pins every copy, with mutation tests. `docs/design/2026-09-24-894-instruction-data-boundary-tool-calls.md` records the inventory and the surfaces left uncovered, which `docs/RISK_REGISTER.md` R3 names; they include the ChatGPT-subscription transport's instructions to the verifier and the startup announce, whose texts the #787 bakeoff and the #892 routing fixtures measured, so changing either is left to a maintainer decision. The layer is prompt-level and unmeasured: the #675 seed runs one generic prompt rather than these prompts, and #676 stays open.

### Fixed

- Routing Discipline keeps an explicit request explicit when the mode's usual input is missing, and scopes the `[direct-mode]` missing-input rule to an honored token (#889). The #133 routing fixtures were run on Claude Opus 5.5 and Claude Fable 5.1, one fresh session per fixture, scored on every `expected.yaml` field. The first pass failed three fixtures on Opus 5.5 and one on Fable 5.1. In three of the four failures the session asked the user to choose among workflows although the request was explicit (a Spanish revision request with no reviewer comments, on both models, and "revisar artículo" on Opus 5.5); in the fourth, Opus 5.5 named the `[direct-mode]` target but neither read nor dispatched it because its files were missing. `.claude/CLAUDE.md` Step 1 and `shared/references/intent_clarification_protocol.md` now say a request stays explicit when the mode's usual input is absent or a word in it has other everyday senses. In the second pass Opus 5.5 failed a fixture it had passed (it treated an agent named mid-message without the byte-0 token as explicit intent), so Step 0 now applies the missing-input rule only when the token is honored. In the third and last pass, both models passed all 11 fixtures that run on them; the pass is not held out, because the prose was tuned against these fixtures, and one session per fixture is a smoke test, not a rate. Fixture 04 runs on Sonnet 5 through its command's model pin: in two of its six sessions, with no papers attached, Sonnet offered to hand the request to `deep-research` lit-review and asked the user to choose, so it goes to a follow-up issue (#897). `tests/fixtures/issue_133_routing/CALIBRATION_LOG.md` records the condition, the scoring rules fixed before the run, and every pass; its README names the calibrated models and scores every field. The pass covers the repo-clone install only (#892).

- `/ars-lit-review` no longer points a run already under way to another workflow (#897). The command's closing note told the model to prefer the `deep-research` skill `lit-review` mode for the upstream research-side review. The command body loads only after the user has picked the command, and `academic-paper/SKILL.md` and `MODE_REGISTRY.md` already say how the two modes differ, so the note now says only what Routing Discipline Step 1 requires: stay in `academic-paper` `lit-review` mode and do not reopen the choice of workflow; when the papers or sources the request refers to are missing, ask for them or offer to search within the mode. This addresses the fixture-04 observation recorded under #889 above; one of the two sessions (out of six) that offered the hand-off quoted the old note, which shows the note was read, not that it caused the offer.

- The revision coach's committee guard now names the case it must not route: journal or conference reviewers, editors, area chairs, and program committees are peer review, not a committee for the #668 committee-correspondence variant, even when the user names the venue or the venue calls the role a committee (#854). `academic-paper/SKILL.md`, `revision_coach_agent.md`, and the variant's activation gate in `committee_correspondence_protocol.md` all say so, and `scripts/test_check_committee_correspondence.py` pins the sentence in each. The eval results that follow are the maintainer's, from run outputs that are not committed. In the #853 verification runs of `plugin-evals` case 03, one session applied the committee variant to ICLR reviews and said the user had identified a real committee, and a second produced the variant's shape without naming it. Case 03 gains a with-only regex grader that fails when an answer emits a line of the variant's output (that shape, or the concern IDs, status line, and footer the protocol requires) and passes an answer that declines the variant in prose; against the stored runs it flags those two and no other. A seven-run retest on Claude Opus 5.5 found 0 of 7 taking the branch with an earlier text of the guard and 0 of 7 on unchanged `main`, so it shows no effect of the guard: the misroute was seen on an earlier model, and the guard is prompt-level and unmeasured. On case 04, the real ethics-committee letter, the 2 of 7 runs that invoked the skill both took the committee variant; in the other 5, Opus 5.5 answered without invoking the skill, which the guard cannot affect. `plugin-evals/README.md` records the retest and corrects its account of the earlier runs.

- The routing core now reaches plugin and skills-copy installs (#892). Claude Code loads `.claude/CLAUDE.md` only for sessions started inside the checkout and does not load a plugin's `CLAUDE.md`, so a plugin install ran without Routing Discipline Steps 0-3 and the #133 anti-pattern; in the #133 fixtures, a plugin-install session on Claude Opus 5.5 failed 6 of 12. The block now has one home, `shared/references/routing_core.md`: `.claude/CLAUDE.md` and every skill's `SKILL.md` carry byte-identical copies, the SessionStart announce reads the file at every session start, and `scripts/check_routing_core_sync.py` fails CI on any difference or on a skill without the block. After compaction, resume, or a fork, the announce's lead-in limits the core to a new request, so a run under way is not routed again; that wording is untested, because every fixture is a session's first message. After the fix, the plugin install matched the repo clone on all 12 fixtures in the Opus 5.5 sessions and on 11 of 12 in the Claude Fable 5.1 sessions. In both, fixture 04 runs on Sonnet 5 through its command's model pin, and fixture 05 passes in the plugin install only under two scoring readings the log records (a read the permission check refused counts as reading the agent file, and a paraphrased Skill argument counts as carrying the stripped message); scored strictly, it fails there on both models. On fixture 06, a `[direct-mode]` token placed mid-message, Fable 5.1 honored the token in 5 of 6 plugin-install sessions and in none of 6 repo-clone sessions, so #892 stays open for it. A skills-copy install gets the core at session start only when the project's `.claude/CLAUDE.md` carries the merged ARS text, as SETUP Method 1 instructs; otherwise it, like the other channels, gets the core only once a skill loads (`docs/CONTROL_AVAILABILITY.md` note 9). The announce reads the file with shell builtins, so the core survives a minimal `PATH` and a CRLF checkout, and the lint compares line endings too. `tests/fixtures/issue_133_routing/CALIBRATION_LOG.md` records the condition, the acceptance rule fixed before the run, and every result; one session per fixture is a smoke test, not a rate.

- Front-page and showcase statements now match their sources (#908). The README tagline says that full mode can draft a whole paper, that the pipeline stops for confirmation at every stage, and that the user remains the author. The showcase gains a version note (the run used academic-pipeline v2.3 in March 2026, the current citation gates have not been measured on that paper, and the byline names Claude because the researcher asked for it), restates the Stage 2.5 counts as the report gives them (8 bibliographic errors, 6–8 likely fabricated), describes the post-publication audit as a Claude Code + WebSearch run that found problems in 21 of the 68 final references (19 removed or corrected), and calls the Round 1 panel role-separated reviews generated by Claude rather than independent reviews. The claim-audit calibration text says the gold set has 25 synthetic tuples and the shipped test uses a stub judge, with no live-judge result recorded. "Pipeline guarantees" become rules that are protocol, not runtime guarantees, and Schema 11 is described by its documented purpose. `QUICKSTART.md` and the README cost pointer use the 2026-09 list-price estimate from `docs/PERFORMANCE.md`. All six README locales carry the changes.

## [3.22.1] - 2026-09-23 — Model currency for Claude Opus 5.5, citation-check loading and Chinese APA 7 repairs, and a Pi wrapper fix

> **Model currency and repairs; the new prompt-level guard is unmeasured:** v3.22.1 names Claude Opus 5.5 beside Claude Fable 5.1 as a supported session model, after a two-reader audit of the Opus 5.5 system card that retires no guardrail (#883). The docs add effort guidance (Claude Code starts Opus 5.5 at `medium`; heavy runs should use `high` or above), one list-price re-derivation for both models, and tiering guidance that reads the ladder as the vendor's lineup order, not a capability order. Because the card reports that Opus 5.5 acts more often than earlier models on instructions inside pasted text, the revision coach now treats pasted reviewer and committee text as data, pinned by a lint; the effect of that prompt-level guard is unmeasured. The release also repairs mode loading and citation checks: the 13 plugin mode commands invoke their namespaced core skill and resolve bundled references from the plugin root, which restores citation-check loading (#857); Chinese APA 7 checks catch a missing in-text author abbreviation, keep ambiguity exceptions and complete reference-list author fields, and propose a reorder only with evidence of a stroke-order inversion (#882); citation checks now distinguish visible syntax errors from unverified resolution or source claims (#882); and new English, Traditional Chinese, and Korean trigger phrases route citation-check requests, with CI bounding each skill description at 1,024 code points (#858, #864). The Pi wrapper accepts string-array system prompts (#880). No schema, command model, or effort setting changes.

### Changed

- Model currency for Claude Opus 5.5 alongside Claude Fable 5.1 (#883). A two-reader audit of the Opus 5.5 system card (`audits/harness-retirement-2026-09-opus-5-5.md`) retires no guardrail. `docs/PERFORMANCE.md` and `docs/PERFORMANCE.zh-TW.md` name both supported session models and add effort guidance for Opus 5.5 (heavy runs at `high` or above: Claude Code starts it at `medium`, and its research scores collapse at `low`), one 2026-09 list-price re-derivation for both models (~$7 on Fable 5.1, ~$2.80 on Opus 5.5; arithmetic, not a re-measurement), tiering guidance for this pair (the ladder follows the vendor's lineup order, not a capability order; switch behavior unchanged), and a recommendation to hand ARS third-party text as files rather than pastes. `shared/model_tiering.md` defines "family" as the vendor lineup rather than Claude Code's `opus` / `fable` family aliases, states that tier position is not a capability ranking, and corrects the declared-model note: Claude Code shows a transcript notice on a classifier fallback and keeps the session on the fallback model until the user runs `/model`. The revision coach now inlines the canonical instruction/data boundary, pinned by `scripts/check_instruction_data_boundary.py`, whose mutation tests now cover every hot-spot agent, because pasted reviewer and committee text is the channel the card reports as a regression (§6.5.1). `docs/RISK_REGISTER.md` R3's risk statement now names pasted third-party text, and R1, R3, R5, and R11 point to the new evidence. The Phase E claim-verification protocol names the model of its 2026-07-22 baseline instead of "the current frontier model", and the cross-model table's session-model row names no model version, as its own note requires. No command `model:` or `effort:` setting changes.

- Citation-check routing: add intent-specific English, Traditional Chinese, and Korean phrases to the academic-paper description, retaining its Spanish trigger. CI now checks each of the four parsed skill descriptions against the 1,024 Unicode-code-point limit (#858, #864).

### Fixed

- Plugin mode commands now explicitly invoke their namespaced core skill and use plugin-root paths for bundled references (#857). The 13 mode commands remain user-invocable; automatic routing selects the core skills directly, avoiding a command stub that can skip its nested skill call. Session-start and resume announcements now give the core Skill-tool targets alongside the user command list. The stubs require a visible stop on loading failure, and a command-dispatch lint guards their frontmatter, target skills, and rooted references. Runtime loading evidence covers citation-check through both slash-command and natural-language entry points; other modes receive static coverage.

- Chinese APA 7 citation checks now test for missing in-text author abbreviation as well as improper abbreviation, preserve ambiguity exceptions and complete reference-list author fields, and require evidence of a stroke-order inversion before proposing a reorder (#882). Citation-check explicitly loads the Chinese guide; Latin alphabetical defaults no longer override the supplied Taiwan/venue ordering rule. Synthetic regression cases cover true inversions, two-author citations, disambiguation, and a romanization override. The same regression run exposed a DOI-prefix heuristic being promoted to a mandatory correction; citation checks now distinguish visible syntax errors from unverified resolution/source claims.

- Pi wrapper: accept string-array system prompts without flattening blocks or mutating host input, while preserving the upstream Pi string path. Add array regression coverage and document the XML-only skill-hiding scope ([#880](https://github.com/Imbad0202/academic-research-skills/pull/880)).

## [3.22.0] - 2026-09-16 — Output-language-pair contract, locale track, plugin eval suites, and Windows / transport repairs

> **Additive structure, bounded evidence:** v3.22.0 lets a run declare its output language pair through a registry-keyed Schema 4 field whose absence reproduces the legacy files exactly (#862 Phase 1, PR #869), and stands up the locale track around it: an es-ES README and conservative trigger phrases contributed by @didacrios, and a community-maintained locale-pack policy with a provisional single-owner route. Two `claude plugin eval` suites (revision-coach, citation-check) and the reviewer-calibration harness ship as regression guards and dispatch substrates only; none claims a measured uplift or calibration value. Repairs: `/ars-mark-read` and the other five lock sites run on Windows through one shared helper with an `msvcrt` backend, the OpenAI request builders stop sending parameters GPT-6 Astra rejects, the contained Codex transport refuses `effort=ultra`, audit provenance records the actual judge identity, Socratic path F6 no longer preselects a direction, and an unsupported claim can no longer be rescued by hedging. READMEs keep three releases; Gartenberg et al. and Wang, Li et al. join the human-in-the-loop anchors. Roadmap Phase 4 (stage-level evidence ceilings) is not delivered in this release; its window carries forward.

### Added

- **A run can declare its output language pair, additively, with a visible failure path and no silent fallback (#862 Phase 1, PR #869; toward #850, not closed).** New `shared/output_language_pair.md` is the contract: a registry of opaque, registry-keyed pair tokens whose language roles, order, and script classes are declared by the entry (initial entry `zh-tw-en` — L1 Traditional Chinese, L2 English; roles are never parsed out of the token), a pointer to the abstract length / keyword regime table (the figures themselves live there, not here), whose single source is `academic-paper/references/abstract_writing_guide.md` (Standard / Conference / Extended abstract / Dissertation rows for the default entry), the no-single-language-pairs rule for Phase 1, and the failure rule that an unsupported or malformed value aborts visibly while naming the registry. Schema 4 (Paper Draft) gains the optional `output_language_pair` field, and the Schema-4 keywords row stops restating a count (`3-6 each`) — it references the guide's regime table instead. Folding the guide's two conflicting checklist figures (English 150-300 words and a duplicated keyword restatement) into that one table is the guide's own reconciliation, not a second copy kept beside it. **Absence is the legacy state**: with the key omitted the legacy object keys (`abstract: {english, chinese}`, `keywords: {en, zh_tw}`), the heading literals, and the omitted serialized key reproduce exactly, and a pre-#862 handoff keeps the same keys, headings, and serialized shape — a file-level literal claim, not a rendered-output or model-behaviour one. The migration is additive only. Pair selection stays separate from abstract cardinality, which intake already exposes. `scripts/check_spec_consistency.py` gains `check_output_language_pair_contract()`: registry membership for every pair a consumer advertises (backticked, the contract's own spelling rule), the default entry pinned to the legacy pair, the regime table required to live in the abstract guide and required absent from the contract, the Schema-4 field documentation required to carry the default token and the contract reference, and a malformed-value self-check so a validator regression fails the lint rather than passing vacuously. Unit tests live in `scripts/test_check_spec_consistency.py` and the new `scripts/test_output_language_pair.py` (registered in `scripts/_ci_pytest_manifest.toml`), with a deterministic four-case corpus under `tests/fixtures/output_language_pair/` (omitted, explicit default, unsupported, malformed) that the test consumes. The ten consumer surfaces ship in the same change and each names the `output_language_pair` field; eight of them state the default token `zh-tw-en` (`academic-paper/SKILL.md`, intake, the abstract agent, the structure architect, the abstract-writing guide, the workflow reference, the bilingual template, and the `ars-abstract` command), while the draft writer's serialization and the mode-selection guide reference the field without restating the token — each omitting the value when the field is absent. The formatter agent and every es-ES trigger line are untouched. No model-behaviour or rendered-output equivalence claim is made: the values held fixed are **file-level literals** (the heading literals and the Schema-4 legacy object keys), and acceptance rests on the deterministic contract tests plus the frozen live comparison runs recorded in the PR body (repository revision, model, and inputs pinned; reported as observed behaviour on that configuration, never as a general equivalence claim).

- **`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) were filed as #865 and closed by #867 (also by @didacrios): the three terms are dropped, fixture 11 takes fixture 09's shape (no reviewer comments yet; polish the draft), and fixture 12's untrue material-shape sentence is removed. 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/`, #859; 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

- **Wang, Li et al. (2026, arXiv:2609.07713) joins the human-in-the-loop anchors as the fifth, and the first that treats production and review as one coupled system; Brodeur et al. (2026, *PNAS*) is cited with its narrow reading (#873; #871 and #872 opened, neither closed).** `README.md`, `README.zh-TW.md`, and `README.es-ES.md` gain the fifth anchor paragraph (`README.zh-CN.md` never carried the anchor section); `POSITIONING.md` § What this is not places the simulated panel on the lowest rung of the survey's evaluative-authority ladder (§4.1). The Brodeur randomized study is quoted as its Crossref and Semantic Scholar metadata report it: human-only 94%, AI-assisted 91%, AI-led 37%, with AI-assisted teams detecting fewer major coding errors, so the anchor reads the study narrowly (in that study AI assistance did not verify better than humans alone and AI-led verification did much worse) and does not claim that ARS's own gates are effective. The two reviewer measurements the survey motivates are tracked as issues only: Round-1 presentation-invariance and soundness-counterfactual paired controls (#871) and author-identity-cue paired controls (#872). No prompt, schema, lint, or runtime change.

- **README changelog sections keep only the three most recent releases; the history moves here and into frozen locale archives (#870).** `README.md` carried 60 release summaries (76 KB, 68% of the file) and the five translations mirrored the list, while `check_spec_consistency.py` pinned 127 old headings across them, so the section could only grow. The 60 English one-paragraph summaries now sit verbatim as the leading blockquote under each entry in this file (11 releases that existed only in `README.md`, v1.0 through v2.6.2 plus v3.0 and v3.1, are backfilled as sections); the translated lists are frozen under `docs/changelog-archive/<locale>.md`. The lint replaces the heading pins with `README_CHANGELOG_KEEP` and `check_readme_changelog_section`, which fails on any extra `### v` heading or a missing CHANGELOG / archive link. The three `stage_capability_matrix.json` claim anchors that pointed into the README changelog now point at `CHANGELOG.md`. The Windows / real-Python prerequisite paragraph moves from `README.md` (and the ko-KR / es-ES translations) to `docs/SETUP.md` "Python (optional)" with a zh-TW counterpart.

- **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

> **Currency and provenance, not new capability:** v3.21.2 aligns the suite to the two September 2026 vendor system cards. `gpt-6-astra` enters the cross-model table as provisional on both transports and becomes the recommended OpenAI verifier under the generation-currency policy; `gpt-5.6-sol` keeps its validated status on the ChatGPT-subscription citation transport, and no new bakeoff result is claimed. The contained Codex transport's reasoning-effort set gains `ultra`. Two guardrails are added, both prompt-level and vendor-motivated rather than ARS-measured: checkpoint decision provenance (only a user turn is a decision; decisions are re-transmitted to subagents verbatim; risk R11) and provider-side monitoring or safety interventions named as a transport failure that is never a verdict. A harness-retirement audit against both cards retires nothing (0 prompt-text retirements; 8 keep-as-debt items now carry a card citation). Fixes: CJK titles no longer fail the exact-title gate in the four index resolvers (#798) and wrapper marks are stripped only as one balanced unit (#800); the autolink round-trip test declares its dependency (#801); `check_surface_form_parity` names a broken environment instead of the manifest; a skill-inventory parity lint (#809); the R10 residual gap de-staled (#813); an MLA key-rules line corrected (#805). Suite/pipeline → v3.21.2; deep-research → v2.12.1; academic-paper → v3.3.1; academic-paper-reviewer → v1.11.1.

### 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 `《Alzheimer’s病中ProEXC表达》` 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 (P 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

> **Measured where stated; otherwise bounded:** v3.21.1 repairs the contained ChatGPT-subscription citation transport for codex-cli 0.147.0 and records the first Promotion Bakeoff: `gpt-5.6-sol` is validated only for that subscription transport, while it remains provisional on the first-party API route. Future bakeoffs now require sealed preregistration. The release also adds a default-off research-workflow profile substrate (offline deterministic conformance only; no pipeline hook or family-specific shipped profile), an opt-in inquiry-ledger alpha (`ARS_INQUIRY_LEDGER=1`), and a design-only alternative register that is not implemented. Their behavioral evidence remains `NOT_RUN`; no usability, recovery, novelty, correctness, or research-outcome benefit is claimed. The review-criteria registry gains one source-backed illustrative MSR 2027 exact-profile proving set—not venue/discipline coverage, a real-author attestation, or constructive-review evidence—and its required independent-human evaluation remains open. Additional changes align `data_access_level`, consolidate markdown lint grammar, register guard-launcher degradations, and list OrcaRouter as a community integration without endorsement. Suite/pipeline → v3.21.1; deep-research → v2.12.1; academic-paper → v3.3.1; academic-paper-reviewer → v1.11.1.

### 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 / IDE` → `cli--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 `redacted` → `raw` (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_only` → `raw` (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

> **Transparency you can check:** v3.21.0 completes the ISO/IEC 42001-spirit audit track (#753–#760). Outward claims are aligned with the evidence record, and four standing artifacts now answer the questions users actually have: which controls operate in your install channel (`docs/CONTROL_AVAILABILITY.md`), what leaves your machine and what is stored (`docs/DATA_FLOWS.md`), how strongly each CI workflow actually enforces (`docs/ARCHITECTURE.md` §7.1), and which risk each mechanism addresses, with what evidence and what residual gap (`docs/RISK_REGISTER.md`) — each defended by its own CI lint. `GOVERNANCE.md` states decision authority, what cross-model review does and does not provide (an error-detection control, not organizational independence), and the end-of-life posture; `SECURITY.md` gains a solo-runnable severity-tiered triage procedure. These are distilled operating principles with informative anchors to ISO/IEC 42001, not a certification claim, and no new effectiveness numbers are claimed. Suite/pipeline → v3.21.0; deep-research → v2.12.1; academic-paper → v3.3.1; academic-paper-reviewer → v1.11.1.

### 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

> **Hardened and bounded:** v3.20.1 makes review and integrity claims match the evidence the suite can actually replay. Claim coverage is population-bounded with semantic completeness left unknown; revision claim-strength changes require explicit byte-bound author dispositions; new read attestations require scope and fail visibly; live reviewer packages remain `NOT_CALIBRATED`; and six-axis provenance replaces binary independence language. It also ships the offline/unmeasured claim-standing probe substrate and a closed first-round assignment gate for the ideation-diversity bundle, plus an opt-in roadmap for future inquiry branches and alternatives. These contracts do not establish improved scientific outcomes, reviewer correctness, complete semantic detection, authenticated human identity, independent error processes, or live-provider effectiveness. Suite/pipeline → v3.20.1; deep-research → v2.12.1; academic-paper → v3.3.1; academic-paper-reviewer → v1.11.1.

### 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 and hardened:** v3.20.0 strengthens evidence and authority boundaries across review, revision, citation, human-subjects, and submission workflows. It adds source-bound evidence rows, author-controlled non-ranking revision roadmaps, replay-bound consistency and content-coverage advisories, unified review criteria, human-subjects authority and pathway traces, deterministic submission and correspondence artifacts, optional post-run adjudication observability, a contained ChatGPT-subscription citation transport, an offline claim-standing candidate ledger, and an opt-in process-isolated PDF text/OCR advisory. Reviewer and re-review contracts gain role-scoped scoring, evidence-before-persuasion gates, and tighter provenance and transport handling; clinical reporting, Chinese-literature resolution, plugin aliases, Pi, and platform guidance are also extended. New evaluation assets include frozen hermetic fixtures and no-call envelopes for revision drift, role topology, ideation diversity, indirect prompt injection, and tortured-phrase screening. Unless a retained measured cohort is explicitly cited in `CHANGELOG.md`, these are protocol or synthetic/offline conformance artifacts, not evidence of safety, efficacy, accuracy, or behavioral improvement. Suite/pipeline → v3.20.0; deep-research → v2.12.0; academic-paper → v3.3.0; academic-paper-reviewer → v1.11.0.

### 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](https://github.com/dcs-scd) in [PR #567](https://github.com/Imbad0202/academic-research-skills/pull/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 15–20 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 P0–P3 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](https://github.com/ktao732084-arch) in [PR #600](https://github.com/Imbad0202/academic-research-skills/pull/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](https://github.com/ktao732084-arch) in [PR #601](https://github.com/Imbad0202/academic-research-skills/pull/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](https://github.com/ktao732084-arch) in [PR #599](https://github.com/Imbad0202/academic-research-skills/pull/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