Extends the #855 entry with the merged companion trigger change (a366e39):
body Español lines plus description subsets, the CONTENT_LOCKS re-pin, the
code-point description counts, the eval and routing-smoke evidence, and the
follow-ups #864 and #865.
Claude-Session: https://claude.ai/code/session_01CckFaPj7hPxWjCqn1dhbXt
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
654 KiB
Changelog
All notable changes to this project will be documented in this file.
[Unreleased]
Added
-
claude plugin evalsuite for theacademic-paperrevision-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 inplugin-evals/README.mdtogether with the run command, thetimeout_seconds/max_turnsceilings, 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-onlyskill-firedgrader 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.mdis a full translation ofREADME.mdat 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()inscripts/check_spec_consistency.pypins the version badge, release-note headings, mode headings, and forbidden retired phrases, andscripts/check_reviewer_role_label.pygains the es-ES REQUIRED entry. Translation only; no skill, mode, or trigger changes. The companion trigger change (#856, merged asa366e39, also by @didacrios) follows the #509 Korean precedent: each of the fourSKILL.mdfiles gains one body**Español**:line of intent-specific compounds (no broad standalone words) and a conservative Spanish subset in its frontmatterdescription, which is the surface the router reads;revisar artículostays exclusive to the reviewer andenmendar mi artículomarks revision intent, pinned by two es-ES boundary fixtures undertests/fixtures/issue_133_routing/(11 and 12).academic-pipeline/SKILL.mdis content-locked, so itsCONTENT_LOCKShash inscripts/check_pipeline_boundary_semantics.pyis 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: the05-apa-es-localecitation-check eval case fires the skill 2 of 2 on the branch against 0 of 2 on the previousmain; 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 onrevisa este artículo(0/2 on the previousmain), and 0/2 onredacta un artículo(also 0/2 on the previousmain; the model writes the article in one turn without calling any skill, the bypass tracked in #851 and #857). Two small follow-ups (three broad terms left in the deep-research body line; fixture 11 asking for reviewer comments it does not attach) are #865. Activation layer only; no mode, IRON RULE, schema, or workflow text changes. -
claude plugin evalsuite for theacademic-papercitation-check flow (plugin-evals-citation-check/; surfaces #857 and #858, neither closed). Eight cases (six that should fire, two that should not), run as a with/without-plugin ablation so the headline is the uplift Δ, not a pass rate. The author-defined quality axis is the four citation failures that matter: a reference the user has no source for, the right paper with the wrong authors, a hedged or minor finding cited as an established result, and a retracted or concern-flagged paper cited as live evidence. Every fire case ships a complete synthetic source pack so all four are detectable offline; the sandbox has no network, so "no source" is graded as flagging the entry as unverifiable, never as a real lookup. Secondary axes: mechanical errors, no false positives on clean entries, no claim of online verification, no rewriting of the manuscript. Cases cover APA 7 (en, zh-TW mixed, es), IEEE, Vancouver with the style unnamed, and Chicago notes-bibliography; the negatives are an APA-to-IEEE conversion request and a Python unused-import question. Cases pinmodel: sonnetand the run command sets--judge-model opusso the judge is never the agent model. Calibration against the 2026-09-13 pilots is recorded inplugin-evals-citation-check/README.mdwith the run command, thetimeout_seconds/max_turnsceilings, 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.pyfreeze a hash-verified manuscript corpus,dispatch_calibration_panel.pyanddispatch_e4_panel.pyrun isolated reviewer-panel cards through the shared Claude CLI transport,build_calibration_measurement_row.pyemits contract-validated measurement rows, andscore_calibration_run.pyscores 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 (emptyCLAUDE_CONFIG_DIR, environment allowlist) and reconstructs every assistant message fromstream-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-okgate 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-paperrevision-coach now advertises its rebuttal-shaped triggers where the model can see them (#851). Therevision-coachtrigger 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 frontmatterdescriptioncarried "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, seedocs/SETUP.mdMethod 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-enskill-firedwent 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, andno-fabricationnow 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 fordeep-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-readno longer fails on Windows: the ledger lock has anmsvcrtbackend (#843, PR #844 by @dajiaohuang).scripts/ars_mark_read.pyimported the POSIX-onlyfcntlmodule at load time, so on Windows the documented CLI raisedModuleNotFoundErrorbefore argument parsing and every test intests/test_mark_read_args.pyfailed. The module now importsfcntlwhere available and falls back tomsvcrt; two small helpers (_lock_nonblocking,_unlock) selectfcntl.flock(LOCK_EX | LOCK_NB)on POSIX andmsvcrt.locking(LK_NBLCK, 1)on Windows, inside the unchanged bounded retry loop (Windows contention raisesEACCES, which the loop already retries). Review dropped a proposed empty-file NUL pre-write becausemsvcrt.lockingcan 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 remainingfcntlimports (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
fcntlsites; Windows gets a documentedmsvcrtbackend (#845).scripts/adjudication_activity.pyandscripts/review_criteria_binding.pystill imported POSIX-onlyfcntlat load time (so their CLIs and test modules failed on Windows before parsing arguments),scripts/inquiry_branch_ledger.pycarried its own try/except, andscripts/ars_mark_read.pycarried the #844 backend split inline. Newscripts/file_lock.pyowns the backend choice (BACKEND):acquire(fd, exclusive=, timeout=)/release(fd)overfcntl.flockon POSIX andmsvcrt.lockingon byte 0 on Windows, never writing the lock file, with contention on either backend surfacing as oneLockTimeout(aBlockingIOErrorcarryingEAGAIN). The POSIX lock sequences are unchanged; the one POSIX-visible difference is textual: the adjudication store's contention message now embedsLockTimeoutwhere it embeddedBlockingIOError(the error codeERROR:LOCKand 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 atWINDOWS_BLOCKING_WAIT_SECONDS(30 s) on Windows, surfacing asBindingError; the inquiry branch ledger alpha keeps refusing non-POSIX hosts (it now checksfile_lock.BACKEND) because its durable writes have no Windows verification. Two release paths that unlocked an unacquired lock infinally(a no-op underflock, anEACCESundermsvcrtthat would have masked the real error) now acquire in their owntryblock and release only after a successful acquire, so aLockTimeoutraised inside the guarded body is also never reported as the outer lock failing.scripts/test_file_lock.py(CI manifest id845-shared-file-lock) covers both backends: the realfcntlbackend for contention, bounded and blocking waits, shared/exclusive interplay, andtests/fake_msvcrt.py(one model of the documented_lockingcontract) for the Windows branch, plus a subprocess test that imports all four consumers withfcntlblocked and exercises each site's Windows decision.docs/SETUP.md/SETUP.zh-TW.mdstate 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 thatPOSITIONING.mdand the mentor agent carry (the directions are the user's own, so this was ranking and preselection, not generation). F6 andsocratic_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 thefull-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.pygains 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 inshared/cross_model_verification.mdsenttemperature: 0.1to/v1/responses; the official Astra migration guide liststemperature,top_p, andtop_logprobsas 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 withCROSS-MODEL-ERROR: invalid_astra_reasoning_effortbeforecurlruns, an unset effort still omits the field so the provider default applies, and ids without a table row stay pass-through. A hermetic test inscripts/test_cross_model_verification_guards.pyexecutes both shipped builders (the smoke script and the documented Bash example located by content) against a fakecurland 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=ultrainstead of forwarding it (#824). v3.21.2 addedultratoACCEPTED_REASONING_EFFORTSinscripts/cross_model_codex_transport.pyas an additional reasoning amount. The codex-cli 0.153.4 app-server schema (v2/TurnStartParams.json) defineseffort: "ultra"as the replacement for the deprecatedmultiAgentMode— "proactive multi-agent behavior" — which is outside the transport's single-reference, no-other-agents contract.ultrais removed from the set, and a newvalidate_reasoning_effortstep rejects it withREASONING_EFFORT_REQUIRES_DELEGATIONbefore transport detection, auth access, temporary-state creation, or app-server launch, on both the wrapper-facingverify_oncepath and the directrun_app_serverpath;maxforwarding 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 thatultrabypasses 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.shlaunched every new audit with a hardcodedcodex exec -m gpt-5.5and its sidecar recorded no model at all, which is why the stale pin could mislabel runs silently. It now pinsgpt-6-astraatxhighfor 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 sidecarmodelblock (shared/contracts/audit/audit_sidecar.schema.json:requested+reasoning_effort, a request, not a provider attestation); the mockcodexinscripts/test_run_codex_audit_e2e.pyasserts the exact model, effort, and fresh-run argv and that the sidecar matches.scripts/claim_audit_pipeline.pydefaultedjudge_modeltogpt-5.5-xhighand 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 /unknownidentity is recorded asunknownand binds the cache key to the run (the same fail-closed shape as an unknownjudge_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.mdandshared/templates/codex_audit_multifile_template.mdstate the same contract; frozen benchmark defaults and historical measurements are untouched.
Changed
-
Locale packs are community-maintained, and a single-owner application can be recorded as provisional (#861, #862).
CONTRIBUTING.mdgains 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 underlocales/<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 coreSKILL.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.mdandREADME.zh-TW.mdgain 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.md1.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.mdtold 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 contractshared/contracts/writer/full.jsonD2, and rule 5 of the M3 temporal iron rule in the writer and bothreport_compiler_agent.mdmirrors 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, bothreport_compiler_agent.mdmirrors,academic-paper/references/writing_quality_check.md, theacademic-paper/SKILL.mdanti-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.mdgains 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 underevals/heldout/unsupported_claim_recovery/with statusNOT_RUN; no measured quality improvement is claimed.
[3.21.2] - 2026-09-06 — Model currency for Claude Fable 5.1 and GPT-6 Astra, checkpoint decision provenance, and CJK title-matching repairs
Added
-
GPT-6 Astra listed as a provisional cross-model verifier; the OpenAI recommendation moves to the current generation (2026-09 model update).
gpt-6-astra(released 2026-09-03) joins the canonical model table inshared/cross_model_verification.mdas 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:VERIFIEDwith one bound source on the Vaswani et al. fixture), which is the precondition for a Promotion Bakeoff, not one. The recommendation moves togpt-6-astraunder the existing #783 policy (recommendation follows generation currency;validatedis earned only by the sealed bakeoff), so the move carries no measurement claim.gpt-5.6-solkeeps 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-previeware unchanged. The id-status allowlist, the quick-setup and codex blocks indocs/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.5on the API route,gpt-5.6-solon 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 gainsultra(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 typesReasoningEffortas 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 provenanceauthority section under the Stage 6 boundary semantics inacademic-pipeline/references/pipeline_state_machine.md, mirrored operationally by a## Checkpoint authority fidelitysection inacademic-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-readscope) remain the enforced layer where they exist.docs/RISK_REGISTER.mdgains R11 indexing the rule, its controls, and the residual gap. Both files are whole-file content-locked pipeline surfaces, so both hash constants inscripts/check_pipeline_boundary_semantics.pyare 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 inshared/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 existingCROSS-MODEL-ERROR: openai_http_<status>transport-failure marker) or as a completed response with no grounding evidence, which the existingNOT_SEARCHEDguard 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. Thecross_model_unavailablerow ofshared/contracts/degradation_registry.jsonis 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 (legacygpt-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 inscripts/dispatch_e4_panel.pyis annotated as measurement identity, not prompt debt. -
Skill-inventory parity lint (#809). New
scripts/check_skill_inventory_parity.pytakes the top-level<name>/SKILL.mddirectories 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.mdSkills Overview table rows, and.claude-plugin/marketplace.jsonplugins[].skills[](./<name>form). It also checks that any "N skills" count claim on the three current-state metadata surfaces (plugin.json/marketplace.jsondescriptions,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 andcheck_version_consistency.pyagree on what a row is, and a row that names a skill but lacks itsvX.Y.Ztoken is reported here rather than silently skipped by the version lint; andcheck_spec_consistency.pynow 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.pyhardcodes the paths;check_version_consistency.pyiterates the CLAUDE.md table), so an unpackaged, unlisted skill passed every inventory lint. Wired intospec-consistency.yml; 60 mutation tests cover each surface in both directions, dangling / mis-targeted / non-symlinkskills/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.mdname 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.mdgains 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.mdresidual 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). Thescripts/cross_model_smoke_test.shheader 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.mdR10 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, fivewrite_scope_guard_*rows) — the register contradicted the CHANGELOG and the registry file in the same tree, at the tag and onmain. 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.mdno 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_paritynames the broken environment instead of blaming the manifest (#801 follow-up). With the manifest file present but pyyaml unimportable,_load_manifestreturnedNone, 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 inrequirements-dev.txt, so only a broken local environment can hit this). The missing-import case is now a distinct_YamlUnavailableErrorand the lint fails naming pyyaml and thepip install -r requirements-dev.txtremedy; regression test pins that the message names the environment, not the manifest shape. Also de-enumerated the hand-maintained dependency parenthetical indocs/SETUP.md/docs/SETUP.zh-TW.mdMethod line ("PyYAML + jsonschema" was already stale against the manifest it mirrors two lines above); both language files move together. -
normalize_cn_titlestrips 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 twohas_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_matchis nothas_cjk-gated — it callsnormalize_cn_titledirectly — so a mark-carrying title with no Han ideograph can change verdict there (《Hamlet》and《Macbeth》no longer matches a pre-mangledHamlet》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_matchrequires 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.pyalready carried a Chinese-awarenormalize_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_similarityhelpers, where.lower()folds case but never width (P U+FF30 never reaches P U+0050) andstring.punctuationcontains 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 asDOI_MISMATCH; and the title-fallback search, which requires ratio and exact-normalized equality and so fell tounresolvable. Both feed the*_unmatchedcontamination signals, so a genuine paper could accumulate k across indexes and be renderedCONTAMINATED-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 promoteshas_cjk/normalize_cn_titleintoscripts/_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 toexact_normalized_titleas an additive third branch, and folds it into_similaritythrough the existingmax. 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-fixexact_normalized_titleand_similarityformulas 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 collapsesER+/ER-andp53/P53because it maps ASCII punctuation to whitespace and lowercases, andexact_normalized_titleORs 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_autolinksoft-imported markdown-it-py — not listed inrequirements-dev.txt— and silently returned when it was absent, so its no-link_opentail 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 cleanmain. 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 bypytest.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 — andrequirements-dev.txtdeclaresmarkdown-it-py>=3.0+linkify-it-py>=2.0.3with 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 intest_renderer_neutralizes_markdown_active_inventory_path(scripts/test_build_submission_packet_manifest.py), which moves to the sameimportorskipidiom (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_inlinenest underinlinetokens, so the pre-existing assertion could only ever catchhtml_block; its strengthened assertions verified under markdown-it-py 2.2.0, 3.0.0, and 4.0.0. The triplicated hostile-row construction intest_evidence_rows.pyis consolidated into one_hostile_rowhelper. Renderer behavior and every renderer-side assertion are unchanged.
[3.21.1] - 2026-08-24 — Bounded workflow substrates, sealed bakeoffs, and transport hardening
Fixed
-
Second markdown-grammar consolidation slice (#794). CA-1 and RR-1 now share one repo-relative path/anchor resolution ladder, using authored fragments as exact generated heading ids rather than re-slugifying them; case-drifted fragments therefore fail consistently.
check_spec_consistency.pynow 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 rejectsuniqueItems(invalid_json_schema, HTTP 400) — the keyword is dropped from the provider-sentMODEL_OUTPUT_SCHEMA; duplicate-source refusal was already enforced fail-closed in the local validator. (3)--disable code_mode_hostsilently 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 asMODEL_RETURNED_NOT_SEARCHED; the host is no longer disabled whilecode_modeitself stays disabled and the forbidden-event scan still fails the receipt on any item type outside the four-type allowlist. Livecross_model_smoke_test_codex.sh: PASS for bothgpt-5.5andgpt-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.pyimplement 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_generalfallback 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 asstale: profile_context_changed; newly introduced authority requirements remain an explicit unmet reuse gate. The #745 matrix registers only deterministic conformance underrq_formation. No manuscript inference, pipeline hook, family-specific shipped profile, default-on change, human usability run, or research-outcome claim ships; behavioral evidence remainsNOT_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 evidenceNOT_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.mdwas standing up a second, unpinned authority for those facts. Fivewrite_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; notimeoutbinary → 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 intohooks/run_guard.sh(plus the README Requirements bullet) checked by the D3 lint, andpinned_bynamingscripts/test_run_guard_launcher.pywhere CI-executable pins exist (the Windows-without-Git-Bash and payload-edge rows honestly carry none). Registry 1.2.0 → 1.3.0;_EXPECTED_MECHANISMSinventory 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-solvalidated 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-solremains 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-searchwebSearchitems are exempt from stream-fatality strictly per the protocol's closedWebSearchActionset ({other,openPage,findInPage} + Responses-API spellings, verified viacodex app-server generate-json-schema; an opened page's URL still can never become a bound source; unknown action shapes stay fail-closed) andDEVELOPER_INSTRUCTIONSnow requires an emptysourcesarray for NOT_FOUND/NOT_SEARCHED — with page-open + unknown-shape mutation tests (51 → 54 transport tests).
Changed
-
data_access_levelre-derived foracademic-paperandacademic-paper-reviewerunder the dirtiest-input rule (#773). #756 re-derived onlyacademic-pipeline's annotation; the other pins froze pre-existing declarations without claiming re-derivation. Applying the same derivation:academic-papermovesredacted→raw(standalone modes ingest ungated user drafts and third-party reviewer comments, andliterature_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), andacademic-paper-reviewermovesverified_only→raw(the standalone/ars-reviewerentry 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'srawis re-affirmed on its inputs (raw user queries + unverified web/database search results), with a ceiling argument as supplement. All fourEXPECTED_LEVELSpins 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 bycheck_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 sharedlinks_toinbound-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.mdSupported Models table + recommendation sentence + Option A example,docs/SETUP.md/docs/SETUP.zh-TW.mdquick-setup blocks,.claude/CLAUDE.mdlive 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 tovalidated.gpt-5.5/gpt-5.5-prostay in the table as validated previous-generation options, withgpt-5.5the designated bakeoff baseline. Gemini 3.1 Pro stays recommended: first-party check (2026-08-19) confirmsgemini-3.1-pro-previewis 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.mdnow records the contributor-submitted OpenAI- and Anthropic-compatible gateway as an option for the ungrounded OpenAI-compatible cross-model path. The directory entry is acknowledgement only, not official support, bundling, endorsement, affiliation, or maintainer verification.
[3.21.0] - 2026-08-18 — ISO/IEC 42001-spirit transparency, verifiability, and feasibility track
Added
-
Lightweight risk register (#759).
docs/RISK_REGISTER.mdis 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 mirrorshared/contracts/capability/stage_capability_matrix.jsonverbatim, and maintainer assertions are explicitly marked as such. New lintscripts/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 forMEASURED/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.pyenforces the exact §3.1 trigger (gate 1: at Stage 2.5 the recordedHIGH-IMPACTtier alone is the registry witness andRANDOM/TOP-UP/NOT-SELECTEDare never eligible, with the plan's five-part basis supplied by the registry or a recorded researcher confirmation; at Stage 4.5 theALLregistry plus the recorded basis is required becauseALLalone 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:proposerenders 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 andbindasserts receipt-vs-surface digest equality; absence of the acceptance, any post-proposal change, or an explicit cancel produces an explicit localnot_checkeddeclination record —consent_absent/consent_invalidated/consent_cancelled— and nothing else). Bound plans validate asclaim-standing-query-plan/1.0or/1.1under the Track A substrate, reuse the declared discovery-adapter roster verbatim, derive the default query with the substrate'sexact_claim_query, refuse explicit falsey decisions rather than defaulting them, and refuse the unauthorizedassisted_then_researcher_approvedplanner mode;bind --outputpersists only underexplicit_local_exportto the consent-derived.query-plan.jsonpath, printing for session-only use otherwise.scripts/check_claim_standing_freshness.pyimplements §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 singleexpected_identityauthority, and any drift yields astaleverdict with a closed reason list, the fixed inspectable-but-not-current presentation rule, and explicitassessed/unassessedbinding 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-checkalso 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; aretrieval_plus_stanceplan 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 recordedconsent_cancelledeven after a stance surface was proposed (never misread as invalidation), the eligibility verdict separateseligible(tier witness) fromdispatchable(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 saysSTANCE 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.mdstates 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.mdgains 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.mdrecords 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.mdmaps 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 lintscripts/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.mdis 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 lintscripts/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 lintscripts/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), falsifiableconformance_pinned_bylints/tests on everyCI_GATED/TESTEDrow, 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 generateddocs/STAGE_CAPABILITY_MATRIX.mdview. 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-pipelinedata_access_levelcorrected toraw+ 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.mddeclaredverified_onlywhile the orchestrator's Stage 1 accepts raw user requests and mid-entry accepts raw existing papers, contradicting the dirtiest-input declaration rule inshared/ground_truth_isolation_pattern.md. The frontmatter now declaresraw(the honest minimal relabel; no per-stage trust-domain split, since the annotation is declarative), thedocs/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 theUser → pipelineintake 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.pyis rewritten as a single-pass checker with anEXPECTED_LEVELSper-skill pin layer: a silent flip back toverified_only, an unregistered new top-level skill, an orphaned pin, or a non-mappingmetadatablock now fails CI with one violation per problem; theacademic-pipeline: rawpin 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 sharedwrite_skillfixture helper moves totests/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.jsondescriptions drop "Production-grade" and "39-agent ensemble" (8 task families areNOT_RUNindocs/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) Theacademic-pipeline/SKILL.mdno-bypass prose ("no--no-blockescape 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 pershared/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, thestate_tracker_agentprerequisite checker, and thepipeline_orchestrator_agenttransfer 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 passportUNVERIFIED— 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) Theshared/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) Theshared/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 withevals/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.mdsits 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 newscripts/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-standardNaN/Infinityconstants included), refuses unlicensed claim language in any plugin/marketplace description field (the capability matrix's effectiveness vocabulary imported fromcheck_stage_capability_matrix.pyso 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 thecheck_agents_mirror_syncMIRRORS roster size. -
Citation-surface version drift + version-consistency invariant 12 (#754, PR #763).
CITATION.cffand thePOSITIONING.mdcitation 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), andscripts/check_version_consistency.pygains 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, itsversiongated through the broad-capture + strict-semver idiom, and itsdate-releasedcompared 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 av-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_RESETflag without co-locating thepassport_as_reset_boundary.mdprotocol reference, trippingscripts/check_passport_reset_contract.pyonmain; the reference is co-located as the lint requires.
[3.20.1] - 2026-08-15 — Contract-honesty hardening and bounded evaluation substrates
Added
-
v3.20 post-release contract-honesty hardening (#734, #735–#740; PR #747). Adds a visible, user-requested exit from non-generating Socratic RQ mode without automatic candidate generation; a hash-bound per-finding E6 disposition sidecar with explicit restore / authorize-with-reason / pause routing; exact-span, raw-byte-bound Claim Registry coverage replay with registered-denominator and unknown semantic-completeness disclosure; required read-scope on every new
/ars-mark-readevent with closed fail-visible resolution; criterion-bound categorical reviewer judgements with live packages fixed atNOT_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.pyconsumes a finalized candidate ledger under aretrieval_plus_stancequery-plan-1.1 consent and produces oneclaim-standing-stance-record/1.0plus itsevidence-row/1.3rows. 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 consentedstance_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 averified_exact_matchevidence row), and any deviation becomes that row'snot_checked/parse_errorwith the raw output retained verbatim; transport failures map tojudge_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_recordis 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.pyrenders 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 leadingSTANCE CLASSIFICATION UNMEASUREDbanner 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 beretrieval_plus_stance: a top-levelstance_plannames the exact stance provider/model, prompt-contract version, and retention disclosure (knownrequires a reference,unknownrequires null); the consent binds its hash; and the authorized content classes extend by exactlyclaim_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-ownedclaim_standing_advisorysurface: 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 mandatorySTANCE CLASSIFICATION UNMEASUREDbanner, 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.pyturns a consent-boundclaim-standing-query-plan/1.0into aclaim-standing-retrieval-input/1.0record 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 honestretention_state: unknownwith 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_failedfor 401/403,rate_limitedfor 429,service_unavailablefor 5xx,malformed_responsefor undecodable bodies,unsupported_queryfor 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). TheretrieveCLI validates the plan and its consent bindings first, refuses to persist any output while the hash-bound consent sayssession_only, accepts only the output path derived from the consent's hash-boundauthorized_output_path(plus a fixed.retrieval-input.jsonsuffix), writes exclusively through the substrate's no-follow fsynced writer, refuses provider redirects (an off-rosterLocationcannot receive the consented query and surfaces asservice_unavailable), maps unreachable services (DNS/connection/TLS failures) toservice_unavailableand plain 4xx request rejections tounsupported_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 asmalformed_response, maps provider publication types conservatively onto the closed status vocabulary (unmapped types stayunknown), and emits a schema-valid retrieval input whoserelevance_assessmentsarray 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 (Codexgpt-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 reserved10.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 expertlabel_guide.md, and a fifth schema freezes the deterministic score-report shape.scripts/validate_claim_standing_stance_assets.pyenforces slot coverage, id/DOI binding, design-slot realization, a heuristic simplified-Chinese screen, and a registration guard — the suite is deliberately NOT insuite_registry.json, and the validator fails if it appears there while the set isseed_unmeasured.scripts/claim_standing_stance_scorer.pyis 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; avalidate-expert-filesubcommand 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_targetfields are construction intent only, never ground truth. No probe implementation, expert label, adjudication, subject run, or baseline row exists; every probe surface remainsSTANCE 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.pynow 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.verifyreplays 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 apair_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).delivertreats the sealed receipt as evidence rather than authority: it replays the complete bundle validation and every semantic gate check — exactly whatverifyruns — against the embedded ledger (a hand-fabricated receipt cannot authorize a deliveryverifywould 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 (mkdiras the atomic desk claim: a new delivery refuses any pre-existing destination, so two racing deliveries cannot both own one desk). Each assignment is claimed by a write-once schema-validated marker (first_round_delivery_marker.schema.json) and closed by a write-once completion marker after publication — an interrupted identical delivery may exact-resume once, but a completed assignment is never re-issued (accepted residual: a crash between publication and the completion marker leaves that assignment resumable with identical bytes for the same judge, affecting no blinding property); the destination is checked before the marker claim so a refused destination never burns an assignment, and a post-publication isolation re-scan remains as defense in depth, so a desk is certified only by a successful exit over exactly one packet. Gate artifacts live in a sibling<run>-assignment-gate/directory, so the runner's exact run-inventory validation stays green (pinned by regression). Twenty tests build a genuine finalized 48-packet bundle and cover pass/idempotence/receipt-conflict, pair and cross-experiment exposure (mutation-verified), coverage, roster, binding drift, loosened private-map modes, tampered-bundle replay, delivery duplication/misdirection/drift, and receipt-schema validity. The gate proves structural exposure constraints only: it cannot authenticate that two handles are distinct people, and judge/adjudicator independence remains a procedural responsibility. Two bounded weaknesses under concurrent operator misuse are documented and accepted (a lost atomic desk claim needs manual marker recovery; concurrent identical resumes can both report success — neither affects exposure, certification, or re-issue guarantees). Unlike the no-call runner, the gate is not yet pinned by the suite's spec-consistency contract lint (schema closedness and the offline claim are covered by tests, not CI AST pins); extending that lint is deliberate follow-up work. No subject, actor, judge, adjudicator, model, or network ran; #659 stays open pending externally recorded sessions, human judges, and the per-mechanism baseline.
[3.20.0] - 2026-08-14 — Evidence-bound review and revision, contained transports, hermetic evaluation substrates
Added
-
Opt-in, process-isolated PDF text/OCR advisory (#512 follow-up; PR #724; safe replacement for the concept proposed in external PR #623). Default calls keep the original sidecar shape, tool version, and structural
PASS / FAIL / UNAVAILABLEdecision; only a caller that selects--classify-contentstarts a fixed child worker over the exact already-hashed bytes and receives an all-or-nothing extension whose verdict scope is explicitlySTRUCTURE_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; onlyTEXT_AVAILABLE / OCR_RECOMMENDED / unavailableadvisory states are emitted. The stdout-only legacy CLI performs no alias precheck, so malformed input paths remain exit-0 structuralUNAVAILABLEdata. 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;samefileerrors 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 withoutfchmodreject 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 private0700staging 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-relativeos.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--outputfails 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 valuerule 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 forsession_onlyand writes once only for hash-boundexplicit_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. The0600intent 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 under0700/0600permissions. 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/0600modes. 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_commitmust 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-IMPACTunder #549 rather than treating RANDOM, TOP-UP, or final-checkALLas 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 boundedauth.jsoninto an ephemeral private home, runs from an empty root under read-only/never with local tools and workspace capabilities disabled, and never accepts a caller-authored prompt or filesystem path. Positive sources are bound to exact structured standalone-search results by item id, result index, and digest; echoed URLs, unrelated queries, missing/malformed searches, multiple finals, forbidden tool events, invalid selectors, and wrong event shapes fail visibly. Closed request/receipt schemas, Bash 3.2 entrypoints, checked-in event fixtures, a fake app-server suite, and CI guards are fully hermetic; the manual live smoke is consent/cost-bound and excluded from CI. The selector is citation-only and does not replace API credentials for DA, reviewer, calibration, re-review, checkpoint, or handoff calls; no model promotion is included. Original approach and product requirement contributed by @dcs-scd in PR #567. -
Prospective revision-claim-drift rubric v2 and subject-context protocol (#679; PR #702). The frozen v1 rubric, held-out set, both historical measurement rows, and every retained 2026-08-07 run artifact remain byte-locked and unchanged. A separately hashed v2 rubric adds a non-control citation-attachment criterion and points to the canonical claim-strength ladder instead of re-copying rung examples; an append-only amendment record explicitly leaves all historical headlines untouched. Future
heldout-measurement/1.1reruns 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,--bareauthentication 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 asnot_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 independentevidence-row/1.2contracts 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 isLLM-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/UNMEASUREDartifact 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 explicitABSTRACT_MISSINGandABSTRACT_EMPTYstates. Read-only consumers compose all rows in the one existingBibliographic Integrity Advisoriessection; 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
86bf0e5c2cedb300d6d1c6428470cdcedfbf97dfpassed 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 theheldout-measurement/1.1row. 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.0core that keeps transported reviewer severity, editorialobligation_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-boundauthor-adjudication/1.0sidecar, with complete triage, decline reasons, exact target subsets, exact registered-claim replacement bytes/rungs, and exact declined-overlap collateral authority; presentation views never alterR<n>transport references or gate arithmetic. Claim surfaces are exact UTF-8 spans whose protected text must equal the referenced ClaimIntentclaim_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.1surface and closedcontent-coverage-advisory/1.0carrier now bind bounded passages from explicit session-held strings to exact #666 authority requirements andstructured_expectations[]pointers after full #667 packet-artifact replay. TheLLM-ADVISORYlayer 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 explicitlynot_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 honestlyUNMEASURED—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_packetevidence entries asDOCUMENTED,NOT_LOCATED,CONFLICTING, orACCEPTANCE_UNVERIFIED; a closed authority or capability gate is instead recorded in explicitAPPLICABILITY_UNRESOLVEDreason 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.0row 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→5claim_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_ethicsanddata_protectionprofiles; 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-byand inverseupdate-torelations, preserves reinstatement/disagreement/degradation, joins HIGH-IMPACT claims by worst tier, compares retraction timing only tosource_acquisition_date, and mechanically recognizes declared legitimate use only when an auditable declaration and retraction-notice citation are both identified. OpenAlex now retainsis_retractedin 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-strictterminal_policies.retractionkey; 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 legacyretraction_checkis 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-coachentrypoint 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-paperpeer 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.pynow rejects raw HTML tags and delimiters inside## Scoring Plan Dissentas[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.mdnow 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, andretraction_checkwithout treating an execution attestation as a clean result. New signals compose in oneprovenance_summary.mdsection and mint no ref-marker token, preserving the existing singleCONTAMINATED-*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 topi/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+ authoritativeevals/heldout/suite_registry.jsondefine the opt-inheldout-measurement/1.0envelope for scored held-out rows — the premise correction from #654 applies:scripts/run_evals.pystill discoversevals/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-writableminimum_for_scoredfield to relax it; a report'ssuite_classis 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 schemaconst: 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_suiteis legal only onmechanical_matchsuites; E4-shaped suites adopt at the cohort roll-up level (a summary row referencingruns/records — per-run shapes emitted bydispatch_e4_panel.pyunchanged). 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 (JSONtrue!=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--allmode, which discovers by the marker key acrossevals/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.mdis 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-phase2canonical fragments, all five seat mirrors regenerated, content lock repinned) andsprint_contract_protocol.mdnow 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 tendeclared-limit/code-not-a-comment/cannot-interruptpinned-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_spansnow 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.pywitness 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.mdgain 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 againstsearch_strategy(the ambient block sets defaults; the Annotated Bibliography's Schema 2search_strategyis 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 Extractiongrammar — one### RR<n>per arithmetic claim with per-procedure numeric fields and explicitunavailablesentinels, or theno_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 withreachability_not_completedrather than approximating), t/F/χ²/z tails via stdlib regularized incomplete beta/gamma with a 1e-9 boundary guard that resolves endpoint grazes torounding_boundary_ambiguous/inequality_unresolvablerather than a float coin-flip, §5.1's both-tails display generated structurally under an unstated tail (F/χ² normalized to their familyupper-tail), and everyunavailablesentinel mapped to its closednot_computablereason deterministically;not_applicablestays 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 thefinding_ref:linkage lines on mismatch receipts.check_phase_conformance.pygains the--extractionstage (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-receiptsidentity gate ([RECEIPT-IDENTITY]: the card's receipt section, read fence-transparently, must equal the injected bytes modulo canonicalfinding_ref:lines — a paraphrased derivation that still passes the receipt grammar is exactly what this gate exists to catch).dispatch_e4_panel.pydispatches the three-call methodology shape under successor evidence contractreviewer-e4/2026-08-06: the extraction call carries one structural retry of the Phase 1 evidence class recorded under its ownextraction_retrieslist; a calculator refusal of a gate-passed extraction is a panel-fatal harness infra fault ([RECOMPUTE-CALCULATOR], exit-2 class, stderr preserved inmethodology.recompute.log), never a conformance abort;methodology.receipts.mdjoins the bundle. Prompt-side, the canonical source gains amethodology-extractionfragment (new dispatcher-visible### Phase 2Emirror in the methodology agent, sync-linted and content-locked like Phase 1/Phase 2) and themethodology-receiptfragment 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 docdocs/design/2026-08-06-610-step5-script-adapter-spec.mdrecords the approved architecture and the measurement design: a separately authorizedscript_adaptersmoke + 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'snot_computablereason 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-truerounding_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-onlyfinding_ref:under identity, LF-pinned write-once receipts, and contract witnesses (RECEIPT-IDENTITY: PASSgate-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
305884bunder the #608 harness (claude-opus-5@ xhigh, isolated per-seat two-phaseclaude -p --baredispatch,--tools ""): six score-eligible records (smoke ms00 r99 + MS00 r2 + MS01 r1/r2 + MS02 r1/r2) inruns/, 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 — inruns/blocked/; provenance valid on all seven,check_e4_promotion.pyPASS 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/4VERIFIEDin both score-eligible MS01 replicates under the mandatory formalAR<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 (predominantlynot_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 Receiptssection — one### AR<n>receipt per attempted recomputation under the four bounded procedures (p_from_test_statistic/grim/grimmer/n_from_df), or a singleno_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 asMISSED. Each receipt carries the spec §4 logical fields as canonicalkey: valuemachine lines with closedprocedure_id/status/ 16-reasonnot_computable_reasonenums, 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 explicitrounding_interval:+nearest_achievable:lines (the SD-01 r3 missing reachability check);n_from_dfverdicts must name theirdf_identity:. Everymismatchlinks to exactly oneW<n>weakness that carries a**Arithmetic Receipt**: AR<n>back-reference;consistent/not_computable/not_applicablereceipts 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 amethodology-receiptfragment thatcheck_reviewer_sprint_prompt_sync.pysplices 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.pygains 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-inlineprose <!--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_shapedconstruction, 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 exactlyAR<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 newcheck_receipt_enum_sync.pydefrift 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.mdgains §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.mdgains §5.1 and the receipt diagnostics. Phase 1, the other four seats, the DA/synthesis grammars, fixtures, anddispatch_e4_panel.pyare 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/4VERIFIEDper 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
112a869under the #608 harness (claude-opus-5@ xhigh, isolated per-seat two-phaseclaude -p --baredispatch,--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) inruns/, two blocked panels (MS01 r2, MS02 r1) recorded — not replaced — inruns/blocked/; provenance valid on all nine with every retry and checker output preserved,check_e4_promotion.pyPASS 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 #610recompute_adjudicationblock, adjudicated per case against the spec's §5 procedure definitions: SD-02n_from_dfand SD-11grimmerVERIFIEDin both replicates; SD-01grimVERIFIEDin r1 butMISCOMPUTEDin r3 (integer-product argument without the required rounding-interval reachability check); SD-03p_from_test_statisticMISCOMPUTEDin 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-levelper_defectdetection is unaffected; class-wide statistical recall 5/5 per replicate (the four recompute rows plus MS02 SD-07reporting_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
b97628funder the #608 harness (claude-opus-5@ xhigh, isolated per-seat two-phaseclaude -p --baredispatch,--tools ""): four score-eligible records (MS00 r1/r2, MS02 r2, smoke r99) inruns/, three blocked panels (MS01 r1/r2, MS02 r1) recorded — not replaced — inruns/blocked/per the corrective-iteration rule, provenance valid on all seven with every retry and checker output preserved.check_e4_promotion.pyPASS 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 withadjudication.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 16commands/ars-*.mdfiles now declare an explicit frontmatternameequal 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 frontmatternamereplaces 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. Newscripts/check_command_frontmatter_name.py(13 mutation tests, manifest-registered) pins the exact canonicalname: <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.orgis 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 throughdoi.org— so the existing four resolvers reduce nearly every Chinese reference tounresolvable, the same state a fabricated one produces. New standalonescripts/chinese_literature_client.pycloses 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-onlydoi.orgcontent 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 mirrorsarxiv_client.py(typedChineseLiteratureUnavailable, per-instancetime.monotonicpacing on two independent host anchors, 429 backoff at the endpoint floor, no retry on 5xx,#331non-expected-200-body guard) with three Chinese-specific departures: an applicability gate so non-Chinese citations areskippedrather thanunmatched; a Chinese-aware exact-title-or-bust matcher, because the shared#431normalizer 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 licensingfalse), 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. Newdeep-research/references/chinese_literature_api_protocol.mdcarries 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: noverification_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
toolplus caller-provided email. Contributed by @ktao732084-arch in PR #600. -
EQUATOR reference gains CARE / STARD 2015 / TRIPOD+AI condensed guidance and a study-design routing sequence (#594).
deep-research/references/equator_reporting_guidelines.mdcarried substance for PRISMA / CONSORT / STROBE / COREQ / SQUIRE while the three study types a clinician-author most often writes first — case reports, diagnostic accuracy studies and prediction models — appeared in the mapping table as names only. Three new condensed sections (§7 CARE, §8 STARD 2015, §9 TRIPOD+AI) follow the file's existing shape: item-level guidance keyed to the official item ids, a citation to the source statement paper, a link to the official checklist, and Clinical Research Application Notes parallel to the existing Higher Education notes. Item wording is paraphrased throughout rather than transcribed — the licensing situation differs per statement, so each section now identifies the exact source artifact, its licence and the nature of this adaptation. A new §10 records the study-design → guideline routing sequence the mapping table previously assumed had already happened, returning a primary reporting guideline plus separately labelled appraisal tools, with ambiguous descriptions remaining unresolved until the scholar clarifies. Reference content only — no agent, schema, mode or file additions. Contributed by @ktao732084-arch in PR #601. -
Deterministic venue-track contract coverage (#615). A test-only, offline contract oracle and 43-test pytest suite now exercise the accepted 15-target disclosure behaviour without adding a runtime schema, live policy lookup, or general submission engine. Coverage includes the existing seven-field/order checker plus deletion and ordering mutations; selector/user-surface drift; complete intake with citation checking and preserved
OTHER / UNCLASSIFIEDuse; unknown, incompatible, prohibited, and uncurated-policy halts; all four disclosure outcomes; anchor-track isolation; distinct purpose-specific multi-placement blocks; conditionalNOT_APPLICABLEchildren; and stricttool × task/run/artifactfact 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.jsonbefore 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.pyre-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>/bundleand canonical installed-raw states and refuses changed, inserted, missing, redirected, ambiguous, inconsistent, or already-consumed evidence.check_e4_promotion.pyindependently 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 ofraw_bundleplus all*_locationfields — 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-27NOT COMPUTABLEcohort 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, witheic_agent,contract_role: eic, serializedEIC/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 remainsNOT COMPUTABLEwithout adjudicated dimension-level gold scores, and every partially annotated dimension reports its ownannotated_n/Nplus 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.mdmoves 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 standalonedisclosurebranch 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 andOTHER / UNCLASSIFIEDpreservation; 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/UNKNOWNstates; 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 intospec-consistency.yml, enforces exactly 15 unique headings, global case-insensitive ordering, and one complete seven-field set per venue; broader behavioural coverage remains tracked in #615. Venue-count/terminology surfaces are synced across the disclosure protocols,academic-paper/SKILL.md,/ars-disclosure, mode-selection guide, ARCHITECTURE, and READMEs ×5, with all four venue applicability outcomes, typed halt status, and the separate policy-anchor render identified. The Chinese Medical Association Publishing House GenAI regulation remains held back: its primary text was verified first-hand on the publisher's official WeChat channel, but no durable official URL exists to cite (session-signed links only; archive capture is captcha-blocked), and this database refuses third-party summaries. Contributed by @ktao732084-arch in PR #599. -
Re-review persuasion-invariance paired controls (#576 Spec B §14, PR-B3 — evals). Final leg of the B1→B2→B3 chain, closing #576. New held-out set
evals/heldout/re_review_persuasion_invariance/— six paired-control scenarios inen+zh-TW(15 arms, 12 pairs, 43(pair, observable, target)cells per language) that hold every Stage 3' input constant except one controlled factor and document, per observable, whether the contract requires the arms to agree or to diverge: P-1 identical revision under strong vs weak Response-Letter rhetoric (verdicts and decision must be identical; the two letters are gated to an equal claim set so rhetorical force is not confounded with claim content); P-2 one identical letter over a substantive vs cosmetic manuscript change; P-3 one and the same new issue asregression/previously_missed/indeterminate(the last withoriginal_manuscriptandapply_reportsabsent), whose sharpest cell is the §8 goalpost guard making the latter two decision-equivalent; P-4 an evidence-backed rebuttal vs an assertion-only one (only the former books avalid_rebuttaladjustment and reachesaddressed_by_rebuttal, and the "deaf" direction is scored as well as the "persuaded" one); P-5 a fix atexpected_change_surfacevs an equivalent fix elsewhere vs a cosmetic edit AT the surface (SD-10: same verdict and different anchor for the first pair, rejection for the third); P-6 a qualifyingresearch_integrityescalation approved vs a non-qualifying methodologicalnew_standardvs the same qualifying exception the user REJECTS, pinning the identical pre-answer emission, the §6.4 closed class set, and the §6 Step-3 floor — and, because §3.1 makes Phase 1 revision-blind while §3.2 puts aNewStandardRecord's origin there, P-6 is the one scenario whose arms carry different Round-1 artifacts rather than a shared packet. Ground truth is documented per scenario with every cell carrying arule_anchorback to the spec clause (#550 convention), and all six maintainer-judgment values are flagged as such in their scenario's ground truth, with the three that are load-bearing for a scored cell (P-3's new-issue severity, P-6'smechanical_decision_impact, and P-4's stated-argument reading) named as such. Scoring is pairwise-consistency at(pair, observable, target)cell granularity, with the honest asymmetry stated:identicalcells are noise-robust because a shared judgment cancels,differscells collapse to absolute correctness on the pair's observables. The set joins the #574 E4 harness per SD-11 (evidence contract, blocked-run separation, replicate discipline reused); E4's 2026-07-27NOT COMPUTABLEcohort result is untouched. Apply reports ship placeholder hash tokens the dispatcher stamps at materialisation, so the §11 apply-chain witness cannot fail against a checked-in constant. Newscripts/check_persuasion_invariance_fixtures.pystructure-only gate (12 invariants incl. the pinned scenario/arm/pair/cell inventory, relation-vs-expected-value agreement, the P-1 claim-set equality, apply reports detected independently of their hash keys and required to declarereport_format_versionplus all three hash keys exactly once each with key-bound placeholder tokens, a pinned count of report-bearing files so a whole block cannot be deleted silently, one-hop pointer resolution, the held-out boundary checking every language's scripted checkpoint answer against every material file, exact packet/arm section sets, and multiset agreement on(arm-pair, observable, target)between eachground_truth.md's Pair-structure table and the index) with a 58-test mutation suite, wired intospec-consistency.ymland the unified pytest manifest. Two limits are stated rather than left to be discovered: invariant 7 pins the declared claim-set arrays, not the letter prose, and invariant 12 compares which cells exist, never what they expect. Cells whose correct value depends on a choice the spec leaves open carryconditional_onand go unscoreable rather than counting as a miss; where an observable can instead be defined TOTALLY over every conformant branch that is preferred, and P-6's arm-levelescalation_path_entryis the worked example: quantified over the whole emission (enterediff ANY Phase-1 record requests escalation or ANY 2A exception exists) because §5.1 permits several standards per item and the exception record carries no item binding at all, and scoped to the entry ATTEMPT rather than its success, since a request that lapses back to advisory still crossed §3.2's boundary. Seed set with no baseline: it ships the fixtures, ground truth, and protocol, and makes no claim about any model's persuasion-invariance until measured. -
Re-review three-gate contract becomes the Stage 3' default (#576 Spec B, PR-B2 — protocol + orchestration). Second leg of the B1→B2→B3 chain: the PR-B1 artifact layer gains its emitters and the contract turns ON.
academic-paper-reviewer/references/re_review_mode_protocol.mdis rewritten around the three-gate orchestration (Phase 1 criteria commitment revision-blind → Phase 2A evidence verdict persuasion-blind → Phase 2B claim matching with typed adjustment records), replacing the read-letter-first Traceability Rule; it now carries the full verdict vocabulary (+CANNOT_VERIFY), the §6 G0–G2/B1–B6/floors decision derivation with the mechanizedp2_addressed_rate, the protocol-level generic P2MADE_WORSEdiscriminator, dissent/new-issue/escalation/routing/input-manifest sections, and theARS_RE_REVIEW_LEGACY=1legacy boundary ([LEGACY-NO-CONTRACT]marker; flagless prerequisite failure → G0-class abort, never silent fallback).pipeline_orchestrator_agent.mdgains the § Stage 3' Re-Review Contract Dispatch section (manifest emission before Phase 1, three sequential validated gates, the three post-2B passes in normative order, MANDATORYcheck_re_review_synthesis.pyinvocation before any outcome surfaces, deferral-loop re-persist/re-check cycle, fail-closed abort surfacing, producer obligations). The seven parallel Stage 4→3' transfer/input surfaces all gain the original (pre-revision) draft (orchestrator handoff row, state-tracker prerequisites, state-machine Normal Flow row + Verified-Paper-Draft matrix qualifier, pipeline SKILL Step-4 enumeration, two_stage_review_protocol, team_collaboration_protocol, reviewer SKILL + protocol input lists), and the four surfaces carrying the old single-reportoutput_draft_hashcheck switch to the §11 ordered-chain rule (incl.revision_patch_protocol.md). New Stage 3'→4.5 handoff row + extended Stage 4/4'→4.5 row forward the sidecar's frozenpreviously_missed/indeterminaterecords;integrity_verification_agent.mdconsumes BOTH attributions as Stage 4.5 gate input; two §8 routing-fixture tests prove the records reach and are ingested on each path. Schema surfaces: Schema 11 gains the sidecar note +CANNOT_VERIFYinstatus+ the legacy-scopedauthors_claimflag consequence; Schema 7consensus_levelgainsSINGLE-VERIFIER; Schema 6judge_recordgains optionalprecommitment_hash/routing_status(oneOf incl. the parameterized unmapped-labels pattern) /apply_chain_witness; theR<n>-follows-must_fix-order ordinal contract is pinned ineditorial_decision_template.md+editorial_synthesizer_agent.md(R side only).reviewer_re_reviewleaves the Schema 13 enum (§5.4) with a rejection regression test; the §7 panel table, reviewer SKILL, andshared/contracts/README.mdpoint at the dedicated family. The degradation registry (1.1.0) registers all 14 #576 mechanisms (MADE-WORSE-UNEVALUABLE, ESCALATION-UNSUBSTANTIATABLE, CHANGE-BASIS-ABSENT, ATTRIBUTION-INDETERMINATE, both CRITERIA-LAYER-ABSENT forms, ROUND1-FINDINGS-ABSENT, COMMITMENT-EVIDENCE-ABSENT, PATCH-BINDING-ABSENT, the three ROUTING-DEGRADED forms, LEGACY-NO-CONTRACT, the apply-chain not-run states) with fixture tests;check_pipeline_boundary_semantics.pyCONTENT_LOCKS re-pinned for the four touched locked files in the same commit. -
Re-review pre-commitment contract artifacts + synthesis checker (#576 Spec B, PR-B1). First leg of the strict B1→B2→B3 implementation chain for the three-gate evidence-before-persuasion re-review contract (design merged as PR #604). Four new schema documents under
shared/contracts/re_review/—precommitment(Phase 1 revision-blind criteria commitment: inherited-criterion binding with derivedR<n>letter ordinals, P2 lighter form, §10-normalizedsource_reviewer_labels,NewStandardRecordwithnew_standard_idadded at implementation sonew_standard_refhas a stable target),verdict_record(Phase 2A persuasion-blind verdicts: CANNOT_VERIFY discriminated union, residual re-grading, dissent records with recomputablecriterion_hash, pending escalation exceptions),traceability(Phase 2B sidecar: typed adjustment chains with closed bases, frozen new-issue copy, deferral-loop records — intents/resolutions/reapplications/rebuttal adjudications/G2(d) acceptances/pending rebuttal upgrades —DecisionInputsoperands, emission-kind-scopeddecision_state), andinput_manifest(present-discriminated §11 unions, taggedpassport:/path:refs, freshness fields). New stdlib-onlyscripts/check_re_review_synthesis.pyrecomputes the whole contract from the primary artifacts (#510 architecture class): JCS-sha256 hash chain, byte-exact criterion/letter binding with the §5.1 contiguity degradation, the apply-chain witness withpatch_digestcontent-bound pairing and the[PATCH-BINDING-ABSENT]pre-1.2 policy, every §13 referential/cardinality invariant, the G0–G2 gates incl. the deferral biconditional, and the B1–B6 + floors decision derivation recomputed both fromDecisionInputsand independently from the raw records — graded exit codes (0 pass; 1synthesis_mismatchincl. all referential failures; 2 schema/manifest invalid with the matching §3.5 reason).scripts/ars_apply_revision_patch.pyreport format bumps 1.1→1.2, addingpatch_digest(full sha256 over the exact patch bytes applied) per the 1.0→1.1 precedent. 181-test mutation suite (scripts/test_check_re_review_synthesis.py): three hand-pinned golden scenarios (Accept, cross-model-active Minor, G2(d) deferred/accepted), one violating fixture per invariant, the §10 card-normalization fixtures pinning both example files' Role lines verbatim (DA vocabulary via synthetic fixtures), a §6 derivation unit table, and jsonschema parity on the positive path; wired into the unified CI pytest manifest. No behavior change: nothing emits these artifacts until PR-B2 turns the contract on as the Stage 3' default. The three-track exact-head review round closed the §3.4 Direction-column enforcement (valid_rebuttal upgrades to FULLY_ADDRESSED only — the sole letter-anchor basis cannot move a verdict sideways; author_pointer_located_evidence is a strict upgrade to PARTIALLY/FULLY), the superseded-reapplication time anchor (a preserved failed attempt is never compared against the post-retry chain tail), the G2(d) acceptance↔adjustment one-to-one join, CrossModelResolution answer-membership, challenged-proposal booking exclusivity, the §11 no-original escalation/attribution degradations, verbatimsource_reviewerbinding, half-transported severity refusal, strict apply-report version/hash grammar, the relative-onlypath:ref grammar, and the §13 aborted-emission exemption scope. -
Reviewer Schema 13.2 role-scoped scoring and canonical decision contract (#574 Spec A). The
fullandmethodology-focussprint contracts move to v2 with per-dimensioneligible_roles/owner_role, mandatory-only paper-blind fatal triggers, a four-token decision enum, and an exhaustive Minor/Major/Reject/Accept condition ladder; full mode adds EIC-owned D6 venue fit and contribution. Phase 2 cards now score only eligible dimensions, use explicit structural/declared abstention, bind warn/block/fatal classifications to Phase 1 triggers, and leave one decision producer: the synthesizer.check_phase_conformance.pymakes role binding, plan grammar, manuscript blindness, trigger binding, dissent/fatality, and Critical/Major anchor checks executable;check_panel_synthesis.pynow applies per-dimension eligible-seat quantifiers (majority n=1 means owner decides), verifiesdimension_verdicts, and enforces machine-addressable DA-CRITICAL adjudication with a terminal Accept escalation marker. The five delivered reviewer prompts gain per-finding band tests, anti-bundling, and singleton-Critical anchoring; decision authority and numeric-threshold residency are single-sourced and mutation-pinned. -
Reviewer Spec-A E4 acceptance attempt recorded as BLOCKED, not scored (#574). A fresh claude-opus-5 xhigh cohort at suite
19bc872launched the two clean-control replicates in parallel under the frozen isolated two-phase dispatch. r1 reached synthesis (editorial_decision=major_revision, one unscored maintainer observation) but is provenance-invalid because its first malformed methodology Phase 1 response was overwritten by the permitted structural retry; the final panel cannot independently prove that response's paper blindness or retry eligibility. r2 is independently provenance-invalid because its first malformed Methodology and Perspective Phase 1 responses were also overwritten by their permitted structural retries; exact checker outputs survive, but the rejected model responses do not. It then stopped fail-loud when the Perspective Phase 2 emitted an empty## Scoring Plan Dissentsection andcheck_phase_conformance.pyreturned[DISSENT-GRAMMAR: dissent section must name dimension_id]. The protocol does not permit a retry for this non-multi-dissent Phase 2 failure, so no DA, synthesis, MS01/MS02 panels, replacement draw, or imputation followed. The cohort has zero score-eligible runs, a provenance-invalid rate of 2/2 (1.00), and a conformance-abort rate of 1/2 (0.50), the latter against the Spec-A diagnostic expectation of approximately zero; the required 2 × 3 fleet, recall means, clean-control mean, severity agreement, DA-critical-versus-Accept monitor, and release gates are all BLOCKED / NOT COMPUTABLE. Both attempts live under blocked record/raw namespaces, preserving available evidence without presenting either as a valid run. -
Reviewer E4 re-measurement on claude-opus-5: fresh two-condition fleet, behavior-batch gate verdicts (#574). The
opusdispatch alias moved from claude-opus-4-8 to claude-opus-5 the day after the E4 baseline, so per the seeded-defect README's re-run-don't-reuse rule BOTH conditions were re-measured on the new model (12 blinded isolated runs: 2 replicates × 3 fixtures × {pre-#581 prompts atf7d9d07via worktree, post-#581 atad81b2e}, frozen dispatch shape reproduced over headless-CLI calls at effort xhigh). Gate verdicts for the #581 behavior batch against the same-model baseline: strict recall PASS and improved (MS01 0.95 → 1.00 with the critical band 0.875 → 1.00 — the SD-01 GRIM achievability recompute, never performed by opus-4-8, fires in both post replicates; MS02 1.00 → 1.00); clean-control false findings PASS and halved (4/2 → 2/1, the baseline's logical-foreclosure / inoculation / recruitment-channel-as-fact fabrications do not recur, and the post r1 run is the only one of twelve whose clean-control decision avoided reject_or_major_revision); severity agreement FAIL on the frozen highest-tagged-seat ladder (0.663 → 0.536) — with the recorded diagnostic decomposition showing DA-only agreement flat-to-up (0.621 → 0.644), letter-fallback cells 4 → 0, and non-DA per-finding tag coverage 0 → 100%, i.e. A3's severity transport is achieved and the frozen metric now exposes cross-seat critical-band inflation that was unmeasurable pre-A3; seat-level band anchoring is the named #574 B1 follow-up residual. All 12 run records + complete raw panel outputs committed underevals/heldout/reviewer_seeded_defects/runs/; README baseline table gains the two same-model rows and marks them (not the opus-4-8 row) as the operative gate baseline. -
Reviewer behavior batch: quota removal + coverage receipts, typed evidence anchors, severity transport, decision symmetry (#574 A1/A2/A3/B1). The four behavior-changing reviewer-prompt reforms gated on the E4 baseline (measured 2026-07-24 at suite
307ef24: MS01 0.90 / MS02 1.00 strict recall, clean-control 0 false findings, severity agreement 0.625 — the batch's A3 half targets exactly that severity gap). (A1) The hard 3-5/2-3 finding quotas are removed from all four scoring seats (EIC + R1/R2/R3) and the peer-review template — findings now follow the evidence in both directions, and a report with an EMPTY Strengths or Weaknesses list must carry aCoverage Receipt(one row per examined dimension with the basis for "no finding"), so no-quota never becomes lazy non-review; the EIC's "still find 2-3 points" accept-caution edge case goes with it. (A2) Every finding carries ONE typed evidence anchor —text/table/figure/equation/dataset/absence— with the vocabulary canonical in the peer-review template: a verbatim quote + page is one type, not the universal requirement; omissions useabsencewithabsence_scope+ the surfaces actually checked; Critical/Major findings MUST carry an adequate, applicable anchor or they are not yet findings; the DA's issue tables trade the untypedLocationcolumn forEvidence Anchor(+Confidenceon CRITICAL/MAJOR). (A3) Schema 6'sseverityenum becomes the declared single source (the E4 severity-source ladder made mandatory): all four scoring seats emit an explicit per-finding Severity + Confidence (1-5) + competence basis, the DA's CRITICAL/MAJOR/MINOR bands map onto the enum with OBSERVATION excluded as a non-defect channel, the Weakness object gains optionalevidence_anchor/confidence/competence_basisfields (additive, legacy cards stay valid), and the synthesizer TRANSPORTS severity/confidence from the cards — never re-derives — marking fallbacks[SEVERITY-SOURCE: letter-fallback]/[CONFIDENCE-SOURCE: report-level]; the decision standards' parallel Serious/Moderate vocabulary is folded into the enum. (B1) Replaces asymmetric strictness with symmetric evidence standards: the EIC's acceptance-rate rigor anchor (Q1 ~10-15%), the synthesizer's "Rare — most papers don't pass", and the standards' "< 5% of submissions" base-rate framings are all removed; a new Decision Symmetry and Register Independence section pins that Accept and Reject carry the same evidence burden, decisions follow venue criteria rather than target distributions, and tone rules govern wording only — severity never moves for politeness or rigor-signaling (the E4 DA band-instability finding gets the matching band-consistency rule). Also closes a #578 P0-3 residue: the skill-level "no duplicate criticisms" quality standard becomes independent-overlap-is-corroboration. Two new fail-closed lints pin all of it:scripts/check_reviewer_data_fences.py(the PR #578<paper_content>+<phase1_output>fences verbatim across all five panel agents + the sprint-contract delivery side + the cross-model Reviewer 2 transport) andscripts/check_reviewer_finding_contract.py(quota regressions, receipt surfaces, anchor vocabulary — with Schema 6's machine-facinganchor_typeenum and both DA inline spellings pinned to the sameANCHOR_TYPEStuple as the template rows, so the prose layer and the schema layer cannot silently desync — severity single-source witnesses, base-rate anchors), 96 mutation/inverse tests, both CI-wired via spec-consistency + the unified pytest manifest. Seventeen cross-model review rounds (finding trajectory 12→7→9→8→6→5→7→9→6→4→4→2→2→2→3→2→0, the final round returning an explicit zero-findings verdict — the convergence signal; three documented rejections carry compatibility bridges instead of removals) closed structural gaps the first cut missed: round 2's load-bearing find is that sprint runs inject ONLY the### Phase 2 — Paper-visible reviewsubsection as the reviewer's system prompt, so the Finding Contract now lives INSIDE that delivered subsection on all five seats (a file-level placement would never reachreviewer_full/methodology_focuscalls — the structural explanation for the baseline's missing per-seat severity tags), with both lints parent-binding the subsection under## v3.6.2 Sprint Contract Protocol(a decoy heading elsewhere no longer passes); the #215 ungroundable-field-norm disposition became canonical-enum Minor +[FIELD-NORM UNVERIFIED](no off-enum "advisory" tier); the remaining directional priors (synthesizer divergence-weighting edge case, decision-template "conservative principle" rationale) and mandatory-strength mirrors (guided-mode opener, review-quality balance check) went direction-neutral/genuine-merit-conditional and are lint-pinned. Round 3 extended the contract to strength anchors inside the delivered block (A2 covers both polarities), unbounded the revision-response skeleton (W1..Wn— a W6 finding can no longer lose its author response), made the perspective seat's assumption-audit gate and the summary's polarity mentions conditional, re-phrased the "most authors overstate generalizability" heuristic as a criterion-only scope check, taught the shared section extractor CommonMark tilde fences, scoped the DA table check to## Output Format, and widened the quota grammar again (worded ranges,at minimum,N+,not fewer than); the DA's no-consecutive-concessions ladder was deliberately KEPT (v3.0 anti-sycophancy: consecutive concessions are themselves accommodation-bias evidence) with an explicit B1-compatibility bridge — pressure-time procedure, never first-pass severity. Round 4 declared the statistical red-flag HIGH/MEDIUM/LOW labels detection TRIAGE (never a competing severity vocabulary — enum severity is assigned by decision impact, not copied), routed a lone Major recommendation through One-Outlier arbitration before it can escalate the decision, gave Schema 6strengthsa structured{description, evidence_anchor}form so strength anchors survive serialization, made the last affirm-first mirrors (hypercriticism guidance, guided-mode summary, response template's Strengths-Acknowledged block) genuine-merit-conditional, required full-line CommonMark fence closers in the shared section extractor (a~~~not-a-closeline no longer closes a block), and taught the quota grammar noun-first heading forms ("Weaknesses (at least 3)") plus per-mirror unbounded-response validation. Round 5 — by then finding only lint-mechanics, the convergence signal on the prompt layer — capped fence indentation at CommonMark's three spaces, bound the transmission witnesses to the ACTIVE delivery clauses (the protocol's numbered Phase-2 step; the Reviewer Track's When-active block), scoped the Schema 6 witnesses to the Schema 6 section (a row migrated into a neighbouring schema now fails), extended the quota grammar to imperative direct counts and maximum forms (both contradict no-minimum/no-maximum), and validated each response mirror separately with a widened bounded-range reject; both lint docstrings now state the threat model explicitly — accidental drift by well-intentioned edits, with adversarial repo editors out of scope since anyone who can craft a pathological decoy can equally edit the lint (the terminal defense there is review of lint-file diffs). Round 6 (down to one P1) pinned sub-claim severity INHERITANCE in the synthesizer's decomposition (all sub-claims of one parent share its transported severity — a differing severity means a different parent, never re-rating; the Step 1b example now shows two bundles), re-worded the EIC's weak-paper edge case to prioritize-never-truncate (independent evidence-backed defects all stay listed; only cascading symptoms of a listed root cause fold in), and fixed two realistic false-POSITIVE paths in the quota grammar (a non-finding "Questions (2-4 items)" count and "between major and minor issues" prose no longer fire; "up to N findings" maximums now do) — with inverse tests asserting the legitimate forms PASS. Round 7 reached four surfaces the earlier rounds had not opened: the cross-model DA's "find the 3 most serious weaknesses" prompt lost its fixed count (A1 reaches the #523 transport path too); the synthesizer's EMITTED package now carries the transported metadata (Severity columns on both roadmap tables, typed anchors in the Top Blocking cells on both mirror surfaces — the Step 1b working inventory is no longer where A2/A3 fields go to die); the DA-CRITICAL "cannot be Accept" iron rule became visible ADJUDICATION on all three surfaces (a validated or genuinely unresolved DA-CRITICAL still blocks Accept, but an EIC-adjudicated-and-rejected one is recorded with rationale rather than vetoing — an unvalidated negative claim carries the same evidence burden as a positive one); the DA's delivered Phase 2 contract is pinned verbatim (a weakened modality can no longer hide behind stronger undelivered sections); the three advertised examples were regenerated in the current finding format; and the quota grammar tolerates modifier words between count and noun whilepointsleft the finding-noun set (scoring language must not false-fire). Round 8 completed the A2/A3 emit chain end-to-end: EVERY roadmap row (both tables, synthesizer + decision template in lockstep) now carries transported Severity + typed Evidence Anchor + per-finding Confidence — not only the ≤3 Top Blocking rows — and Schema 7'sRoadmapItemgains the same three optional fields for machine consumers (withtypeexplicitly re-scoped as revision magnitude, distinct from finding severity); the last qualitative base-rate cue ("uncommon in practice") left the Accept scenario; the regenerated examples were corrected where they under-demonstrated the contract (explicit per-finding Severity fields, input cards carrying the severities the inventory transports, arbitration reading the per-finding Confidence rather than the report-level score); and the quota grammar gained limit/cap constructions, while the range pattern's precision-over-recall tradeoff (descriptive prose could false-fire on curated prompt surfaces; a visible CI failure beats a silent quota regression) is now documented in place rather than half-fixed. Round 9 finished the machine-transport tail: Schema 7 gainedseverity_source/confidence_source(an enum value alone cannot carry a fallback tag, so provenance gets its own fields), the lint pins all five Schema 7 transported-field rows scoped to the Schema 7 section, the DA's MINOR rows carry Confidence (a MINOR issue that becomes a Suggested Revision transports it like any other), the cross-model DA prompt block passes the full quota grammar (not just the retired literal), the between-range pattern tolerates modifiers, and both example files' Revision Roadmaps carry the transported metadata end-to-end. Round 10 — the first zero-P1 round — tightened four advisory residues: text-type strength anchors keep their short verbatim quote (prompt-compliant strengths can no longer be schema-invalid), the field analyst's low-maturity configuration makes developmental feedback a REGISTER while the recommendation stays evidence-based, the quota grammar catches top-N selection counts ("report the top 3 weaknesses"), and the Schema 7 witnesses pin transport semantics rather than row existence. Round 11 (also zero-P1) cleared the last advisory residues: selection-verb direct counts ("Select three weaknesses") joined the quota grammar, the sub-claim decomposition example's roadmap items and the remaining example text-anchors now conform to the transport contract end-to-end, and every sample severity cell in the decision template reads [transported] so no row invites re-derivation. Round 12 (zero-P1 again, two advisory residues) finished the tail: the template's last hard-coded severity sample became a transported placeholder, and the sub-claim example's omission finding carries a properabsenceanchor from the input card through the roadmap. Round 13 (zero-P1, two advisory residues) split the one example roadmap row that merged two findings with heterogeneous metadata and made the fence mutation-test helper wrap-tolerant like the checker it exercises. Round 14 (zero-P1, two advisory residues) gave Schema 6's ReviewerReport an explicitreviewer_confidence(1-5) field — the legacy fallback's target, deliberately distinct from the 0-100 editorialconfidence_score— and made the decomposition example's input cards schema-representable (anchors + competence bases, plus an atomicity note framing the bundle itself as the residual failure mode decomposition guards against). Round 15 (zero-P1, three advisory residues) completed the contract's edge semantics: ReviewerReport gained a conditionalcoverage_receiptobject so a reviewed-empty list survives serialization distinct from a thin review; the Minor Issues block is now an explicitly declared NON-finding channel (copyedit-level, below the finding threshold, merged into Priority 3 assource_kind: "editorial"items); and RoadmapItem gainedcorroborating_sources+source_kindso multi-source consolidation drops nothing and no-source items are legitimate rather than legacy-ambiguous. Round 16 (zero-P1, two advisory residues) addedcompetence_basisto Schema 7's driving and corroborating source shapes and put full typed anchors into the decomposition example's working inventory. The rounds also closed fail-open classes in the lints themselves (fence witnesses now scoped to the DELIVERED### Phase 2 — Paper-visible reviewsubsection / protocol / Reviewer-Track blocks, since a fence relocated out of the injected subsection is delivered nowhere; DA column checks parse the header row the shared footnote can no longer mask; quota patterns generalized beyond the retired literals; the Schema 6confidencerow pinned) and a family of kept-sentence contradictions the batch's new rules had left standing: the DA's CRITICAL band re-anchored to the canonical decision-impact bar (a fixable acceptance-blocker is Critical on every seat), the decision standards' cross-dimension table reframed as decision-impact-only (no second severity-assignment path, no off-enum values), unresolved-split handling made direction-neutral in both the synthesizer and the standards, the mandatory-merit lines ("they always exist", "for fairness") replaced with genuine-merits-only rules, the Coverage Receipt made polarity-aware (it states whether it covers Strengths, Weaknesses, or both), Minor findings' anchors made mandatory (section-level locator suffices), Schema 6'squote/absence_scope/check_performedmade type-conditionally REQUIRED, the SKILL.md "fake diversity" anti-pattern row reframed as overlap-suppression (P0-3), and the #506 calibration leniency prior explicitly bridged as a measurement-reading prior that never shades a decision (B1). Post-change E4 re-measurement per the set's README is the acceptance step for the next release. -
E4 baseline measurement recorded for the reviewer seeded-defect set (#574). First operative baseline on the v0.1 fixtures, executed under the protocol's now-frozen dispatch shape: per manuscript, a blinded neutral-filename copy outside the repo, a field-analyst call, then per seat a PHYSICALLY SEPARATE paper-blind Phase 1 call (contract + title/field/word_count only, manuscript reads forbidden —
sprint_contract_protocol.md§2's load-bearing mechanism executed literally) and a paper-visible Phase 2 call, then the synthesizer over the five cards, with the protocol's §§4-5 structural lints enforced at dispatch (claude-opus-4-8, reasoning effort xhigh; 2 replicates × 3 manuscripts; maintainer-delegate adjudication under the README's frozen severity-source ladder). Isolation provenance, stated precisely: the manifests-opened-only-after-all-runs clause held for the superseded first attempt; by the operative rerun the ORCHESTRATING context had already adjudicated that attempt and so carried ground truth — contamination isolation for the operative runs therefore rests on the dispatch fence, not orchestrator ignorance: every reviewer/synthesis agent received only the neutral-named manuscript path, the reviewer skill files, the contract, and prior-phase outputs as delimited data, with allevals/reads forbidden and no defect-related vocabulary in any prompt (the same fence every post-change run will rely on, since any post-baseline orchestrator is manifest-aware by construction — now recorded in the README protocol). Results at suite commit307ef24: MS01 strict recall 0.90 in both replicates (critical band 0.75 — the sole non-detection in every MS01 run is SD-01, the GRIM-impossible mean: panels recompute t-vs-p and df-vs-N unprompted but never run the achievability check, cleanly isolating the A4 recompute gap to the granularity class); MS02 strict recall 1.00 in both replicates (critical band 1.00; both panels explicitly name the absent interview protocol); clean-control false findings 0 in both replicates (every asserted weakness verified as a true-absence observation or hedged advice; decisions were Minor Revision and "Major Revision gated on citation verification", the latter driven entirely by the excluded-by-design synthetic-DOI class — recorded as a v0.2 fixture-design consideration, not a measurand); severity agreement 0.625 mean (0.500–0.722 across runs) — recall is stable while the losses split between DA band placement (the dominant share: the same defects swing a full tier across replicates and seats) and the letter-fallback rung (three 0.5-losses where NO detecting seat carried a tag, so the proxy governed) — both halves are the A3 severity-transport gap, one as band instability, one as missing per-seat tags. Two protocol events occurred and were recovered per protocol, both recorded for the operational monitors: a PANEL-SHRUNK abort (DA multi-dissent, §5 retry-from-Phase-1) and a voided-and-retried synthesis (§8.1 duplicate emission pair; the voided output is preserved under runs/raw/voided/ and the retried synthesis reproduces the same fired set and decision). Measurement provenance: the first same-day attempt ran each panel as a single in-context simulation; the cross-model review track showed its "paper-blind" Phase-1 plans pre-registering manuscript-specific facts (physical call separation is unachievable in one context), so it was superseded and fully re-run — the attempt is preserved underruns/superseded/2026-07-24-in-context-dispatch/with near-identical headline numbers (MS01 0.90 same sole miss; clean-control 0; severity 0.599 vs 0.625), evidence the leak was not inflating recall. The dual-track trajectory (security three passes, all clean; cross-model ultra iterated to convergence: 2P1+2P2 → 3P1 → the dispatch-validity P1s that triggered the user-approved re-run → adjudication/grammar P1s closed by a mapping correction and the §8.1 synthesis retry → a final numeric-consistency pass) also drove the committed raw-output evidence base (runs/raw/, verbatim panel outputs making every verdict re-adjudicable), the frozen severity-source ladder, and the frozen dispatch-shape clause in the README protocol. The #574 behavior batch (A1/A2/A3/B1) is now unblocked, with A3 (severity transport) and A4 (recompute) carrying the clearest baseline-measured headroom. -
Reviewer seeded-defect held-out set v0.1 (#574 E4). The acceptance instrument for reviewer-prompt changes:
evals/heldout/reviewer_seeded_defects/ships two synthetic defective manuscripts with ground-truthed manifests — MS01 (quantitative educational-technology study, 10 planted defects: GRIM-inconsistent mean, df/N contradiction, p-vs-statistic mismatch, causal overclaim from a cross-sectional design, citation-claim reversal, random-vs-convenience sampling contradiction, absent ethics/consent statement, subgroup-sum mismatch, abstract-vs-results value drift, unbounded generalization) and MS02 (qualitative/mixed higher-education-policy study, 9 planted defects incl. undescribed instrument, unoperationalized construct, n=12→sector generalization, abstract/methods count contradiction, suppressed disconfirming voices, re-identifiable "anonymized" participants, significance claim with no test, citation-claim mismatch, unqualified first-comprehensive claim) — plus MS00, a deliberately sound clean control for the false-positive side. Every manifest row carries a verbatim-uniqueanchor_quote, closed defect-class/severity/detector enums, and the arithmetic defects are independently re-computed in their descriptions. The README pins the measurement protocol (fresh session per manuscript, manifests are contamination — never in review context; maintainer-adjudicated DETECTED/PARTIAL/MISSED per defect; recall + clean-control false findings + severity agreement) and the honest scope line: a directional smoke tier, not FNR/FPR calibration (the #574-rescoped E5 decision).scripts/check_seeded_defect_fixtures.py(6 invariants incl. an expected-fixture inventory pin with duplicate-id rejection and an exact per-fixture defect-ID pin, 21 mutation tests, CI-wired into spec-consistency + the unified pytest manifest) gates fixture integrity only — the behavioral baseline is measured manually per the README and recorded in its baseline table BEFORE the #574 behavior batch (A1/A2/A3/B1) may land. -
THIRD_PARTY.md "Upstream & related projects" section (#573). Acknowledges Yila-AI/sci-ssci-skills by @MissOrangePeel as the origin of the mechanism shape adapted in the #569/#570 revision-round claim-drift guards (PR #571), reciprocating their README listing of ARS under "Projects using or adapting this work".
-
revision_claim_drift post-guard re-measurement — first row under the #654 envelope (#652). The 2026-07-22 pre-guard baseline (2/8 claim-strength/hedge drift) finally gets its post-guard counterpart, in the issue's preferred two-arm concurrent design: 8 items × {unguarded, guarded} × 2 replicates in one session window on a frozen
claude-fable-5headless-CLI subject (neutral cwd; context probed free of guard content), where the guarded arm carries a guard block condensed from the shippeddraft_writer_agentrevision-mode ladder section (rules 1-3 near-verbatim + ladder scale; shipped rule 4's roster mechanism replaced by a token-conservation line - the row measures this prompt, not the shipped pipeline path). Result: unguarded 7/16 = 0.4375 vs guarded 1/16 = 0.0625 item-replicate drift; deterministic unauthorized numeric/citation deltas 4/16 unguarded (including the rp-06 citation consolidation the pressure aims at and a derived "24%" restating HR 0.76) vs 0/16 guarded; controls clean in both arms. The guard did not eliminate drift — the guarded residual (rp-03-G-r2) restated a null as an affirmative "showed no relation" (absence-of-evidence → evidence-of-absence), and the unguarded arm ran hotter than the 2026-07-22 row (4/8 items vs 2/8; descriptive temporal comparison only). Measurement discipline:heldout-measurement/1.0envelope validated by the #654 checker (its first real-row engagement), adjudication rubric v1.0 committed + hashed before any judge output existed, legacy-comparability judge (codexgpt-5.6-solxhigh, blind to arms/controls via seed-652 anonymization,judge_plan.exception: legacy_comparabilityper the contract), blinded fresh-context adjudication resolving 10 flagged runs (any verdict boolean true) to 8 confirmed drifted (both rp-01 runs rejected on the rubric's C3/C4, mirroring the baseline), raw subject/judge/adjudication outputs + superseded first-sample attempts retained underruns/2026-08-07/(the run's two apparent call failures were later found to be in-flight reads - two agreeing samples each, corrected in RUN_NOTES items 9-11 along with a control-scoped criterion citation and the operator-attested status of the same-window claim). Judged rate remains a lower bound conditional on judge recall (rubric C7).
Changed
- #569/#570 guard language tightened from "closes the residual" to "a guard now watches it" (#652, do-now half). The v3.19.0 release notes (CHANGELOG + README en/zh-TW +
.claude/CLAUDE.md) described the revision-round claim-drift guards as closing the epistemic and token halves of the #390 honest-claim residual. The guards shipped after the only measurement row inevals/heldout/revision_claim_drift/(the 2026-07-22 pre-guard 2/8 baseline), and no post-guard row exists yet, so the honest statement is that the failure mode is measured and a guard now watches for it — not that the guards reduce it. All four surfaces now say exactly that and point at the #652 post-guard re-measurement. Wording only; no mechanism, schema, or gate behavior changes.
Fixed
-
Conformance machine lines tolerate Markdown decoration; Severity self-correction is escalation-only (#637). The 2026-08-03 #610 baseline attempt aborted 3/6 panels on decoration alone: every synthesis first attempt (6/6) fenced the four mechanical audit lines, one retry re-emitted them as whole-line inline code spans, and one domain seat self-superseded its Severity declaration mid-card — all three shapes render the content verbatim and visibly, so by the #609 reader-visibility criterion these are false aborts, not leak guards.
check_panel_synthesis.pygainsaudit_candidate_lines: the four mechanical audit-line grammars now also read fenced content (fence-marker lines dropped, the same open/close state machine asstrip_fences) and the unwrapped bodies of whole-line inline code spans, normalized to their CommonMark-DISPLAYED form (padded-span one-space strip, indented-fence dedent); rejection rationales and the DA marker keep the plain-line source as a declared, test-pinned boundary._one_bodycollapses same-value re-statements while two distinct parsed values stay a loud abort, so a disagreeing decoy can never be silently absorbed.check_phase_conformance.pyaccepts a multi-declaration Severity chain only when it strictly escalates (Minor < Major < Critical — the observed self-correction direction, ms01_quant r1's Major -> Critical) with a[SEVERITY-SUPERSEDED]advisory trail in the gate log; de-escalation (which could waive the Critical/Major Evidence-Anchor hard gate with one appended weaker line), non-monotone or repeated-value chains, and same-line pipe pairs all keep the abort. Dual-track review (security opus + codex gpt-5.6-sol xhigh) surfaced 8 advisory findings, all first-party verified and closed; round 2 converged clean on both tracks, including a differential proof that the post-fix abort set is a strict superset of the pre-fix one. 16 new pinning tests cover fence-state decoys after malformed/short/mismatched/Unicode-separator closers, CRLF, the operative-value anchor pin, and the padded-span conflict abort. -
Monthly harness-retirement scope and debt wording no longer drift (#617). The scheduled generator now checks out the repository and renders its issue body from
scripts/ars_phase_scope_manifest.json, so the Bucket A inventory, per-skill counts, and agent names stay aligned with the authoritative manifest (currently 23 agents, includingtimeline_extraction_agent). The generated debt categories are model-agnostic, and regression tests cover manifest growth, deterministic CLI output, and removal of the stale inline workflow list. -
E4 dispatch harness: the evidence contract becomes structural (#608).
reviewer-e4/2026-07-27requires every checker-rejected response and its checker output to survive a retry, and the 2026-07-27 Spec-A fleet lost that on both launched panels (observed_provenance_invalid_rate: 1.00, gate verdictNOT_COMPUTABLE) because a retry wrote over the response it was retrying. That is not an operator mistake to correct by trying harder: the preservation step sat at the moment the operator was trying to get the run to proceed. Newscripts/dispatch_e4_panel.pyinverts the order — a response is written to an attempt-numbered path withO_EXCLbefore any checker may judge it, so preservation precedes the decision to retry instead of depending on it, and each checker invocation's own bytes are stored beside the response it judged. Checkers run from inside the bundle with relative paths, so nothing has to be stripped and every stored diagnostic isverbatim. Paper-blind and paper-visible calls now get separate whitelisted sandboxes and the blind one does not contain the manuscript at all, making blindness a filesystem fact rather than the seat's restraint (hand dispatch put every artifact in one directory);evals/is outside both, and the contamination fence is a PATH allowlist rather than a word denylist: the harness may read only the contract, the seven agent files, and the three manuscripts, so a manifest is unreadable and a future held-out artifact is unreadable by default. A word denylist was written first and measured to be worse than the failure it guarded against —manifestandseededare ordinary review vocabulary and 5 of the 18 committed real panels of this set contain one, so gating assembled prompts would have aborted roughly a quarter of panels after all five cards existed, with no replacement draw permitted, which is a false-abort channel of the exact kind #609 was raised to remove. Ground-truth tokens in model output are now an advisoryleak_canary_hitsfield, never a gate. The seat set is derived from the contract withpanel_sizeasserted rather than hard-coded a third time; only a reviewer-conformance exit is retried, because §11 routes every exit-2 class to abort-no-retry and retrying one would file aphase1_retriesevent the evidence contract does not classify as a retry; and the one permitted Phase 2 recovery is recognised from the checker's own[PROTOCOL-VIOLATION: multi_dissent=true]line, now pinned by the checker's tests so a reword fails CI instead of silently killing a fleet. The four closed status fields are derived, withprovenance_statuscomputed by re-checking that each named location still resolves rather than by trusting the write path. The work directory mirrors the set's own tree —runs/<stem>.jsonbesideruns/raw/<stem>/, or the blocked namespace for an aborted panel — with every*_locationalready record-relative, so promoting a run is a copy and nothing is rewritten at commit time; that rewrite is what previously turned a verbatim diagnostic into a paraphrase. A completed panel carriesadjudication.status: "pending"because the harness cannot fillper_defectwithout the held-out manifest and does not guess. A work directory inside the repository is refused with nothing written, since writing a record there is the act being refused, and an internal preservation fault produces a blocked record rather than a traceback: losing the record is the one failure mode this mechanism cannot afford. Cross-model review then found four more places where the harness was not yet dispatching the frozen shape or not yet durable, all fixed here: the field-analysis call sent a one-line placeholder instead offield_analyst_agent.md; the synthesizer received only its sprint-contract arithmetic subsection, which stops above its Editorial Decision Letter and Revision Roadmap instructions, so a panel could be score-eligible with neither artifact while the arithmetic checker still passed; a synthesis-layer failure (checker exit 1) aborted instead of being voided and re-run once with the diagnostics as delimited data, as §8.1 requires, so ordinary stochastic formatting blocked valid panels; and a transport timeout or missing binary escaped as a traceback with no record at all, while a nonzero exit truncated stderr to 400 characters inside a field stampedverbatim. A no-response event now writes its exact bytes and blocks the run without filing a retry event, which is what the contract says a re-dispatch after no response is.--dateand--fixtureare validated before they become path components (one separator relocated the evidence bundle, filed a blocked run under the scored namespace, or lost the record entirely);read_prompt_materialrefuses a path that reaches its target through a link, since an allowlist over names would otherwise authorize whatever the name points at; a record'sdiagnosticfield is stripped of absolute local paths before a blocked record is committed to a public repo; andseats_forruns inside the handler so contract drift leaves a blocked record rather than a traceback. Two further review rounds found nine more, in the same two classes. What the panel was asked: the five Phase 2 calls never received the Phase 0 reviewer configuration cards, so a generic seat was dispatched where full mode defines a configured identity (the cards go to Phase 2 only, being paper-derived); the one permitted Phase 1 retry re-sent the identical prompt, and since eachclaude -pis a fresh conversation it could not learn the lint gap §4 requires be hinted; and the CLI was launched without--bare, so it auto-discovered the maintainer's user-level CLAUDE.md, hooks, plugins and auto-memory -- context the allowlist never authorized, reaching a paper-blind prompt -- while the record already attested "no session persistence" (--barerequiresANTHROPIC_API_KEY, an operational cost taken so the fence is true). What the record attests:suite_commitwas read AFTER a tens-of-minutes panel and ignored a dirty tree, so it could name a commit the dispatched bytes never came from; it is now read before dispatch and paired withsuite_commit_reproducible. Durability: repeating a blocked panel into the same work directory madeemit's bundle rename raiseENOTEMPTY, losing the record entirely while the traceback's exit 1 reads as this harness'sEXIT_BLOCKED-- a fleet driver would file a blocked panel that has no record; relocation is now non-destructive and non-fatal and bothemitcall sites are wrapped. Honesty of attestation:diagnostic_formis derived from whether the text was rewritten instead of being stampedverbatimunconditionally, and the path scrubber's general strip-anything-path-shaped pass was removed after it was measured to corrupt ordinary diagnostics (title/field/word_countbecametitleword_count, a DOI URL lost its host). And the symlink refusal added one round earlier was itself a false-abort: comparingresolve()againstabsolute()refused every set root under a symlinked ancestor, which on darwin means every/tmppath and every pytest tmpdir, with a message blaming the manuscript; it is now scoped to components at or below the declared root, and its pin was rebuilt because the original passed on darwin with the fix removed. A third round then caught the previous round's own fix converting a loud crash into a silent one: making the bundle relocation non-fatal meant a repeated panel identity overwrote the first attempt's record and left it stampedprovenance_status: validwhile pointing at the first attempt's bundle -- a retry destroying the account of what it replaced, which is the failure this harness exists to eliminate, one layer above where it was fixed. A repeat is now refused before anything moves or is written, the location prefix is computed from where the bundle actually is, the record is writtenO_EXCLlike the artifacts, andlocations_resolve_fromis enforced inemitrather than only in tests so a prefix mistake downgrades the attestation. The indirection refusal was extended from the set root to every allowlist family from its own base's parent (agent files are now sent whole, so a redirected one would deliver its entire target into a prompt, and amanuscripts/ -> manifests/directory link would otherwise BE the base and never be walked). Absolute paths are removed at the source rather than by a scrubber, since dropping the over-broad pattern had re-opened exposure for work directories under neitherREPOnor$HOME-- the README's own/tmp/...shape. And a fail-closed preflight refuses before dispatch when neitherANTHROPIC_API_KEYnor anapiKeyHelperis visible, since--barereads neither OAuth nor the keychain and discovering that as a transport abort costs the first call of a fleet. A fourth round found the two deliverables no checker looks at.check_panel_synthesis.pyvalidates the audit lines and the arithmetic and exits 0 on a synthesis carrying no Editorial Decision Letter and no Revision Roadmap, and nothing validates the field analyst at all -- the harness's own clean-path fixture was the placeholder stringfield analysisand stayed score-eligible, which is a demonstration that an unconfigured panel could be measured. Both are now required by the harness, since it is the only place an absent deliverable can be caught, and the fixtures carry the real sections. The synthesis call is paper-visible again, as the committed 2026-07-25 artifact records it: a blind synthesizer cannot check a disputed reviewer claim against the paper, which is most of what arbitration is. A nonzero exit or a timeout that nonetheless produced output now preserves that output as apartial-responseartifact and says so, because the contract's no-response carve-out applies only when there is no response. The auth preflight passes--settingswhen anapiKeyHelperis the credential path, which it previously accepted without ever supplying. And the run's own work directory and set root joinREPOand$HOMEas scrubbed prefixes, exact rather than heuristic, since the README's own/tmp/...shape is under neither. One more, found by walking §2 step by step rather than by waiting for a reviewer: step 1 requires the contract be prepared and validated before dispatch, and the harness was sending the on-disk template verbatim. The template carries nogenerated_at; the committed 2026-07-27 bundle's contract does, because the real dispatch populated it, so the seats were receiving a different contract from the one the frozen shape sends -- and step 1's abort-on-error gate was absent entirely, so a malformed contract would have reached all five seats unchecked.prepare_contractstampsgenerated_at(--generated-at, defaulting to now in UTC) andvalidate_contractrunscheck_sprint_contract.pyon the prepared object, refusing before the first call. A fifth round found five more, three of them created by the fixes above. Every seat was receiving all five reviewer configuration cards rather than its own, which SKILL.md Iron Rule #2 forbids and whose anti-pattern table names overlap suppression as "unexecutable under blindness" -- shipping the panel's angles to each seat is what would make it executable, and a suppressed finding is a MISSED in strict recall, so the fix for one instance would have depressed the metric the fleet exists to produce; each seat now gets only its own card, and a seat with none is told so rather than handed its peers'. The contract validator staged its file inside the work directory and ran before the auth preflight, so a refusal printing "nothing was written" wrote anyway, poisoned the emptiness precondition, and turned the documented fix-and-retry into a blocked record for a panel that never dispatched, consuming the panel identity; it now stages in a scratch directory after the preflight. The synthesis call passeddiagnosticsinto themanuscriptparameter, so every first attempt received<paper_content>None</paper_content>and every retry received the checker transcript as the paper -- caught only because a reviewer read the call site, since the existing test exercised the builder rather than the wiring. Instructions and data are now sent as the system and user halves §2 names rather than concatenated into one user message; a malformed contract leaves a blocked record instead of an uncaught exit; the<reviewer_configuration>block carries the Iron Rule #7 data-boundary sentence its two siblings already had; and a filesystem-root work directory can no longer reduce a scrub prefix to/and delete every slash in a diagnostic. A sixth round found six more. A seat that follows its own instruction -- on two or more dissents, abort with[PROTOCOL-VIOLATION: multi_dissent=true]INSTEAD of drafting a card -- failed the checker at parse_report, whose output then never carries the token, so a compliant seat lost the one §5 recovery; the token-only shape (strictly one non-blank line, so it cannot be smuggled inside a real card) is now recognised from the response itself. Card #4's slice ran to end-of-analysis and swallowed## Review Strategy Recommendations, whose reviewer-complementarity notes are panel-wide Iron Rule #2 material; a card now ends at the next card or the next section heading. A field analysis missing any of the four required cards (the analyst's own quality gate) dispatched a generic seat while staying score-eligible; it now aborts at field analysis, and only the DA -- seat five, cardless by design -- gets the no-card notice. The field-analysis and synthesis calls gained the Iron Rule #7 data-boundary sentence their agent files do not carry (under--bare --system-promptnothing else would say it). A[DELIVERABLE-MISSING]diagnostic is now written to the artifact the record names as authoritative rather than pointing at a model response or an already-PASS gate log. And per §6's independent cycles, one seat's conformance failure no longer stops the remaining seats: every seat runs, every attempt is preserved, and the panel then aborts with the first failure -- a transport fault stays panel-fatal.check_phase_conformance.pygains--phase1-only(mutually exclusive with--phase2, mirroring the existing--synthesis/--layer1-onlysplit) because the retry decision is taken while Phase 2 has not been requested yet, and a reimplemented gate would not be the checker's own output. A seventh round — codex r6 plus a closing security pass that drove the whole contract on the scripted transport and swept every assembled prompt — found six more. The deliverable gate was a literal##-substring test and failed in both directions: the committed synthesizer output varies its heading level (# Part 1: Editorial Decision Letterin two acceptance-cohort panels) and sometimes drops the "Part N: " wrapper, so 3 of the 18 committed real panels would have aborted AFTER the full panel had burned with no replacement draw permitted — the #609 false-abort channel reopened at the synthesis step — while a synthesis that merely mentioned the heading in prose, or emitted it empty, or inside a fence, passed. Sections are now resolved as real nonblank heading sections via the repo's sharedheading_section(H1-H3, "Part N: " wrapper optional); the one 2026-07-24 panel titling its letter# Editorial Decisionstays outside the gate as a documented accepted miss, because accepting that bare name would also accept a seat's own## Editorial Decisionsection pasted into a synthesis. The Phase 1 retry hint was delivered as user content where §4 says "hinted in the system prompt" — role placement is part of the registered condition — and now rides the system half. A precondition refusal into a work directory holding an interrupted attempt's bundle reopened that bundle, appended the new abort to its journal, and renamed the whole directory — stale responses included — under the new blocked stem with the record attestingprovenance_status: validover evidence it never produced; an existing bundle is now refused untouched, with no record written, because a duplicate refusal record costs nothing and the earlier evidence has no other copy. A mid-edit contract template escapedmainas aJSONDecodeErrortraceback with no record (its exit 1 reads asEXIT_BLOCKEDto a fleet driver); it is now a precondition failure with a blocked record. Agent files were read lazily per call, so a checkout change mid-panel could deliver different bytes to later seats while the record still named the pre-dispatchsuite_commitas reproducible; the seven files are snapshotted at builder construction. Andcard_forscanned the whole analysis for the firstCard #Nmention, so a card number named in an earlier inconsistency note would hand that seat the surrounding prose as its configuration with the missing-cards gate none the wiser; only the cards section is searched now, found by the same H1-H3 variants the deliverable gate accepts, so a variant analysis cannot pass the gate and then dispatch a cardless panel. A seat that decorates the multi-dissent token with prose or a fence still loses the §5 recovery — pinned as a documented accepted miss, since the strictly-one-line shape is what keeps the token from being smuggled out of a real card. The set README gains the harness bundle-directory layout in §6 and a comparability rule: harness cohorts change the dispatched condition (--bare, per-seat cards, split system/user halves, separate sandboxes, stamped contract), so they are never compared against the hand-dispatched rows and both conditions re-run under the harness. An eighth round — codex r7 in parallel with a second closing security pass — then caught the seventh round's own deliverable fix repeating the class it fixed, plus five more. The heading-variant rule judged emptiness byheading_section's same-or-higher-level body, and the committed synthesizer output organises a letter's content as sibling H2 sections as often as child H3 ones, so two real panels (one in the operative 2026-07-25 acceptance cohort) read as deliverable-less — the net false-abort count was unchanged at 3/18 with two panels swapped, and no test guarded them. The gate now measures the deliverable's INTERVAL — from its heading to the next required-deliverable heading — via a position-aware_heading_lineswhose fence rules are copied fromheading_sectionand pinned to it by test; measured against all 18 committed panels the one abort left is the documented accepted miss, and the interval rule also closes two shapes the seventh-round spelling passed (an empty letter borrowing a contentful roadmap's body, in either heading-level direction). Builder construction had ALSO moved the agent-file read outsidedispatch_panel's handler, so a symlinked or missing agent file escaped as a traceback after the bundle was on disk — no record, with exit 1 reading asEXIT_BLOCKED; construction is back inside the handler and an unreadable prompt file is aPreconditionFailure. The synthesis boundary sentence now covers every delimited block, because a manuscript directive can be echoed into a reviewer card or the field analysis and the synthesizer's agent file carries no untrusted-material rule; the Phase 1 metadata envelope — whose title is quoted from the manuscript's H1 — gains the same data-only sentence. Aclaudeexit 0 with empty stdout is a transport event rather than a conformance failure that consumes the one permitted Phase 1 retry. And git provenance failure (outside a worktree both commands fail whilestatusprints nothing) no longer reads as a clean tree at commit "unknown": unknown provenance is declared dirty, sosuite_commit_reproduciblecannot be true for a commit that does not exist. Two shapes are documented as accepted misses rather than gated: a field analysis whose four cards are bare heading shells (validating card substance across the committed card shapes — inline single-line cards and heading cards — risks the false-abort class this round just removed twice, the analyst's own quality gate owns card content, and all 19 committed analyses carry substantive cards), and a deliverable heading hidden inside an HTML comment (_heading_linesmirrorsheading_sectionin tracking fences but not comments; the comment-visibility channel is #613's scope). A ninth round — codex r8 in parallel with a third closing security pass (which confirmed the interval rule at 1/18 aborts, both prior regressions closed, and found nothing blocking) — added five more. The CLI's own built-in tools are now denied per call (--disallowedTools):--barecuts customization and--strict-mcp-configcuts MCP, but neither disables Read/Bash/WebSearch, and the checkout is public, so a paper-blind seat could otherwise retrieve a manuscript's held-out siblings mid-call with no tool-use audit trail in a text response — the seats' task is pure text and needs no tool; a NEW tool name shipping in a later CLI is a declared residual. §6's independent cycles are confined to reviewer-conformance failures: a checker infra exit (2) or crash (1) inside the seat loop now aborts the round immediately instead of re-running the same global fault once per seat. A mid-panel checkout change downgradessuite_commit_reproducible(the checkers and their imports load from REPO afresh at each gate, so the pre-dispatch snapshot alone could attest a reproducible commit for a run that mixed checker versions). A regular file passed as--work-diris a stated precondition refusal instead of aNotADirectoryErrortraceback with no record. And--datemust be a real calendar day in canonical ISO form — the shape regex alone passed2026-02-31, which would have consumed a full panel and been committed as invalid provenance. One more shape is documented as an accepted miss: heading lines count as interval content (excluding them would reject the committed pattern that writes the decision in a heading line, a real-corpus shape, to close one with zero corpus occurrences). A tenth round — codex r9 in parallel with a fourth closing security pass — caught the ninth round's own tool fix being the wrong polarity, plus five more. The security pass measured the--disallowedToolsdeny list incomplete against the installed CLI the day it was written (13 built-in names beyond its 14) and found its completeness sentence to be a false attestation; the CLI's own whitelist spelling--tools ""now does the shutoff — under an emptied whitelist a tool added by a later CLI is closed by default, the property a deny list can never have, and the argument that replaced this harness's word denylist with a path allowlist — with the deny list riding behind as depth only. The auth path stopped smuggling context past--bare:--settingsloads the whole file it names, and the user's own settings may carryenv, hooks and plugin configuration, so theapiKeyHelpernow travels alone in a staged one-key file, and nothing is passed at all whenANTHROPIC_API_KEYis set. A synthesis whose checker passes but whose Editorial Decision Letter or Revision Roadmap is absent is voided and re-run once per §8.1 instead of aborting a completed panel on an ordinary stochastic omission after all twelve calls had burned. An external--set-rootno longer reads as reproducible provenance (its manuscript bytes are not covered by the repo commit). A sandbox-creation or initial-write failure — read-only parent, full storage, two processes racing on one work directory — is a stated setup result instead of a traceback whose exit 1 reads asEXIT_BLOCKEDwith no record. Andstem_forapplies the same calendar-day validation as the CLI, so the library-level guard is not weaker than the entry point's. An eleventh round — codex r10 in parallel with the fifth closing security pass — added seven more, three behavioral and four hygiene. The hard-coded Phase 1 metadata field mislabeled the MS02 quality-assurance manuscript as educational technology for every paper-blind call, biasing that fixture's measurement before any seat saw its card; the field now follows the fixture (the ms00 value matches the committed 2026-07-27 bundle's metadata).card_for's catch-all line regex minted a marker from prose like "covered by Card #3", truncating the current card and letting the named card resolve to the mention line itself; only heading lines open a card now, measured against all 26 committed field analyses (every card in the corpus opens with a heading, every cross-mention sits in prose). A transport timeout's summary no longer serializesstr(TimeoutExpired)— the whole argv, system prompt and staged paths included — into a log meant for public commit. Hygiene from the security pass: theapiKeyHelperstaging happens once per transport instead of once per call (a six-panel fleet would have strewn ~96 copies of the helper command across the temp tree); an EMPTY leftoverbundle/no longer blocks the refusal record (it holds no evidence to protect, and the message sent the operator to move aside evidence that did not exist);_bare_auth_availablereturns an actual bool; and the README names the pre-fleet single-panel smoke test for the never-live-called--tools ""and--bare/xhigh/thinking interactions. A twelfth round — codex r11 in parallel with the sixth closing security pass (which measured the heading-anchored card slicing against the full corpus: 110 slices, zero deltas, confirming the old regex had never actually truncated a committed card) — added two corrections and pinned one rebuttal. A shrunk panel now aborts with §6's own[PANEL-SHRUNK: usable=<k>, panel_size=<N>]cardinality marker ahead of the first seat's diagnostic, so the operational monitor can count these rounds. The one console refusal assembled from a raw exception is scrubbed (an OSError spells out absolute paths; console-only, never a committed record). A claimed missing §8.1 terminal marker was REFUTED by measurement and pinned: the synthesis checker's exit-1 output opens with its own[PANEL-SYNTHESIS-MISMATCH...]line and the abort diagnostic is exactly that line. The corpus-size figure in the card-slicing rationale is corrected from 30 to 26 (three independent counts agree; the conclusion it supported was re-verified on all 26). A thirteenth round — codex r12 in parallel with the seventh closing security pass — added six more. The security pass caught a stray.coveragebuild artifact (a SQLite file carrying the operator's absolute paths) that a bulkgit addhad swept into the amended commit; it is removed and ignored (.coverage,.coverage.*,htmlcov/). It also proved the path scrubber's resolved-only prefixes never matched an OSError's caller-spelled paths — on darwin/tmpresolves to/private/tmp, so a setup failure under the README's own--work-dir /tmp/...example put an absolute path into a committed record, refuting the README's no-absolute-path sentence;RUN_ROOTSnow registers both spellings (absolute only, since a short relative spelling would corrupt ordinary prose as a substring). Sandbox inputs are exclusive-create, so two panels accidentally given the same empty work directory can no longer silently cross-contaminate — the loser's writes previously overwrote the winner's contract, metadata and manuscript before the bundle's O_EXCL collision, letting gates check Phase 1 against the other fixture's paper. A mid-panel OSError (a gate-log write, a checker launch) leaves a blocked[IO-FAULT: ...]record instead of escaping as a traceback. An abort diagnostic now exists byte-for-byte in the artifact its record names authoritative: the panel-shrunk composite goes to its ownpanel-shrunk.log, and the transport response-status suffix is composed before the log is written. And--replicateis validated up front (1-99, instem_fortoo): a nonpositive value minted normal-looking run ids, and a long one raised ENAMETOOLONG only inemitafter the full panel had burned. A fourteenth round — codex r13 in parallel with the eighth closing security pass (which verified the diagnostic-artifact equivalence across all twelve abort shapes and the/tmpscrub end-to-end) — added four more. A[PHASE1-MANUSCRIPT-LEAK]verdict is never granted the Phase 1 retry: the leak shares exit 3 with the structural lints, so the harness retried it and a clean second attempt made the contaminated panel score-eligible, although the checker's own comment names blindness "the half a retry must not be granted in spite of". The Phase 2 card wrapper no longer forbids what the card exists to do: it said the card "may not alter your identity" while the card IS where full mode's reviewer identity comes from, so an obedient seat would refuse its own configuration and the harness would measure generic reviewers — identity adoption is now authorized explicitly while Phase 1 commitments, scoring procedure and output format stay fenced. Allowlist keys bind to the declared LEXICAL paths, never resolved ones: resolving a manuscript name that is itself a symlink to a held-out manifest inserted the manifest's real path as an allowed key, and a read via the target's own spelling then passed both membership and the symlink walk. Andmain's precondition stage catches OSError too —validate_contractstages in a temp directory, and a full TMPDIR escaped as a traceback with no record while the work directory could be on a different, writable filesystem (_git_stateis guarded the same way, and the temp root joins the scrub prefixes). A fifteenth round — codex r14 in parallel with the ninth closing security pass — hardened the last second-order failure paths. The post-panel provenance re-probe is guarded (a git spawn failure AFTER a completed, expensive panel raised before_emit_or_explainand cost the record; the initial probe already tolerated the same OSError). Every abort handler's own journal and artifact writes are best-effort — an exception raised inside anexceptblock is not routed to a later sibling, so a journal device failing mid-abort escaped WITH the blocked result it was recording; a missing artifact is caught bylocations_resolve_from, which downgrades the attestation instead of losing the record. A setup failure after the bundle was created returns that bundle rather than None, so the refusal path no longer mistakes this invocation's own half-written bundle for an earlier attempt's evidence.emitrefuses a repeated identity across BOTH record namespaces — a successful run repeated produced a blocked record beside the normal one under the identical identity. And the two OSError-derived diagnostics declare their rewrite honestly: they were scrubbed before composition, soscrubfound nothing left to change and stampedverbatimon strings whose paths were in fact removed;PanelAbortednow carries an explicit form set at composition. A sixteenth round — codex r15 in parallel with the tenth closing security pass — closed the race and custody residue. The work directory is claimed atomically (.claimed, O_EXCL) before anything else: two processes racing past the emptiness check both built state in one directory, and the loser could consume the run identity with a blocked record, leaving the winner's finished panel unable to emit — the loser of the claim now dies having written nothing. A setup failure journals its diagnostic so the record's authoritative artifact actually holds the line, and a bundle that merely OPENED a directory already holding content is never handed back by the setup handler (the stale branch keeps custody — the same F3 class, reopened by the previous round's own fix and caught by the security pass). The stagedapiKeyHelpercopy is removed at process exit. One provenance bound is declared rather than detected, in the README: an edit made during a gate and reverted before the end probe is not caught — prompt material is snapshotted and immune, the checkers load per gate, so the checkout must not move while a panel runs. A seventeenth round — codex r16 in parallel with the eleventh closing security pass — polished the remaining edges. A claim-time OSError (unwritable parent, full disk) is a stated precondition refusal instead of an uncaught exit 1. Record locations are serialized with POSIX separators regardless of platform, so a record produced on Windows still resolves when committed and read on POSIX. A settings file whose valid JSON has a non-object top level carries no helper instead of crashing the auth preflight. The README'snormalizeddefinition now describes the scrubber's actual (and intended) semantics — registered run roots are removed wherever they appear in a harness-assembled diagnostic, since an OSError spells its path mid-sentence, not only leading. And the claim-refusal wording credits an earlier or concurrent invocation rather than always another one. An eighteenth round — codex r17 in parallel with the twelfth closing security pass (which delivered the final residual inventory) — closed the last escape hatches. The blindness check runs BEFORE structural parsing under--phase1-only: a response both malformed and carrying a manuscript shingle reported only the grammar failure, so the dispatcher granted the retry a proven leak must never receive.locations_resolve_fromcoverssynthesis_retries(the bundle-side check already covered every retry event; the record-side predicate now matches). Undecodable UTF-8 in prompt material or in the settings file is a stated refusal, not aUnicodeDecodeErrortraceback — it is a ValueError, caught by neither precondition handler on its own. And transport construction (which re-reads settings and stages the helper file, after.claimedexists) routes its failures through the record path. A nineteenth round — codex r18 in parallel with the thirteenth closing security pass (which proved the checker-order asymmetry unreachable: a leaking seat's Phase 2 is never dispatched) — rejected the structurally ambiguous shapes. A synthesis carrying TWO letters or two roadmaps was score-eligible on the first interval; a duplicate is now rejected like an absence, riding the same §8.1 rerun. A duplicated configuration-card number letcard_forhand the seat the first copy while synthesis received the whole conflicting analysis; the panel now aborts at field analysis naming the duplicated card. And the transport-construction handler declares its diagnostic rewrite (normalized) like its two sibling handlers instead of stampingverbatimon a pre-scrubbed string. A twentieth round — codex r19 in parallel with the fourteenth closing security pass — held the measured condition steady on three more edges and pinned one rebuttal. An unexpected Card #5 (six superseded-namespace analyses emit one spontaneously; none of the 18 scored panels do) never reaches the DA, whose seat is cardless by design. The work directory is made absolute at entry: a relative--work-dirleft the sandbox relative, and the subprocess cd's INTO the sandbox before the CLI re-resolves the same relative--add-dirfrom its new cwd. The helper value is read in one defensive snapshot, since a settings change between the availability probe and the second read raised KeyError past the transport-construction handler. And the claimed late-provenance routing gap was REFUTED by measurement and pinned: the reachable shape (an artifact vanishing before emission) is routed to the blocked namespaces by the bundle-side re-check that runs before the destination is chosen — the record-relative predicate downstream is a defensive invariant over emit's own arithmetic. A twenty-first round — codex r20 in parallel with the fifteenth closing security pass — tightened the last preflight edge and pinned two boundaries. A truthy non-string or whitespace-onlyapiKeyHelperno longer passes the preflight and stages an unusable credential (which would have converted an operator precondition into a dispatched blocked run — the exact cost the preflight exists to prevent). A claimed final-artifact completeness gap was resolved AGAINST wideningprovenance_status: the README scopes that closed two-value field to retry-evidence completeness alone and states verbatim thatvaliddoes not attest panel completeness — an accepted artifact deleted by an outside force after dispatch is the maintainer's commit-time concern, with the raw bundle beside the record; the scoping is pinned by a test quoting the contract. And the Card #5 guard's rationale comment counts the corpus correctly (six superseded-namespace analyses, none scored). A twenty-second round — codex r21 in parallel with the sixteenth closing security pass — sealed the delimiter boundary. Every untrusted block travels through one_delimitedhelper that refuses text carrying its own closing delimiter — a manuscript containing</paper_content>(or a card containing</reviewer_configuration>, and so on for every block) would close the declared data early and let the remainder read as instructions; refusal keeps the dispatched bytes untouched, where an escape scheme would change what the model receives. The retry hint's checker transcript — which rides the system half per §4 and can echo model-controlled text — is now explicitly fenced as checker-output DATA and delimiter-checked like every other block. A symlink-loop work or set root is a stated precondition refusal instead of aRuntimeErrortraceback. And a whitespace-onlyANTHROPIC_API_KEYfails the preflight instead of suppressing a valid helper and failing the first live call. A twenty-third round — codex r22 (down to a single finding) in parallel with the seventeenth closing security pass (which byte-audited the delimiter refactor: exactly one intended line changed across all 32 prompt halves, zero corpus hits for any closer token) — worked the token-only audit classification to its honest resting place — in two steps, the second correcting the first. The first spelling replaced the retry event's diagnostic with the response's token line; the next codex round refuted it against the contract (verbatimis byte-for-byte CHECKER output, and the named gate log did not hold the replacement). The retry event therefore keeps the checker's own line — its eligibility was already machine-readable asstage: phase2_multi_dissent, with the bare token verbatim in the named rejected response — and §11's exhausted marker rides the TERMINAL record only, in a harness-written artifact that is byte-equal to the recorded diagnostic, with the checker's gate log untouched beside it. A twenty-fifth round — codex r24 in parallel with the nineteenth closing security pass — hardened the delimiter refusal to whitespace-variant closers (</paper_content >is as valid an end tag as the exact spelling) and made an operator interrupt durable: Ctrl-C mid-call escaped everything, stranding a.claimeddirectory with a partial bundle and no record, which a rerun then refused — the interrupt now leaves a blocked record with its own byte-equal artifact, every completed attempt already preserved by write-once. A twenty-sixth round — codex r25, again a single finding — restored the restarted Phase 1's own §4 retry: the §5 multi-dissent recovery restarts at Phase 1, and every Phase 1 pass carries one structural retry, but the restart was dispatched with a single-attempt budget, so an ordinary formatting slip on the replacement blocked an otherwise recoverable panel. A twenty-seventh round — codex r26 in parallel with the twenty-first closing security pass — finished the fence equivalences. Closer detection is case-insensitive and covers the self-closing spelling (HTML reads tag names case-insensitively and a model may too). Card discovery and duplicate counting are fence-aware like the deliverable gate, so a fenced template inside the cards section cannot configure a seat from example text. The bare-root scrub pass requires a path boundary (/tmpregistered no longer eats the/tmpinside a sibling/tmp2/file). And a helper that dies between preflight and staging is a loud precondition instead of an empty flag list that would launch--bareuncredentialed and burn the first live call. A twenty-eighth round — the twenty-second closing security pass, with codex r27 returning ZERO findings for the first time — caught the previous round's helper guard sitting behind the tolerant probe's short-circuit, unreachable for the very scenario it named;auth_flagsnow classifies the settings file itself in one read, and reaching it without any usable credential — never configured, deleted, rewritten or broken since the preflight — refuses loudly instead of launching--bareuncredentialed. A twenty-ninth round — codex r28 in parallel with the twenty-third closing security pass — carried the §4 grammar into the checker itself and stopped mutating refused directories.parse_phase1validated only the Scoring Plan, so a response missing its Contract Paraphrase and terminal[CONTRACT-ACKNOWLEDGED]passed--phase1-onlyand the dispatcher could retry — or accept — a protocol-invalid Phase 1; the checker now enforces all three §4 requirements, on both its modes. And the work-directory claim looks before it plants: an accidental--work-dir /tmpgets its refusal record without a stray.claimedfirst, while the post-claim emptiness check stays for the race. A thirtieth round — codex r29 in parallel with the twenty-fourth closing security pass (which replayed the four real dispatch Phase 1 outputs against the stricter grammar: all PASS, zero false aborts) — gave the paraphrase check its floor: a bare heading over one line passed although the contract'sparaphrase_minimum_dimensionsis "all", so the paragraph count is now the machine-checkable lower bound (the real outputs carry exactly one paragraph per dimension, and the test fixture was raised to that valid shape); whether each paragraph is TIED to its dimension stays with the seat's own §4 preflight. A second interrupt window (after dispatch, before the record) was resolved AGAINST further handling, with the security pass's standing measurement: the window is sub-second, the bundle already sits preserved on disk, and re-entering a half-finished emission would add real complexity for a recovery the write-once layout already provides by hand. A thirty-first round — codex r30 in parallel with the twenty-fifth closing security pass — made the transport abort path tell the whole truth. A partial-response write that itself fails is no longer claimed "preserved" (the summary now says it could NOT be), and the transport summary and stderr are scrubbed at the source so the committed log matches the record byte-for-byte — the partial response itself is evidence and is never rewritten. A thirty-second round — codex r31, a single finding — routed SIGTERM into the same durable abort path as Ctrl-C: a fleet runner cancels with SIGTERM, which does not raise KeyboardInterrupt on its own, so the process exited with no blocked record and a stranded.claimedmarker. A thirty-third round — codex r32 — installed the record atomically (staged.tmpplusos.replace; an ENOSPC mid-write left a truncated JSON at the final path that later runs refused, unrecoverable normally) and rerouted a late record-side predicate failure to the blocked namespaces — the third reviewer angle on this seam; the second rename cannot collide because the identity check already covered both namespaces. A thirty-fourth round — codex r33 — completed the §4 grammar (the H2 sequence must be exactly## Contract Paraphrasethen## Scoring Plan, in order and nothing else at H2; presence alone let a reordered or extra-sectioned precommitment pass, and all four real dispatch outputs carry exactly the required sequence) and wrapped the whole claim-to-record span against interrupts: an interrupt outside the dispatch loop — preflight, contract staging, transport setup, emission — now exits with a stated refusal instead of a traceback, everything preserved in place. A thirty-fifth round — codex r33's successor — aligned the Phase 1 envelope with the seat files' own promise (all five state the contract arrives "under## Contract" and the metadata "under## Paper Metadata"; the plain labels were a different envelope from the registered instructions) and made the terminal acknowledgement check fence-aware, so an acknowledgement that exists only inside an unclosed code fence no longer passes. A thirty-sixth round — codex r34's successor — carried the §11 cardinality marker onto the synthesis-stage seat failure (§8.1 classifies that exit 3 as an unusable reviewer; the count is honestlyunknown, the checker's own finding rides in the diagnostic) and scrubbed checker crash tracebacks at therun_checkeroutlet — a crashing checker's stderr spells absolute script and module paths straight into committed gate logs, while normal checker output is relative-path-only so the scrub is a no-op there andverbatimstays honest. A thirty-seventh round — codex r35's successor — closed the two second-order effects of the previous fixes. The acknowledgement check now requires BOTH tails: the raw tail catches output trailing after the marker (a fenced block vanished from the structural view before the tail was computed), the fence-aware tail catches a marker that exists only as fenced code. And the checker-output normalization state travels with the output —run_checkerreturns a third element, carried through the attempt chain intoPanelAbortedandRetryEvent, so a scrubbed crash diagnostic is stampednormalized, neververbatim. A thirty-eighth round — codex r36's successor — closed the last race seam and the last acknowledgement spelling..claimedexisting refuses OUTRIGHT, before the occupied pre-check: an occupied-looking directory may be the owner's live run (sandboxes built, bundle still empty), and skipping the claim check there let a loser claim the owner's empty bundle through the stale branch and consume the identity. And the acknowledgement is compared unstripped bar the line ending — an indented marker renders as a code block, and stripping before comparison let it pass the exact terminal-line requirement. A thirty-ninth round — codex r37's successor — widened the closer refusal to attributed end tags:</paper_content data-x=1>is invalid HTML, but tolerant parsers accept it, and the boundary must not depend on the model being a strict parser — while the word boundary keeps a longer tag name (</paper_contents>) out. A fortieth round — codex r38's successor — bounded the last card at any non-card heading of its own level or higher (a literal##check would let an H3-headed strategy section ride inside an H3-headed Card #4) and gave the exhausted synthesis retry its §11[SYNTHESIS-MISMATCH]terminal marker, riding its own byte-equal artifact with the gate log untouched beside it. A forty-first round — codex r40's successor — closed the last twoverbatimfalse attestations in the abort handlers: the transport arm derived the form from the summary alone (a scrubbed stderr left the named log rewritten under averbatimstamp), and the two preflight failure paths dropped the fact of normalization entirely —validate_contractdiscarded the checker's own form and the OSError arm pre-stripped the path so the record's scrub fallback saw clean text;PreconditionFailurenow carries the form to the record. A forty-second round — codex r41's successor — closed three install-side gaps: the §4 paragraph floor counted six bare### Dnheadings as six paragraphs (a heading now separates paragraphs and never counts as one; all four committed Phase 1 outputs still PASS), the staged record file became the identity lock — one path per stem across BOTH namespaces, re-checked inside the lock — so a concurrent emission of the same identity can no longer leave a scored record beside a blocked one, and a staged-write failure after the raw bundle moved intoruns/raw/now rolls the bundle back so the identity is not consumed by a failure that wrote no record. A forty-third round — codex r42's successor — extended the §4 separator set to the other zero-content lines (thematic breaks, single-line HTML comments); the list is closed by design, not a CommonMark block classifier — a bulleted paraphrase still counts as content, because refusing real prose over formatting is the false-abort channel #609 exists to remove. A forty-fourth round — codex r43's successor — kept the abort classifiers honest on three edges: multi-line HTML comment blocks join the zero-content set via a conservative line-leading-opener state (prose mentioning<!--mid-line still counts), the §11[SYNTHESIS-MISMATCH]marker now requires the retry-attempt checker to have actually spoken (a checker crash on attempt 2 keeps its own infra diagnostic), and the exhausted multi-dissent marker fires only on a conformance exit that survived the one retry — a token-only response over an infra exit on attempt 1 is no longer dressed as exhausted. A forty-fifth round — codex r44's successor — answered the emission-failure recovery question at the documentation layer: the explain message now names the preserved state, and the set README documents the two recoverable post-panel failure states (any staged-write or install failure rolls back with the identity unconsumed; a rollback failure on top leaves the bundle in place, named on the console) — resume-from-bundle re-emission stays a declared bound of this version, not a CLI feature quietly half-built. A forty-sixth round — codex r45's successor — widened the emission rollback guard to everything after the raw rename (an ENOSPC on the record directory used to strand the bundle in the runs tree, exactly as the recovery notes said it would not) and made the diagnostic scrub process its roots longest-first with deduplication (macOS aliases/tmponto/private/tmp, and the shorter spelling processed first corrupted the longer one's occurrences mid-path); a third finding — container-prefixed comment shells like- <!-- … -->satisfying the paragraph floor — is declined under the declared closed-separator-list bound: recognizing comment openers behind arbitrary CommonMark container prefixes is the renderer-rewrite slope both this round and #609 exist to refuse, and §4's TIED judgment stays with the seat's preflight. A forty-seventh round — codex r46's successor — admitted lone list markers (-/*/+with no item text) into the closed separator list (zero parse slope, zero false-abort surface — a marker WITH text still counts), and honored the record contract's MUST at emission: a terminal abort whose named artifact fails to resolve is rewritten once from its own diagnostic (the bytes ARE the diagnostic, so equality holds by construction) before the downgrade fires, and the README now states plainly that the downgrade is the terminal fallback for any unresolvable named location, not an attestation that a retry occurred. A forty-eighth round — codex r47's successor — closed the zero-content axis with an explicit termination bound written into the counter itself: variants beyond the closed list (container-prefixed comments, malformed comments like<!-->, entity/whitespace tricks) are declared out of scope — the variant space is unbounded, the observed base rate in committed panels is zero, and §4's substantive judgment sits with the seat's preflight, not this floor. 259 new cases (239 harness + 20 conformance) run the whole contract on a scripted transport with no model call, reusing the two checkers' own valid fixtures so a card that stops satisfying a real gate stops satisfying the harness tests too. Measurement-side only: no reviewer prompt, contract, template, or fixture changed. -
An empty-but-present dissent section no longer destroys the panel through an unretryable Phase 2 abort (#609).
scripts/check_phase_conformance.pytreated a## Scoring Plan Dissentheading carrying no canonicaldimension_id:line as a hard conformance failure; because Phase 2 permits no retry, that seat's abort took the DA and the synthesizer with it. On the 2026-07-27 Spec-A fleet it fired on 1 of 2 launched panels, an observed conformance-abort rate of 0.50 against the Spec-A diagnostic expectation of approximately zero, so every measurement of the review stage ran through a gate whose completion rate was bounded by how often four or five seats all avoided one formatting slip. The prompt layer was not the cause and was not changed again: both prompt-side preflight rounds (#591, #592) were already ancestors of the suite root the fleet ran on, every seat agent states the omit-the-whole-section rule twice, and the observed Perspective card restated the rule in its own output and still left the heading standing. A section that spells no dissent field at all is now read exactly as an absent section — both inputs mean no dissent — and the run record carries the visible[DISSENT-EMPTY-SECTION: …]diagnostic, including the section's non-blank line count, so the occurrence stays auditable rather than silently absorbed. The tolerance is keyed on field shape, not on canonical spelling, and the shape test is decoration-agnostic by construction: a line is a claimed dissent when the letters preceding its first colon spell exactly a field name, whatever non-letters surround them (NFKC-folded first, so a fullwidth re-spelling cannot present itself as prose), and a claimed dissent aborts unless it is canonical. Enumerating Markdown wrappers instead would leave the next unenumerated one reading as prose — successive cross-model review rounds against a character-class version produced- dimension_id:, then- [ ] dimension_id:and| dimension_id: |, each absorbed as no-dissent; the inverted rule covers those plus link labels, HTML comments, indentation, and hyphen/space spellings without naming any of them. Letters are collected withstr.isalpha, so CJK prose that happens to name a field keeps its own letters and stays prose instead of collapsing onto the field name and aborting a panel it should tolerate (zh-TW, ja, and ko prose shapes are pinned); markup spans carrying letters of their own — HTML tags, reference targets, task-list markers, and link destinations up to one nesting level deep — are dropped before the colon is located rather than after, so an absolute link target's ownhttps:cannot win the partition, and decoration beyond those (a deeper nested destination, a quoted HTML attribute containing>, an entity-encoded colon, exotic custom syntax) is deliberately out of scope, with a test pinning that declared limit so it cannot move by accident, because absorbing it costs one advisory-flagged record while a broader rule costs the false aborts this tolerance exists to remove. The scan runs on the section's raw span, not on the sanitized body, so a field the sanitizers delete (fenced block, HTML comment) or relocate (a field spelled as its own## dimension_id: D1heading, whichsplit_sectionsmoves out of the body entirely) cannot reach the tolerance branch as an empty section; a field line the canonical parse never saw is a dissent the seat cannot be credited with, so it fails whether or not it is canonically spelled, counted by occurrence rather than matched by value so a hidden copy of a canonical field cannot ride in on its twin's identity. That scan also closes a pre-existing hole it sits on top of: becausestrip_fencesleaves HTML comments in place, canonical fields written inside<!-- … -->parsed as a genuine dissent onmainand collected the trigger-binding exemption while the visible card claimed nothing, so a drifting trigger passed; a commented-out field is now struck from the canonical parse and fails as an unparsed occurrence, with the delimiter-opening confined to lines a comment actually covers so a canonicalrationale:merely mentioning<!--in its unrestricted text is not rewritten out of agreement with the canonical parse, while a fence or comment carrying no field stays tolerated. Comment state is resolved by delimiter order rather than by presence: a line that closes one comment and opens another (<!-- an aside --> <!--) leaves the fields below it hidden instead of handing the exemption back, while a balanced aside on a single line hides nothing after it. The closer may reuse the opener's own last two dashes, as CommonMark closes<!-->and<!--->, so ordering the scan does not turn those into unterminated openers that abort a card a presence test had passed; the state machine is pinned line by line in both directions. Only an unfenced, uncommented heading delimits the span — fence state is tracked exactly, mirroringpanel.strip_fencesunder a test that pins the two to agree line-for-line across backtick, tilde, longer-closer, indented, unterminated and CRLF shapes — so a heading written inside a fence or an HTML comment cannot end the span early and hide the fields after it, while a comment opener counts only as a block opener — at most three spaces of indentation, never inside a fence — list and blockquote markers included, since CommonMark opens raw HTML for- <!--exactly as it does for a bare<!--and the bullet form needs no closer at all. Indentation is read as columns, not characters: the outer and inner allowances may not add to four (which would make the line indented code), and a tab advances to the next four-column stop, a distinction that put> \t<!--and- \t<!--on opposite wrong sides while characters were being counted. Two shapes stay out: a marker following text on its own line, and one indented as a lazy paragraph continuation. Both do form a comment, so the miss is stated at full price rather than discounted: it grants a trigger-binding exemption for a dissent the rendered page does not show, which omitting the section would not have granted. It is refused anyway because closing it deterministically means reading a bare<!--inside unrestrictedrationale:text as an opener, which aborts a valid card on a phase that permits no retry; the card that would die is pinned as a test, as is each limit, and the deterministic closure belongs in the reviewer output grammar rather than in this parser, tracked as #613. Paragraph state is tracked, because it decides which markers may open anything, and each of its rules is state-dependent in a way that cost a defect to learn: only an ordered list starting at 1 interrupts an open paragraph; an ATX heading of any level, a thematic break and a lone-end a paragraph from either state; a=run or two-or-more hyphens is a setext underline only when there IS a paragraph to underline, and is ordinary paragraph text otherwise; an empty list item cannot interrupt a paragraph at all, so a lone*,+or2.only holds the state down where none is open. Blankness follows CommonMark rather than Python, counting only spaces and tabs, so a line holding an ideographic space stays a paragraph instead of aborting a valid zh-TW card. The opener pattern is matched against a tab-expanded line with one unambiguous run per gap; the earlier spelling let a single space be claimed by either of two adjacent optional groups, doubling the work per nesting level (0.17 s at 20 levels, 2.7 s at 24, a stall by 30) instead of returning a verdict. Measured rather than asserted, against a real CommonMark render over a grid of 511 opener spellings x 14 block contexts x closed or unclosed comment, deduplicated to 14308 distinct shapes; every count below is relative to that grid. Shapes hidden from a reader but credited, and shapes visible to a reader but refused:main8139 / 0, then 2372 / 337 with no paragraph state at all, 3560 / 61 with paragraph state guessed from mere non-blankness, 3133 / 122 with the setext rule applied unconditionally, 3133 / 0 once each rule was stated per state, and 2889 / 0 once a comment block stopped counting as a paragraph. Read the middle rows rather than the last one: the first interim spelling had no state and its false aborts argue for having some, while each of the next two had state and guessed at it, and every guess cost false aborts until the rules were separated by which state they hold in. The step that cleared the last 122 left the miss count untouched at 3133, with 122 shapes entering the miss set as 122 left it:==and--lines had been refused for the wrong reason, and correcting the model makes them correctly not-a-list-opener, hence correctly credited-though-hidden. Every surviving miss is a declared shape: the delimiter follows text on its own line (a zero-gap marker, or an ordered marker other than 1 under an open paragraph whose later closer forms an inline comment), a lazy paragraph continuation, or an opener inside an already-open list item or block quote where the container shifts the column at which a block starts. Resolving the last two means growing a container parser. Those, and the wider channel this visibility model does not cover at all (raw HTML that is not a comment, such as a<script>or<template>block, whose contents no renderer displays), are pinned by tests and tracked in #613 rather than left implicit. A commented heading still delimits the span, agreeing withsplit_sectionsrather than second-guessing it: disagreeing cost four false aborts across review rounds and bought only a miss that credits the seat nothing, while agreeing stops a comment opened above the heading from laundering the fields below it, not even one repeating a title that exists structurally elsewhere in the card. Only a field-shaped heading inside the span counts — a## Rationale: …section elsewhere in the card is an ordinary extra section the report grammar permits, not a dissent. The diagnostic's line count is taken on the raw span for the same reason: fenced placeholder prose is archived content and would otherwise be reported as zero. One consequence of the same rule closes a pre-existing hole: a canonical dissent plus a decorated second one had been slipping past the multi-dissent cardinality gate. Every other claimed-dissent abort is unchanged and separately pinned: arationale:without itsdimension_id:, adimension_id:without itsrationale:, duplicate ids, a claimed dissent placed after## Dimension Scores, and duplicate## Scoring Plan Dissentheadings. The empty case short-circuits ahead of the section-order check, since an absent section has no position to violate. What the tolerance costs is stated rather than papered over: a seat that narrates a deviation in free prose without spelling any field is read as no dissent. Its Phase 1 trigger binding is still enforced in full — no dissent claimed means no per-dimension exemption, so a deviating trigger still fails at[TRIGGER-DRIFT]— but a deviation whose trigger text stays inside the Phase 1 commitment is recorded only as the advisory line plus the archived raw response. No deterministic rule closes that residual without re-breaking the observed case, which was itself free prose; the same deviation is equally invisible when a seat simply omits the section, so the tolerance does not open a channel that being silent did not already offer. Parser-side only: no reviewer prompt, contract, template, or fixture changed, and scoring, decision, severity, fatality, dissent-cardinality, and retry semantics are untouched, so the change cannot move a review-side metric. 205 new cases (178 → 383), including the verbatim 2026-07-27 placeholder-retraction shape, twenty-four decorated field-shape spellings, three sanitizer-laundered shapes and their multi-dissent variants, every other multi-dissent bypass, nine tolerated shapes carrying a colon in four languages plus field-less fences and comments, and a CLI pass asserting the diagnostic reaches stdout. The coupled Phase 1 canonical-line first-attempt miss rate stays open until #608 preserves the rejected responses needed to characterise it. -
Reviewer E4 retry-evidence contract versioned without weakening older blocked-run safeguards (#574; PR #603). Only the 2026-07-27 requirement to preserve every checker-rejected response followed by a retry and its checker output has a prospective boundary; a disclosed transport/session re-dispatch that produced no response has no such artifact to preserve. Panel completeness, exclusion from means, no imputation, and no replacement draw that conceals an abort apply regardless of date; the grandfathered set is closed to the already-committed 2026-07-24/25 scored records and their accepted final panels, while superseded artifacts stay frozen without a current-contract label, so no other historical root can be promoted. New records name
reviewer-e4/2026-07-27; both blocked Spec-A records now use one machine-readable provenance schema, and r2 records both overwritten Phase 1 responses, making the clean cohort's provenance-invalid count 2/2. This corrects the earlier Unreleased Added summary's conformance-only description of r2. It also replaces r2's previously committed paraphrased methodology diagnostic with the dispatch log's path-stripped verbatim checker output and records that correction in the run notes. The Unreleased 0.663 → 0.536 gate claim remains governed by the pre-2026-07-27 evidence contract and does not attest that rejected retry responses can be re-adjudicated; the README records the non-gating sensitivity of all three affected baseline panels. -
Sprint DA table parsing now has one terminal-suffix invariant exposed by formal E4 (#574; PR #598). The fresh post-#597 clean-control cohort reached both DA Phase 2 calls, but both cards placed ordinary Review Body prose after the
#### MAJORtable. The shared parser skipped the separating blank line and misread that prose as another table data row, so both panels failed conformance before synthesis (2/2 aborts, 100% versus the expected near-zero operational monitor); no Phase 2 output was retried, no replacement replicate was drawn, and the quant/qual fixtures were not started. The delivered DA prompt and protocol now require every prose paragraph before#### CRITICAL; the exact CRITICAL and MAJOR tables form the terminal suffix of## Review Body, with only blank lines between/after them, and DA reports contain no HTML comments. The shared conformance/synthesis helper enforces that single structural invariant, rejects hidden or late payload without enumerating Markdown container shapes, preserves exact missing-heading diagnostics, and keeps malformed contiguous rows fail-closed. Raw HTML-comment openers are checked before fence normalization, fenced blocks remain opaque nonblank sentinels for DA structure, and a bare-->remains valid report text rather than being misclassified as a comment. Positive pre-table prose plus symmetric CRITICAL/MAJOR-tail, outer-pipe, pipe-less, list, blockquote, heading, multiline, shadow-table, fenced-payload, and HTML-comment regressions cover both checker paths. The failed root remains frozen and E4 restarts from a new root after this corrective merge. -
Sprint reviewer retry diagnostics and
text:anchor preflight now match the deterministic gates (#574). The first fresh E4 root after #592 stopped the fleet at the clean-control pair: one methodology Phase 1 plan still omitted a mandatory fatal trigger after its single allowed retry, because the checker diagnostic named the missing field but not dimensionD3; the other panel reached its fifth Phase 2 seat but the DA emitted atext:anchor with no quoted excerpt, which the shared parser correctly rejected. Both panels aborted before synthesis, no malformed Phase 2 output was retried, quant/qual fixtures were not started, and the root remains frozen. Phase 1 field-cardinality diagnostics now name the exact dimension, and duplicate-subsection diagnostics preserve the sorted duplicate headings, so the one retry receives the protocol's required specific lint gap across that grammar family. All five delivered Phase 2 prompts, the sprint protocol, and the report template now state that everytext:anchor contains at least one balanced quoted verbatim excerpt as well as enforcing the existing 25-word per-excerpt ceiling; lockstep lint mutations pin both detailed and terminal wording. Runtime acceptance, anchor parsing, scoring, decision, fatality, and retry semantics are unchanged; formal E4 restarts from a new root after this corrective merge. -
Sprint reviewer terminal preflight now closes the whole structural family instead of one observed token form (#574; PR #588). The next fresh E4 root confirmed that the empty-dissent placeholder fixed by #591 no longer recurred, but stopped again before a complete panel: one emitted EIC card paraphrased its D6 block trigger instead of copying a Phase 1 substring and failed
[TRIGGER-DRIFT]; the other EIC paper-visible call exited with no output, while one paper-blind plan still needed the allowed retry after emitting a forbidden D5 fatal sentinel. No malformed Phase 2 card was retried and the root remains frozen. The five delivered Phase 1 prompts now end with a complete structural audit over required section order and paraphrase cardinality, exact headings and field multiplicity, fatal-key cardinality, forbidden paper-visible output, acknowledgement placement, and manuscript blindness. The five delivered Phase 2 prompts now end with one closed-family audit over required/retired sections, dissent cardinality, role/dimension and abstention shape, character-for-character trigger binding, fatal/block-class scope, scoring-seat findings and receipts, exact DA table grammar and dense IDs, typed anchors, and B1 bands; the protocol mirrors both and lint relocation/mutation checks pin terminal delivery. This changes no runtime checker, scoring, decision, severity, dissent, fatality, or retry semantics; E4 restarts from a new root after merge. -
Sprint reviewer terminal preflights now make omission rules executable at generation time (#574). The first fresh formal E4 root after #590 aborted both clean-control panels before synthesis: two independent paper-visible cards emitted an empty
## Scoring Plan Dissentheading with anomittedplaceholder despite the delivered omission rule, and one paper-blind EIC plan first emitted a forbidden non-mandatory fatal sentinel before the one allowed Phase 1 retry corrected it. The deterministic gates rejected both malformed Phase 2 cards, no Phase 2 call was retried, and the failed root remains frozen as operational evidence. All five delivered Phase 1 system prompts now require a terminal mandatory/non-mandatory fatal-key count, while all five delivered Phase 2 prompts require a terminal heading/content check that deletes empty dissent placeholders; the sprint protocol mirrors both and role-scoped lint mutations pin every delivered surface. No checker, scoring, decision, severity, dissent, or fatality semantic changes; formal E4 restarts from another fresh root after this corrective merge. -
Sprint reviewer live grammar instructions now close the E4 delivery gaps exposed after #589 (#574). The first post-merge isolated Opus 5 pre-measurement attempts aborted five of seven panels: one Phase 1 reviewer exhausted its retry after emitting a forbidden
what_triggers_fatal: NOT_APPLICABLEline on a non-mandatory dimension, while four paper-visible cards independently failed on a duplicate report-levelcontract_role, an empty## Scoring Plan Dissentsection,Severity: Strengthunder strength headings, or an over-25-wordtext:anchor excerpt. The deterministic checkers rejected every malformed card as designed, and no Phase 2 output was retried. All five delivered Phase 1/Phase 2 system-prompt subsections now spell out the corresponding omission/placement rules, exact contract dimension names, weakness-only Severity, and the pre-output quote-length check; the sprint protocol and report template mirror them, while role-scoped lint witnesses and mutation tests pin the lockstep surfaces. This clarification changes no checker, decision, scoring, severity, or anchor semantics; E4 restarts from a fresh output root after the corrective merge. -
Sprint reviewer typed-anchor grammar made self-contained at the delivered Phase 2 boundary (#574). E4 preflight exposed that the checker correctly required
<type>: <locator>, while the isolated sprint call receives only each agent's### Phase 2 — Paper-visible reviewsubsection and that delivered text described wrapping variants without spelling out the colon grammar. All five delivered reviewer subsections now state the exact value grammar, demonstrate whole-value wrapping, reject both observed type-only wrapping forms, require matched and unpadded outer wrappers plus balanced straight/curly quote pairs, and define the executableabsence: <where> — expected <item>; checked <surfaces>subgrammar with each reserved separator appearing exactly once. The peer-review template and sprint protocol mirror the rule; template replacement instructions sit on a separate non-value line, while the shared phase/synthesis validator uses total deterministic delimiter parsing and a type-aware quote stack so balanced nested quotations remain valid and rejects reversed or backtracking separators, padded or unbalanced wrapper debris, crossed quote pairs, and incomplete absence anchors without mistaking balanced locator content such asEq. [3]or inline code for an outer wrapper.check_role_scoped_contract.pymutation-tests every seat, both mirrors, all three shipped examples, the template's ordered canonical text/table/absence examples, every nested quote pair, square-bracket ordering, exact wrapper assignments, and each executable parser witness. -
Reviewer contradiction batch: C1/C2/C4/C6 + manuscript data-fencing + panel-reference no-invention rule + Top Blocking Issues section + overlap-prohibition removal (#574 PR 1). Seven prompt-level defects from the #574 audit, none changing the review's behavioral direction (the behavior batch — quota removal, typed anchors, severity transport — waits on the E4 baseline). (C1)
eic_agentfrontmatter claimed the EIC "delivers the final editorial decision" while the body forbids exactly that — the description now names the review-card role and the synthesizer's decision ownership. (C2) The "Recommendation to Peer Reviewers" output field is retired: reviewers run independently in parallel (Iron Rule #2), so no channel exists to deliver it — dead output at best, an independence leak at worst; the SKILL.md flow line "Sets the review tone" goes with it. (C4) The Roadmap's Priority-2 rule cited[CONSENSUS-2], a label the same file's taxonomy explicitly says does not exist — now "corroborated findings (agree = 2, conflict = 0)". (C6) The EIC's "first impression score (1-10)" fed no output field and is retired. (A6) All five reviewer agents now receive the manuscript wrapped in a<paper_content>...</paper_content>data delimiter with an explicit data-not-instructions rule (SKILL.md Iron Rule #7 operationalized at the call boundary; injection attempts are themselves a reportable weakness);sprint_contract_protocol.md§2 step 4 specifies the delivery side in lockstep, and the re-review protocol marks both the revised manuscript and the Response to Reviewers as untrusted author-authored inputs. (A5) Panel-suggested references gain the no-invention rule indomain_reviewer+perspective_reviewer: the v3.11 citation gate covers the author's citations, not the panel's suggestions, so recommendations must be attestable or carry[UNVERIFIED]as search leads; the perspective reviewer's "3-5 references" quota is removed (zero allowed). (E7) The Editorial Decision Letter gains a mandatory "Top Blocking Issues (0–3, ranked)" section — each row with source reviewer(s), evidence anchor, and resolving roadmap item; zero rows is valid for a genuine Accept (template + synthesizer letter format in lockstep, replacing the synthesizer's unanchored "Summary of Key Issues"). (P0-3) The DA's "No repeating other reviewers" discipline — unexecutable under Iron Rule #2 blindness and a suppression incentive against legitimate corroboration — is reframed: report what you find, deduplication is synthesis work. -
Re-review plumbing: yardstick continuity + Stage 4→3' handoff completeness + apply-report output binding (#574/#576 pre-work). Three bug-class gaps surfaced by the 2026-07-24 reviewer-stage audit, fixed ahead of the #576 contract design because each is wrong today independent of it. (1) Yardstick continuity: re-review re-ran
field_analyst_agentover the revised manuscript (academic-paper-reviewer/SKILL.mdmode table), regenerating the reviewer configuration mid-loop so Round-2 verdicts could be judged against a different yardstick than the one that produced the Roadmap being verified. Re-review now reuses the Round-1 Reviewer Configuration Cards (new § Yardstick Continuity inre_review_mode_protocol.md; target-venue freeze included); the standalone-invocation fallback regenerates visibly with a[YARDSTICK-REGENERATED: <original|revised> manuscript — <reason>]marker recorded on a new Judge Record "Reviewer configuration" line — advisory, never a block. (2) Handoff completeness: the orchestrator's Stage 4 → 3' transfer row omitted the Revision Roadmap and the #390 apply report even though both are declared required re-review inputs (revision_patch_protocol.md,academic-paper/SKILL.md); the row now carries both plus the Round-1 configuration cards, and the re-review protocol's own input list gains the same two entries. The #528 orchestrator content lock was re-pinned per its documented procedure. (3) Apply-report output binding: the apply report recordedbase_draft_hashbut no hash of the revised draft it describes, so a post-apply rewrite (finalizer pass, manual edit) could silently invalidate the untouched-block evidence while the report stayed paired with the draft. Report format 1.1 addsoutput_draft_hash(same 12-hex format, hashed over the exact written bytes);revision_patch_protocol.mdand the re-review protocol now instruct consumers to check it before relying on the report, and the Stage 4→3' row names the check. 3 new tests (TestReportOutputHash) pin the binding;check_390/check_268/ boundary-semantics / spec-consistency lints all green. The 8-round cross-model review trajectory (13 P1 + 8 P2 closed, final round 0 findings;/security-review0 findings) drove the fixes across every operative mirror: all six Stage 4→3' authority surfaces carry the new inputs in lockstep (SKILL.md, state machine transition + artifact lineage, team collaboration, state tracker, reviewer SKILL.md, two-stage protocol), each gated on the re-review-default vs user-requested fresh-full-review split (mid-entry quick→full path) so the legitimate no-Roadmap path is never rejected;shared/model_tiering.md's Stage 3' caching roster no longer re-dispatches field_analyst; Schema 6judge_recordgains the optionalreviewer_configurationmember (round1_cards_reused/ verbatim[YARDSTICK-REGENERATED ...]); the #390 spec records format 1.1 in a §0.1 amendment; and the three registered examples execute the new contract (EIC-only verification, reused cards, apply-report retention).
[3.19.0] - 2026-07-22 — Revision-round claim-drift guards, PDF read-integrity preflight, read-scope attestation
Added
-
PDF read-integrity preflight for locally-extracted page anchors (#512). Closes the local-extraction-channel gap between the v3.7.3 locator-presence rules and the #182 existence gate: PDF readers silently truncate documents with malformed cross-reference tables, so a real, correctly-cited source could acquire an apparently valid
pageanchor from a truncated or mispaginated read and pass every existing gate. Newscripts/pdf_read_preflight.py(pypdf-backed with theverify_submission_package.pyImportError-degradation precedent; not a "grep the first/Count" check — xref streams,/Prevchains, and object streams ride pypdf's machinery) compares three independent page-count signals — the raw root page-tree/Count, the script's own cycle-guarded/Kids-walk leaf count, and pypdf's flattened page list — and emits a JSON sidecar (pdf_read_preflight/1: verdict + file sha256 + the three counts + captured parser-repair warnings).PASSrequires all three to agree with zero parser warnings; count disagreement isFAIL(the truncation/mispagination signal itself); everything the preflight cannot vouch for — encryption, cycles, repair chatter even with agreeing counts, missing pypdf — isUNAVAILABLE. Enforcement sits upstream of the writers per the issue: the three v3.7.3 emitters gain firm rule R-L3-1-D (a locally-read PDF'spageanchor requires aPASSsidecar in context; otherwiseanchor:noneor an independently-visible locator + explicit warning),claim_ref_alignment_audit_agentStep 4 gains the precondition bound to the existingref_retrieval_method == manual_pdfdiscriminator (sidecars join onref_slug; the sha256 is confirmatory until #513 supplies an anchor-side hash field) with the[pdf_read_integrity_unverified]advisory rationale tag (never an UNSUPPORTED verdict on this basis alone), and the §3.6 orchestrator — the layer that CAN run Bash — runs the preflight once per locally-read corpus PDF and passes sidecars into audit and drafting context. Cross-model review rounds 1-8 (3 P1 + 2 P2, then 7, 2, 1, 2, 1, 1, 1 P1, all closed): rounds 6-8 finished the header scan's lexer fidelity (ten-digit object numbers; %-comment token separators; signed and zero-padded integer tokens — the scan now implements the full ISO 32000 separator AND numeric-token model, closing the lexical-obfuscation family structurally); round 5 extended the header scan's boundary/separator class to full ISO 32000 whitespace (NUL-preceded replacement headers are seen) and added the compressed-object variant (a direct raw replacement of an object whose active copy lives inside an object stream, appended after its container with a stale startxref, now vetoes PASS; a raw copy before the container is the legitimate superseded-into-objstm case and stays clean); round 4 fixed the object-header scan for ISO-valid bare-CR line endings (Python's multiline anchor does not treat CR as a line start, which blinded both xref-coverage checks on CR-only files); round 3 hardened the stale-startxref check against redefined-object-number variants (the newest raw copy of every directly-stored object must be the copy the active xref chain references, with a calibration guard for offset-shifted files) and switched the trailing-data predicate to ISO 32000 §7.2.2 PDF whitespace (NUL padding passes, vertical tab is data). Round 2 added the stale-startxref xref-coverage cross-check (raw object headers absent from the active xref chain veto PASS), plus a strict integer-object requirement on/Count(float/string coercion rejected), the FAIL-vs-UNAVAILABLE split in R-L3-1-D (positive truncation evidence refuses the page anchor; mere absence of verification — standalone dispatch, no-Python installs, unpreflighted files — is an explicit-warning advisory, never a manufactured refusal), thepdf_preflight_verdictkey on citations handed toretrieve_fn(content-based passage selection BEFORE the judge reads a page-scoped passage), the orchestrator sha256 freshness re-check + cross-runtime coverage note, and the tagged-SUPPORTED row in the operational finalizer matrix. Round 1: trailing-data-after-final-%%EOFveto (a truncated incremental update otherwise PASSes on the older revision's agreeing counts); preflight moved to Stage 1 corpus intake, independent of the opt-in audit mode (an audit-gated preflight left default-mode runs sidecar-less at R-L3-1-D, gate-refusing valid citations); the executable-path enforcement —run_audit_pipeline(pdf_preflight_sidecars=...)tags rows at the Step-6 emission point after cache resolution so cache hits cannot bypass, and the finalizer surfaces[LOW-WARN-PDF-READ-INTEGRITY-UNVERIFIED]on SUPPORTED rows so content-based-fallback support does not render the advisory invisible; parser warnings survive early exits (appended in the capture handler's finally). 18-test synthetic-PDF suite + 8 pipeline/3 finalizer tests (no binary fixtures);agents/mirrors re-synced; the #528 orchestrator content lock re-pinned per its documented procedure. Provenance: mechanism observed in kengo006/alexandria; ranked P1 of three in the 2026-07-11 dual-track adoption review. Spec:docs/design/2026-07-20-512-pdf-read-preflight-spec.md. -
read_scopehonest-coverage attestation on the human-read ledger + anchor-aware finalizer promotion (#513). ARS recorded source possession, AI verification, and a binary human-read mark — but nothing recorded HOW MUCH of a source was read: a TOC-only reading promoted a citation whosepageanchor pointed at a chapter the user never opened./ars-mark-readgains an optional declaration-only attestation —--scope {full_text,sections,abstract_only,toc_only,unknown}+ repeatable--locator(requiressections; contradictory attestations are refused, not recorded) +--note(requires--scope) — written as an optionalread_scopeobject on the ledger entry; absent meansunknown, never fabricated or backfilled, and attestation args are rejected with--unmark. Placement honors the v3.6.8 firm rule the dual-track review verified: corpus entries are adapter-owned and never carry human-read state, so the attestation lives on the user-owned<passport-stem>_human_read_log.yaml, now covered by a sidecar schema (shared/contracts/passport/human_read_log.schema.json,version_recordsprecedent, registered in the contracts README; test-time validation only — the CLI stays dependency-light). The consumer ships in the same PR (an attestation with no consumer is dead metadata): the Cite-Time Provenance Finalizer's LOW-WARN → ok promotion becomes read-scope-aware — absent/unknown/full_textpromote (legacy marks impose no migration);abstract_only/toc_onlyresolve to the new draft-visible acknowledged-partial statusLOW-WARN-PARTIAL-COVERAGEwith an explicit coverage note;sectionspromotes only when apage/section/paragraphanchor falls unambiguously within a declared locator, andquoteanchors promote only under full coverage. The governing attestation rides the settled latest-timestamped-event-wins precedence (a latest rescind always wins), the idempotency evidence enumeration names the attestation explicitly, andLOW-WARN-PARTIAL-COVERAGEkeeps LOW-WARN's severity everywhere (contamination suffixes attach identically; the formatter passes it as an acknowledged variant with the note surfaced — distinguishable from an unacknowledged LOW-WARN at the terminal gate, never refused). CLI presence checks useis not None(an explicit empty string is invalid, not absent) and locator/note length bounds are enforced at write time in lockstep with the schema, so the CLI can never produce a ledger the schema rejects; two cross-model review rounds (5 P1, then 3 P1, all closed) drove the hardening: round 2 addedLOW-WARN-PARTIAL-COVERAGEto every base-status enumeration (scripts/check_v3_10_policy.py_BASE_STATUS, the hard-gate handoff, the contamination/policy-stamp sections — partial markers get suffixes andpolicy_hashstamps like any LOW-WARN and are never refused by the freshness gate), put the citation's own anchor into the idempotency/revision-loop evidence enumeration (a revision moving an anchor out of declared coverage re-resolves the marker even with corpus and ledger unchanged), and mirrored the locators-require-sections rule as an if/then conditional in the sidecar schema so audit-time validation matches the writer contract.commands/ars-mark-read.md+ formatter LOW-WARN remediation updated in lockstep; #528 orchestrator content lock re-pinned. Provenance: mechanism observed in kengo006/alexandria; ranked P2 of three in the 2026-07-11 dual-track adoption review. Spec:docs/design/2026-07-20-513-read-scope-attestation-spec.md. -
Revision-round claim-drift guards: claim-strength ladder + deterministic token conservation (#569 / #570). Stands an advisory watch over the epistemic and token halves of the #390 honest-claim residual — the patch mechanism confines silent-distortion exposure to touched blocks but never checks a touched block's interior (DELEGATE-52, arXiv:2604.15597, the same round-trip subtle-modification finding #390 already anchors); a guard now watches each half, and whether the guards reduce the measured drift is a separate re-measurement (#652), not a claim this entry makes. Two complementary layers, both advisory-first, mechanism shape borrowed from Yila-AI/sci-ssci-skills by @MissOrangePeel (its
sci-ssci-polishingclaim-strength ladder +check_invariants.py). (1) Claim-strength ladder (shared/references/claim_strength_ladder.md): an ordered epistemic scale (is consistent with < is associated with < predicts < contributes to < affects/leads to < causes) whose invariant is "no silent move, either direction, without an authorizing roadmap item" — wired intodraft_writer_agentrevision mode (a patch op changing a claim's rung must name the item that authorizes the strength change, not merely an item touching the block) and into a new advisory Phase E6 inclaim_verification_protocol.md(revision-round-onlySTRENGTH-DRIFTED→ADV-E6-<n>rows, mirroring the ADV-E4/E5 pattern, never gating). (2) Deterministic token conservation (scripts/check_revision_token_conservation.py): a stdlib checker over numeric tokens,<!--ref:-->/<!--anchor:-->markers, bracketed/author-year citations, and protected terms, in pair mode or per-op against a #390 patch, with Unicode fold-before-split (the #524 ordering lesson) and per-oproadmap_item_idsattribution; framed necessary-but-not-sufficient, mirroring the v3.11 deterministic-gate/LLM-semantic split. Rather than cite an earlier-generation-model study as motivation (issue #569 precondition), a held-out set (evals/heldout/revision_claim_drift/, codex-generated scenarios, cross-model + maintainer-adjudicated judging) measured the CURRENT frontier model's baseline first: 2/8 = 25% claim-strength/hedge drift under hedge-drop and null-reframe pressure, 0/8 unauthorized numeric/citation drift (the one authorized change correctly isolated), 2/2 controls clean — the evidence that the ladder earns its place rather than closing as documented-negative-scope. 31 mutation tests, registered in the CI pytest manifest. Dual-track pre-ship review (/security-review+/codex reviewxhigh): security 0 findings (read-only stdlib lint, empty attack surface); codex closed 6 (4 P1 + 2 P2) — the checker's number regex now keeps signs inside brackets/after commas and scientific exponents (6.02e23→6.02e24was a false-negative), narrative author-year citations now carry the author (Smith (2020)→Jones (2020)on the same ref slug was a false-negative), E6 was rewired to consume the durable per-round revision-patch sidecars via a declared Revision-Evidence Bundle (its original inputs were never transferred across the Stage 4→4.5 handoff) and to audit every round on a multi-round path (not only the last pair), step 3a's attribution semantics corrected (the checker emits the deterministic signal; E6 supplies the authorization verdict), and E6/ADV-REV report tables + checkpoint options defined.
Fixed
- SETUP Method 4a description-length figure de-drifted (#564).
docs/SETUP.md/docs/SETUP.zh-TW.mdstated the four skilldescriptionfields "currently sit in the 440-842 range"; the actual lengths have since grown to 566-986 characters. Replaced the hardcoded range with the durable comparative statement (each exceeds claude.ai's 200-character upload cap while staying under Claude Code's 1,024-character allowance), so the sentence cannot silently drift again as descriptions evolve. Rationale for not trimming the descriptions themselves is unchanged. - Write-scope guard launcher: watchdog-path stall removed; #545 flaky launcher tests fixed (#545). Two-layer root cause. (1) Production: on hosts without a
timeoutbinary (stock macOS),hooks/run_guard.sh's watchdog subshell — and thesleepit forks — inherited the caller's$(...)capture pipe; after the parent killed the subshell, the orphanedsleepkept the write end open, so every healthy probe/guard run blocked for the full wall-clock bound (~2× bound per PreToolUse call; measured 6.1 s at the default bound). The watchdog now redirects to/dev/null, cutting the healthy watchdog path to ~0.15 s with decisions unchanged. (2) Tests: the harness pinnedARS_PROBE_BOUND=1, leaving so little margin that loaded machines pushed real interpreter + guard runs past the bound into the launcher's designed pass-through degradation — which every deny-expecting test read as a rotating, load-dependent failure. The harness default is now a generous 30 s (pure margin; only the hanging-candidate tests wait a bound out, and they override to 5 s), and a newLauncherSlowInterpreterTestpins the regression (a python3 that takes ~1.5 s per invocation must still forward the real deny). Suite: 21 tests / 70-80 s → 22 tests / ~23 s, green under 8-way CPU load.
[3.18.0] - 2026-07-18 — Self-improvement survey integration: advisory quality layers, risk-stratified claim gate, cross-model reviewer & judge tracks, cache re-validation
Added
- Re-review judge independence + judge record (#539). The Stage 3' re-review judges revisions on the same model family that drove them — an analogous correlated-judge configuration to the one §8.1.2 warns about. When cross-model verification is active (configured + consented), every Priority 1 roadmap item's addressed-ness verdict gets an independent judgment-specific cross-model pass (provider transport reused, no grounding gate, closed verdict set incl. MADE_WORSE, non-conforming →
unavailable); results land in the R&R matrix's Cross-model column (agree/diverges: <verdict>/unavailable/not_configured) —divergesis a Phase 2 synthesis review trigger, never a vote. Single-family runs carry a never-omitted verbatim disclosure line. Both cases emit a Judge Record (verification judge; Round-1 panel provenance copied seat-level from the #540 provenance block — carried into Stage 3' via the Editorial Decision Letter, now named on all handoff surfaces incl. the re-hashed state-machine lock; prompt/rubric surfaces; evidence seen; judging budget separate from generation) — Schema 6 optionaljudge_record, Schema 11 optional Priority-1 row fields. External motivation: Ren et al. (2026, arXiv:2607.13104 §8.1.2). - Cross-model reviewer track (#540). With
ARS_CROSS_MODELconfigured, ONE existing peer-reviewer slot (Reviewer 2) runs on the cross-model family — a substrate swap inside the fixed five-seat panel, explicitly NOT the retired 6th-reviewer design (whose five counterproductive conditions all attach to an added seat). Transport follows #523 dispatching-layer ownership with a two-call sprint-contract split (paper-blind Phase 1, then Phase 2 with the paper); the slot's vote enters the panel matrix unchanged; the synthesizer computes NO cross-family aggregate (forbidden-operations list holds) — splits are visible by inspection in the matrix, and the provenance block names which seat ran on which family. Activation requires the explicit cross-model consent, not the env var alone. Without a second provider, the new Editorial Decision LetterReview Panel Provenanceblock discloses the single-family correlated-error caveat instead of silently implying independence; dispatch failure falls back with the fallback disclosed. Authority:shared/cross_model_verification.md§ Cross-Model Reviewer Track. External motivation: Ren et al. (2026, arXiv:2607.13104 §5.2) — repeated sampling of one model may amplify a confidently-wrong conclusion; heterogeneous critique models are among the named safeguards. - Cache-source staleness advisory + opt-in live re-validation (#541). The citation-verification cache (v3.11 Delta 2, 90-day TTL) gains an age-based advisory layer:
VerificationCache.entry_age_days/stale_reportcompute the oldest-live-row age per citation, and Phase A § A0.5 emitsADV-CACHE-<n>advisory rows (citation, age, threshold) for cache-served verifications older thanARS_CACHE_STALE_ADVISORY_DAYS(default 30;0disables) — displayed with per-row options at the MANDATORY checkpoint via the #547 advisory-row template, never gating. The wiring is real code, not prose:verify_citation/verify_passportrun cache-through by default (closing the #182 Delta-2 forward-decl — the four resolvers route via the detailed contamination-signals wrappers with interoperable cache keys), the CLI gains--no-cache, andARS_CACHE_REVALIDATE=1re-verifies stale rows live at the gate (per-row bypass + re-population; cost scales with stale-row count). Invalidation cascade is unconditional: after any re-validation the citation's summary row regenerates and Phase E verdicts re-run at the gate (no baseline exists to diff against). Cache robustness hardened: naive timestamps read as UTC, malformed rows are misses, clock-skew clamps to 0, and the advisory flag is computed from the emitted rounded value. Summary schema gains optionalcache_age_days+cache_stale_advisory; cache/gate/CLI suites extended (52 gate+cache tests green). Framing: Ren et al. (2026, arXiv:2607.13104 §6.2.3) names scheduled review-and-attenuation and the staleness failure mode; applying it to the citation cache is ARS's design inference. - Risk-stratified Stage 2.5 claim verification (#549). Phase E Mode 1 sampling moves from a uniform 30% random spot-check to the #518 tier logic at claim level: HIGH-IMPACT claims (headline / numerical / causal / methods-critical / disputed — same definition family as
shared/cross_model_verification.mdstep 2) verified 100% uncapped, plus a 10% RANDOM sentinel of the remainder (min 3 / max 10) for unbiased drift detection, with a top-up floor preserving the pre-#549 minimum of 10. Each audited claim's tier is recorded in the Claim Registry so coverage is inspectable. Mode 2 (Stage 4.5, 100%) unchanged — the delta is WHEN consequential distortions surface: before the review stage instead of at the final-gate backstop, cutting late-rework loops. Authorityclaim_verification_protocol.md§ Sampling Strategy; mirrored inintegrity_verification_agent+integrity_review_protocol.md. External motivation: Ren et al. (2026, arXiv:2607.13104) §3.3 active-acquisition targeting of failure modes/verifier disagreement + §9.2 resource-aware improvement; the high-impact-first allocation is ARS's design inference from those principles. - Search-bounded novelty claims (#548). Absolute priority language ("the first study to...", "no prior work has...") asserts the absence of literature — no cited source can support it, so the citation gate structurally cannot catch inflation there.
draft_writer_agentgains a## Search-Bounded Novelty Claims (#548)section: default emission is the bounded form ("To our knowledge, based on searches of [databases] through [date]...") filled from the Schema 2search_strategyactually used, nearest prior work named instead of claiming a vacuum, the bounding qualifier marked as a protected hedge (so abstract compression cannot silently restore the absolute claim), and the absolute form emitted only on explicit user confirmation (recorded for AI disclosure). Phase E gains an E5 novelty-claim classification (authority:claim_verification_protocol.md§ E5; mirrored inintegrity_verification_agent+integrity_review_protocol.md): primacy assertions classify asSUPPORTED_WITHIN_SEARCHorUNRESOLVEDagainst the documented search — never "globally verified" — advisory-only, never gating, per-row user decision.failure_paths.md's Insufficient Novelty guidance stops advising bare "strengthen the novelty claim" and routes through the bounded form. External motivation: Ren et al. (2026, arXiv:2607.13104 §7.4) — discovery agents cannot easily verify novelty on their own and may exploit weak proxies. - Pipeline behavior robustness held-out set, seed v0.1 (#550). New
evals/heldout/pipeline_behavior_robustness/— metamorphic paired cases for the runtime routing/gate layer: 8 documented-ground-truth base scenarios (explicit-trigger routing, cross-phase clarify obligation, rebuttal-audit vs revision-coach input-shape gate, rejected-mechanism refusals, Stage 4.5 IRON-RULE hold under deadline pressure) × 3 perturbation axes (terse / verbose / long-context padding), en + zh-TW pairs, invariant = same expected behavior per pair. Lives underheldout/(LLM-judged, no script entrypoint) with per-itemrule_anchorcitations so ground truth stays reproducible from the shipped rules; paired-consistency + long-context-degradation metrics and a fresh-session measurement protocol mirror the #505 runs. v0.2 forward note: wrapper/reorder axes + cross-model variant authoring before the set gates routing-layer changes. External motivation: Ren et al. (2026, arXiv:2607.13104 §8.2.1) — evaluate prompt-policy behavior under paraphrases, formatting shifts, and longer contexts. - Per-sub-question scope bindings + Phase E scope-conformance advisory (#547). RQ Brief Schema 1 (
shared/handoff_schemas.md) gains an optionalsub_question_bindingsfield — each sub-question records which parent-scope constraints (population / timeframe / geography / domain) it inherits and any user-approved deviations; absent = full-scope inheritance (pre-#547 briefs remain valid inputs; the E4 advisory then compares against the wholescopeobject — the advisory itself is new behavior).research_question_agentemits the bindings and never silently broadens;structure_architect_agentoutline sections name the sub-question they serve and inherit its bindings. Phase E gains an E4 scope-conformance advisory (authority:claim_verification_protocol.md§ E4; mirrored inintegrity_verification_agent+integrity_review_protocol.md): claims whose stated scope exceeds their inherited scope surface asSCOPE-BROADENEDrows at the Stage 2.5/4.5 checkpoints — advisory-only, never gating, per-row user decision (proceed open, or accept with justification; a requested reword is an ordinary revision instruction in the normal flow), no automatic rewriting. Closes the silent-broadening path from a narrow RQ to conclusions the evidence never covered. External motivation: Ren et al. (2026, arXiv:2607.13104 §5.1) — decomposition becomes vulnerable when sub-problems stop preserving the original task's constraints. - SessionStart update-available reminder for plugin installs (#543 → #544). New
scripts/ars_update_check.sh(Bash 3.2, no jq, always-exit-0 fail-silent) compares the installed plugin version againstmain's.claude-plugin/plugin.json— 24 h cache at~/.cache/ars/update-check, 3 s network ceiling,ARS_UPDATE_CHECK=0kill switch — and the SessionStart announce prepends a one-line reminder pointing at/plugin update academic-research-skillswhen behind. Motivated by #543 (reporter six minor versions behind on a long-fixed bug): third-party marketplaces default auto-update OFF and surface no behind-signal. Hermetic 15-test suite (scripts/test_ars_update_check.py, manifest id544-update-reminder). Spec:docs/design/2026-07-18-544-update-reminder-spec.md. - README motivation + calibration docs: self-improvement survey anchor (#542). The README /
README.zh-TW.md"Why human-in-the-loop, not full automation?" sections gain Ren et al. (2026, arXiv:2607.13104, Self-Improvements in Modern Agentic Systems: A Survey) as a third, survey-level literature anchor — §7.4 scientific-verification limits, §5.1–§5.2 human-auditing safeguards, §2.2 EURISKO user-as-external-evaluation-signal precedent — framed explicitly as design rationale, not empirical proof.academic-paper-reviewer/references/calibration_mode_protocol.md§ Why this mode exists cites the survey's §8.1.2 evaluator-independence + calibration-against-a-verifiable-subset safeguards as the external statement of what calibration mode instantiates. The survey's actionable deltas stay tracked as issues (#539–#541, #547–#550). External motivation: Ren et al. arXiv:2607.13104 (2026-07).
[3.17.0] - 2026-07-16 — Pipeline boundary semantics, canonical cross-model handoff envelope, executable panel checker
Security
- Tools allowlist for the three top-level plugin agents (#514, implemented in PR #521 by @madtriceps). The three plugin-exposed agents (
synthesis_agent,research_architect_agent,report_compiler_agent; deep-research sources + byte-identicalagents/mirrors, six files) now declaretools: Read, Write, Edit, Grep, Globin frontmatter — no Bash, no WebFetch/WebSearch — so dispatch-time capability is least-privilege even in hook-less installs, complementing the runtime Bucket A Bash deny (scripts/ars_write_scope_guard.py), which keys on agent name and continues unchanged. Retrospective entry: the code merged just after the v3.16.0 tag; documented here per the changelog-covers-merges gate.
Fixed
-
Blind-checkpoint transport moved to the dispatching layer (#523). The #518 blind disagreement checkpoints told their Bucket A primary owners (
research_architect_agent,editorial_synthesizer_agent) to execute the cross-model curl transport themselves — unexecutable under the runtime Bash deny and, for the architect, the #514 dispatch-time allowlist, so on every hook-active run the check at an irreversible decision silently degraded to single-model, indistinguishable from a transient API outage. New Transport ownership (#523) contract inshared/cross_model_verification.md§ Blind Disagreement Checkpoints: the owner commits its structured decision and emits the sanitized cross-model input as a handoff artifact; the dispatching layer (the main session running the skill, orpipeline_orchestrator_agentin pipeline Mode A — neither is Bucket A) executes § API Call Patterns, applies the mechanical enum comparison, and re-invokes the owner only for the divergence rebuttal; the editorial checkpoint's dispatched shape is an explicit, justified exception to the before-the-roadmap ordering (safe because the sprint-contract boundary keeps cross-model drivers out of the roadmap). The rule generalizes to any Bucket A cross-model owner —devils_advocate_reviewer_agent's independent DA critique routes the same way, with every successful response returned to the owner (no mechanical comparison exists for the dispatcher to resolve); non-fenced owners (integrity_verification_agentat the Stage 2.5/4.5 gates, deep-researchdevils_advocate_agent, the main session) execute directly, unchanged. No Bash/WebFetch re-added to any fenced agent (resolution (a); (c) rejected). Converged 0 P1/P2 across first-party security review + two codexgpt-5.6-solxhigh rounds. -
Pipeline prompt-surface contradictions from the #528 Mode-A replay (#529). The two genuine contradictions of the four ambiguities the replay surfaced: (1) the Methodology Blueprint was listed as a Stage 1 deliverable and in the Material Dependency Matrix but omitted from all three Stage 1→2 handoff surfaces — added to
academic-pipeline/SKILL.md,references/pipeline_state_machine.md, andagents/pipeline_orchestrator_agent.md; (2) the post-review coaching trigger read "After Stage 3 or Stage 3' completion, Decision = Minor/Major", but routing sends a Stage 3' Minor directly to Stage 4.5 — the trigger is now split by stage (Stage 3 = Minor/Major; Stage 3' = Major only) and the Coaching Rules exclusion list extended to match. Text-only. Retrospective entry: the code merged before this entry was written; documented here per the changelog-covers-merges gate. -
Stage 5 / Stage 6 boundary semantics — the two under-specified boundaries from the #528 Mode-A replay (#528). New authority section
references/pipeline_state_machine.md§ Stage 5 and Stage 6 Boundary Semantics, mirrored inacademic-pipeline/SKILL.mdandagents/pipeline_orchestrator_agent.md+references/process_summary_protocol.md. Stage 5: "Before finalization: always MANDATORY" now names exactly one checkpoint — the entry gate between Stage 4.5 PASS and the Stage 5 dispatch, carrying the format decisions; the in-stage content confirmation before the final PDF is Stage 5 execution (not a pipeline checkpoint), and the Stage 5 completion checkpoint (Final Paper delivered, before Stage 6) is FULL — never SLIM — but not MANDATORY. Stage 6: the state machine previously ended atStage 5 → ENDwith no Stage 6 at all; it now defines the Stage 5→6 transition, the decline path (Stage 6 is non-mandatory: declining marks itskippedand the pipeline still terminatescompleted), the terminal checkpoint after the Process Record is delivered, and the canonical terminal-acknowledgement vocabulary (finish/end/done/confirm, or an unambiguous natural-language equivalent) whose acceptance sets the pipeline global state tocompleted. All derived from existing text — no architecture change, no checkpoint relaxed. Thirteen codexgpt-5.6-solxhigh review rounds drove the consequential closure across the wider surface set: thestate_tracker_agentcontract gains Stage 6 (stage_id enum, SSOT block, prerequisite rows, terminal/decline action pairs), the FULL checkpoint-type row stops claiming "before finalization" (it collided with the MANDATORY row), Stage 5 execution consumes the entry-gate citation-style decision instead of re-asking, Stage 6 joins the explicitly-skippable list (the skip validator would otherwise reject the pinned decline path), the engagement-tracking SLIM downgrade gains its FULL-checkpoint exception, and the whole-pipeline collaboration-observer pass is re-timed to Stage 6 record compilation (before delivery — "at pipeline completion" could not coexist with completion-after-acknowledgement). Newscripts/check_pipeline_boundary_semantics.pydefrift lock pins all four #528 resolutions across the five surfaces with 66 mutation tests (one adverse-value witness per closed gap), wired into spec-consistency.yml and the unified pytest manifest; because twelve rounds showed sentence-level pins alone cannot converge on prompt surfaces, all five files also carry bibliography_agent-style whole-file sha256 content locks — any byte change fails CI until the pinned hash is updated in the same commit. Closes #528.
Added
- Canonical cross-model handoff envelope + dispatcher consumer contract (#527). The #523 owner→dispatcher→owner transport path was internally coherent but enforced by prose only — no canonical delimiter, no machine-stable schema, no malformed-result mapping, no pinned consumer trigger, so every test could stay green while a dispatcher silently treated a checkpoint owner's handoff as an ordinary deliverable. #527 closes that: one canonical
[CROSS-MODEL-HANDOFF v1]envelope (checkpoint_kind / owner_agent / correlation_id / expected_result / owner_decision-outside-payload / payload) defined inshared/cross_model_verification.md§ Cross-model handoff envelope, withscripts/cross_model_handoff.pyas the NORMATIVE grammar (parse + outcome routing as pure functions) and a deterministic owner→dispatcher→owner fixture suite on a fake transport (scripts/test_cross_model_handoff.py— no external API, no manuscript upload; literal pins guard the module constants against self-referential testing). The three checkpoint owners (research_architect_agent,editorial_synthesizer_agent,devils_advocate_reviewer_agent) emit the envelope with their closed kind/result-shape pair; the Mode-A orchestrator pins the consumer contract (recognition as a transport request never a deliverable; malformed envelope/result →[CROSS-MODEL-ERROR: malformed_*]→ outcomeunavailable, never a fabricated judgment; agreement → mechanical fill with NO owner re-invocation; divergence → re-invoke the original owner with the minimum return context, the dispatcher never authors the rebuttal; DA full-return: every successful response goes back to the owner;ARS_CROSS_MODELunset stays byte-equivalent). Newscripts/check_cross_model_handoff_contract.pypins the contract across all five surfaces (including a prose-enums-follow-the-module invariant) with a per-branch mutation-witness suite; both suites wired into spec-consistency.yml + the unified pytest manifest. Closes #527. - Defrift lock for the #514 tools allowlist (#524). New
scripts/check_tools_allowlist.py+ a 74-test suite (a failing witness per invariant branch), wired into spec-consistency.yml and the unified pytest manifest. YAML is the authority, not a line scan: every semantic decision reads a duplicate-preserving node tree (yaml.compose, which keeps a shadowed duplicate key visible and resolves an alias into shared node identity), the frontmatter fence is a column-0---only (an indented---inside a block scalar can't truncate the block and hide keys below it), and any frontmatter that will not compose to a mapping or uses a merge key (<<) / alias is a fail-closed error. Invariant 1 pins thetools: Read, Write, Edit, Grep, Globvalue on all six #514 surfaces: the node tree must carry exactly onetoolskey whose value normalizes to exactly the canonical five, plus an additive byte-exact raw-line witness (CR-sensitive, so a symmetric LF→CRLF conversion is drift; fires when the verbatim pinned line is absent). This closes the drift scenario where a future PR edits a source+mirror pair symmetrically (re-adding Bash, dropping a tool, or typoing a name) and passes every CI gate green, becausecheck_agents_mirror_sync.pypins only pairwise byte-equality and the runtime guard keys on agent name, never frontmatter; changing the allowlist now requires touching the lint's pinned value in the same commit (standard lock semantics). Invariant 2 reconciles the frontmatter channel against the runtime channel: any agent whosenameis a Bucket A key inscripts/ars_phase_scope_manifest.jsonmust not declare Bash in atools:key in any YAML-legal form — comma string, quoted string, flow/block list, inline comment,Bash(...)permission specifier (BashOutputis a different tool and not flagged) — failing closed on a missing/non-mapping manifest, unparseable Bucket A frontmatter, or an unrecognizedtoolsshape. Twelve rounds of codexgpt-5.6-solxhigh review plus first-party self-probing drove the design from a line-scan first cut to ayaml.composenode-tree authority (the byte-exact witness anchored to the composed key'sstart_mark.index; frontmatter fences found withsplitlines, which recognizes every YAML line break; the wholetoolsvalue folded through Cf-format-char stripping + NFKC BEFORE any split — so every compatibility separator becomes ASCII first: a fullwidth comma,U+FF0C thatsplit(",")would miss, and a fullwidth-paren specifierBash(git:*)U+FF08/U+FF09 that the(split would miss, both reduce to theirBashbase — so an invisible-character or homoglyph re-spelling of a tool name, or of a separator around it, cannot masquerade as a different token), closing a series of YAML-form fail-opens — quoted/flow/escaped Bash, escaped-key duplicates, merge-key injection (plain<<, chained<<: [*a, *b], alias-to-merged-mapping, a merge tag on a non-scalar? !!merge [x]key, and a merge buried in a sequence), a#-in-quoted-key comment-strip bypass, an indented-fence truncation,!!binary/!!strtag tricks, a leading-BOM skip, parser-dependent duplicatename/toolskeys, nested agent files missed by a non-recursive glob (nowrglob), directory symlinksrglobdoes not descend (now fail closed), bare-CR (old-Mac) frontmatter read as absent, aRecursionErroron pathologically deep nesting (now fails closed), and a zero-width/BOM/fullwidth re-spelling ofBashslipping the exact-string membership test (Bash,Bash,Bash—str.strip()leaves Cf format chars, so the token stayed distinct fromBash; now folded, with the byte-witness confirmed to still fire on an invisible-char canonical value), and the two ordering corollaries where a compatibility separator escaped an ASCII split and soBashwas never isolated as its own token — a fullwidth-paren permission specifierBash(git:*)(U+FF08/U+FF09) past the(split, and a fullwidth commaRead,Bash(U+FF0C) past the,split — both fixed by folding the whole value before any split — plus two byte-witness false-positives (atools:line inside adescription:block scalar, and Unicode line breaks NEL/LS/PS that YAML counts butsplit("\n")does not, both fixed by anchoring the witness to the composed key's byte offset); each closure carries a witness. The round-12 pass established the separator-class fold as complete: an exhaustive first-party scan of NFKC-stable alternate separators (ideographic/Arabic commas U+3001/U+060C, division/fraction slashes, semicolons) confirmed none can isolate a bareBashtoken for any consumer — they do not fold to the ASCII,/(the split (or a normalizing consumer) honors, soRead、Bashstays one non-Bashtoken everywhere — plus a scan confirming no codepoint NFKC-decomposes INTOashand no non-Cfcodepoint folds to empty (no token-merging attack); this boundary is pinned by a documenting non-bug test so the separator set is not later over-broadened into false positives. Plus the one-line allowlist mention the #521 review flagged as absent:docs/PERFORMANCE.md/docs/PERFORMANCE.zh-TW.md§ plugin agents and the SessionStart announce script's plugin-agents line. - Machine-readable degradation registry + omission reason-provenance (#511 Part A). New
shared/contracts/degradation_registry.json: a six-row auditable INDEX of every graceful-degradation mechanism (citation resolver outage →unreachable/unresolvable; contamination-signal API degradation → omit-field; VLM absence → skipped/PASS WITH NOTES; submission-package incompleteness →not_checked/exit 3; cross-model absence → warn-and-continue with required disclosure; non-SR compliance warn-cap — the one legitimate severity cap, because compliance already has a native block>warn>info scale). Each row records failure class → emitted degraded state → diagnostic marker → downstream consumer → terminal-policy effect → the per-mechanism authority as a verbatim CONTENT anchor (line numbers forbidden — the issue's own line refs had drifted by implementation time) → the tests/lints/schemas pinning the behavior. The registry indexes, it never re-authors: semantics stay in each authority file, and newscripts/check_degradation_registry.py(31 mutation tests, spec-consistency + pytest-manifest wired) fails CI when any anchor or pin stops resolving. No score caps — rows use native semantics per the issue's explicit rejection. Plus the one live weakness closed:literature_corpus_entry.schema.jsongains optionalcontamination_signal_omissions(closed enumapi_degradedonly; derivable omissions — manual exemption, no-arxiv-id skip — deliberately not recordable), guarded by manual-entry forbid + per-key mutual exclusion withcontamination_signals+ arxiv-requires-id rules, and written on BOTH paths:bibliography_agentat ingest (sha256 baseline updated per the F2 procedure) and the backfill layer — newcontamination_signals.build_signals_with_omissions()(manual checked upstream so aNonefrom the resolver means exactly "degraded";build_signals_objectbecomes an equivalence wrapper) + idempotentrecord/clearhelpers, with both migration tools recording the omission once on a degraded lookup and clearing it when a later run computes the signal (recovery + idempotency + dry-run tests). A degraded lookup is no longer indistinguishable from "never computed". Advisory-only: R-L3-2-C k/k_max counting is unchanged. Design:docs/design/2026-07-15-511-degradation-registry-design.md. Closes #511. - Transport-fixture integration test for the citation gate (#511 Part B). New
scripts/test_transport_fixture_citation_gate.py+ checked-in redacted raw API bodies underscripts/fixtures/transport_bodies/(success / miss / error per resolver, all metadata synthetic —10.5555example-prefix DOI, fictitious arXiv ID). A URL-dispatch fake aturllib.request.urlopen(any unrouted URL fails the test) feeds the bodies through the four ACTUAL client implementations (crossref_client/openalex_client/semantic_scholar_client/arxiv_client) intoverification_gate.verify_citation, pinning the gate's 3-class verdict end-to-end: all-hit →true(ID fast path, one request per resolver), fabricated IDs (404 + real empty-result bodies through the title fallback) →false, total 5xx outage →unresolvableneververified. Closes the gap where the per-client suites pin one client at a time and the citation eval replays already-reducedresolver_outcomesauthored from the same reducer rule — client parsing was never integration-tested. Wired into the unified pytest manifest. Deliberately NOT a product--offlinemode and NOT a replication of the 51-case gold set (scoped out in #511 as inflation). Part A (degradation registry) remains open. - Executable sprint-contract panel checker (#510). New
scripts/check_panel_synthesis.pyrecomputes both v3.6.2 decision layers from the primary artifacts — Layer 1: each reviewer's own scores → declared fired conditions → own## Editorial Decision; Layer 2: the panel scoring matrix → quantifier thresholds → precedence → the synthesizer's declaredfired_conditions:set AND emitted decision — and fails on mismatch (self-consistency gate on LLM output, not a correctness gate). Exit codes are classified by artifact source (1 synthesis-layer → retry synthesizer once; 2 contract/infra → abort; 3 reviewer-report → unusable reviewer ⇒[PANEL-SHRUNK]; precedence 2>3>1), with a--layer1-onlymode for per-reviewer verification at Phase-2 lint time. The §9 expression vocabulary is implemented as a closed grammar (unknown forms, orphan dimension literals, and empty priority scopes all fail closed — no vacuous truth), report/synthesis output grammars are pinned in the five reviewer agents + synthesizer prompts (role line,score:/fired:lines, exactly-once decision line, fenced-code stripped, duplicate sections rejected), and cardinality is guarded against duplicate paths/byte-identical reports/role-set forgery. Ships with protocol §8.1 runtime wiring, the zero-fired accept-grade fallback aligned across all surfaces (derived from the contract's F0 action, never hardcoded), and the majority quantifier corrected from a⌈N/2⌉+1transcription error to simple majority⌊N/2⌋+1(evidence chain in #531 — every concrete threshold in the v3.6.2 design says 3/5, 2/3). Design:docs/design/2026-07-15-510-panel-synthesis-checker-design.md; cross-model design review (gpt-5.6-sol xhigh) drove the grammar pinning, fired-set verification, and exit-code classification.
[3.16.0] - 2026-07-12 — Model tiering, cross-model gate hardening, WP advisory sharpening
Added
-
Model tiering: judgment/execution split with two opt-in directions, default untouched (#517). New
ARS_MODEL_TIERINGenv switch and canonicalshared/model_tiering.md, motivated by Lance Martin's "Cost effective harnesses with Fable" (2026-07-10; advisor-checkpoint configs measured ~90% of frontier-solo quality at ~34% of token cost, with delegation paying only when workers absorb enough tokens to offset per-handoff coordination cost). Default (unset): byte-equivalent pre-#517 behavior — every agent staysmodel: inherit(same opt-in philosophy asterminal_policies).economy(frontier-tier session): the 13 execution-type agents dispatch exactly one tier below the session model, floor Opus-class, never Sonnet (academic-prose tolerance is untested; the article's numbers came from ML tuning) —draft_writerexplicitly flagged as the highest-savings / most quality-sensitive downgrade point.quality-boost(below-frontier session): the judgment-type agents dispatched at the Stage 2.5/4.5 integrity gates and the final-review surfaces step up to the frontier tier; nothing is ever downgraded. Both directions have explicit no-op conditions with a one-line announcement; unknown values warn once and behave as unset (fail-open to the safe default). Tiers are relative positions, never hard-pinned model ids (the v3.7.0opuscommand floor retired in the Fable 5 harness pass is the cited precedent). Because many ARS roles execute inline today (no per-role model choice), the mechanism is dispatch-shaped: when a direction applies to a role, the session dispatches it as a subagent pinned to the target tier — inline roles included — and falls open to inline-on-session-model with a one-line announcement where subagent dispatch is impossible;docs/PERFORMANCE.md's (en/zh-TW) "no separate model routing layer" sentence is reconciled with a pointer. The frozen 39-agent classification (26 judgment / 13 execution — the issue header's 25/12 arithmetic corrected, membership unchanged) lives twice on purpose: a machine-readablescripts/model_tiering_manifest.jsonand the canonical doc's table, pinned to each other AND to the*_agent.mdfiles on disk by newscripts/check_model_tiering.py— set equality with a repo-wide stray sweep (a new skill dir can't smuggle unclassified agents), tier-enum + duplicate checks, and EXACT per-(tier, skill) token-set comparison against the doc table (missing/extra/duplicate tokens, per-row counts, duplicate rows all fail; 15 mutation tests; wired into spec-consistency.yml + the local pytest manifest). Prompt-caching guidance (when a direction is active, route repeated same-stage calls to the SAME worker so its cache accumulates) documented in the canonical doc and each of the fourSKILL.mdfiles' compact## Model Tiering (#517, optional)dispatch block — scoped so the unset default stays byte-equivalent, dispatch shapes included. No agent-file edits (the sha256-lockedbibliography_agent.mduntouched), no schema change, no hook. Spec:docs/design/2026-07-12-517-model-tiering-spec.md. -
Cross-model gate hardening: risk-stratified sampling, blind disagreement checkpoints, id-status allowlist, promotion bakeoff (#518). Four upgrades to
shared/cross_model_verification.mdand its consumers, from a 2026-07-11 cross-model consult (gpt-5.6-sol, xhigh). (1) The integrity-gate cross-model sample moves from uniform random 30% (min 5, max 15) to risk stratification across four mutually-exclusive tiers (highest-precedence tier wins, one verification per reference): HIGH-IMPACT references (headline conclusions, numerical claims, causal claims, methods-critical, disputed) verified 100% uncapped at both gates; a 10% RANDOM sample of the remainder at Stage 2.5 (round-up, min 3, max 10); at Stage 4.5, NEW-CHANGED references (behind claims new or changed since 2.5) verified 100% uncapped plus a 10% CONTROL sample of the unchanged remainder replacing RANDOM — verification budget concentrates where the paper's weight rests, and the results table gains a Tier column (integrity_verification_agentupdated in lockstep). (2) The two irreversible checkpoints — research-design freeze (research_architect_agent) and final editorial decision (editorial_synthesizer_agent) — gain optional blind disagreement checks: the primary commits its own decision in the same structured form first (the architect in a new Design-Freeze Checkpoint Audit blueprint section; the synthesizer's is its emitted decision), the cross-model then produces an independent structured decision from the same inputs (never seeing the primary's decision — same anchoring-prevention rule as the integrity samples; the editorial input is the panel'spanel_sizeN usable reviewer cards, never a hardcoded five), differing enum values trigger a targeted rebuttal addressing each cross-model driver against the evidence on file, and divergence escalates to the user — a review trigger, never a vote, never averaged; under a sprint contract the check runs strictly post-Step-3 against the mechanical protocol'seditorial_decisionand its drivers never enter the scoring matrix. (3) The "6th reviewer — Planned" section is retired, not deferred: the consult's counterproductive-conditions list (score averaging, role duplication, findings treated as confirmed defects, majority-vote false confidence, synthesizer context burn) matches ARS's documented anti-patterns one-for-one; the blind checkpoints are the replacement design, and the live mirrors (.claude/CLAUDE.md,shared/raise_framework.md, SETUP feature tables en/zh-TW) drop the "remains planned" claim. (4) The model-detection snippet separates "which provider endpoint" from "is this id known-good": newCROSS_MODEL_ID_STATUS=validated|provisional|unlistedannouncement with an explicit warning for unlisted first-party-prefix ids (gpt-made-upno longer passes silently) — routing itself is byte-identical, an unlisted id still takes the grounded route and never falls through to the ungrounded compatible branch. Plus a § Promotion Bakeoff operationalizing thegpt-5.6-solprovisional→validated criteria: a 30-reference paired same-day run (20 real / 10 fabricated; committed as a versioned, labeled, sha256-recorded fixture before any run counts; 3 repeats with ≥2/3 majority verdict, a 1–1–1 split scored conservatively against the model that produced it) against five non-inferiority thresholds (grounded-search completion, mismatch recall, false-disagreement rate, jq-guard shape stability — a hard requirement, p95 latency), entry-gated byscripts/cross_model_smoke_test.sh, results recorded underaudits/either way — and a deliberate two-step outcome: a full pass makes the idvalidated, while the recommended default flips only with an additionally stated superiority or operational-benefit reason. Spec:docs/design/2026-07-12-518-cross-model-gate-hardening-spec.md. Related: #517 (model tiering) will reference the checkpoint surfaces added here. -
GPT-5.6 Sol listed as provisional cross-model verifier + explicit reasoning-effort control (#515). OpenAI's
gpt-5.6-sol(released 2026-07-08) joins the canonical model table inshared/cross_model_verification.mdas provisional pending ARS validation — endpoint support (Responses API + hostedweb_search), the reasoning-effort enum (none|low|medium|high|xhigh|max, defaultmedium), and pricing (same standard rates as GPT-5.5; premium isreasoning: {mode: "pro"}on the standard slug, NOT a-promodel id) were verified first-party against OpenAI's model page and GPT-5.6 guide, but ARS-specific behavior (grounded-search completion rate, citation-mismatch recall, false-disagreement rate, jq-guard response-shape stability, p95 latency) has no operating history, so GPT-5.5 stays the recommended default. The documented OpenAI Responses call pattern gains an explicit reasoning-effort control via newARS_CROSS_MODEL_REASONING_EFFORT— set, it is passed asreasoning.effortso the run's effort is visible and reproducible; unset, the field is omitted entirely and each model's own provider default applies (forcing one value would silently change behavior for existinggpt-5.5-pro/legacy setups, a codex-review P2) — and both SETUP quick-setup blocks (en/zh-TW, parity-linted) mirror the new example lines. Newscripts/cross_model_smoke_test.sh— a live, manual (not CI; needsOPENAI_API_KEY) promotion gate asserting HTTP 2xx, a completedweb_search_call, a single verdict token, VERIFIED-carries-source, model echo, and effort echo — is the prerequisite for ever flipping the default to Sol. The canonical doc's Chat-Completions-web-search claim was re-verified against OpenAI's current web-search guide and deliberately left unchanged (a cross-model review suggested it was stale; first-party docs confirm it is still accurate). -
WP advisory held-out miss-rate measurement + acceptance set, Part 2 (#501; direction from the PR #468 review thread, @brycewang-stanford). New
evals/heldout/rq_framing_offlist/: a 48-item held-out set (32 shells outside the WP01-WP20 surface forms and the four in-prompt examples — 23 family variants + 9 off-list — plus 16 domain-native hard negatives), generated cross-model (gpt-5.6-sol), shell items regex-filtered (four negatives intentionally carry listed surface substrings as hard-negative material), dual-annotated with documented drops, English-only per the #468 caveat. Scored against the runtime LLM judge (isolatedclaude-sonnet-5sub-agents, verbatim advisory section only, pre-#503 vs post-#503 variants, two post replicates): overall miss rate 0.34-0.38 (above the inherited FNR < 0.30 line), concentrated in decorated compound-title off-list shells (7/9 missed, stable across replicates; judges read generic topical nouns as the exemption's "specific mechanism"), family-variant generalization under the line post-#503 (0.17-0.22), false-fire 0/16 on both variants. Verdict: miss rate HIGH → per #501's decision rule the set is now the acceptance test for any future advisory change (protocol in the set's README; report ataudits/rq-advisory-heldout-measurement-2026-07-11.md). Deliberately outsideevals/gold/(LLM-judged; notarget.entrypoint, labels not reducer-reproducible). Closes #501; the measured off-list gap is tracked as a follow-up design issue. -
Introduction & Title Rhetoric reference (#500; gap surfaced by PR #485, @lorenzo392). New
academic-paper/references/intro_title_rhetoric_guide.md: CARS three-move Introduction guidance (territory / niche / occupation, with a licensed-gap rule, the universal-negative trap, purpose-sentence discipline, and a common-failures table) plus a title-crafting section (anatomy, four title types with claim-level cautions, checklist, weak→stronger worked examples, and a cross-check against the WP06/WP17/WP18 wording-pattern shells). Wired as adraft_writer_agentStep 1 setup checklist item;academic-paper/SKILL.mdFile Structure reference list updated (stale count 20 corrected to the actual 28). The declined PR #485 skill shape (new top-level skill) stays declined; this lands the two genuinely-uncovered content areas as a reference file per the maintainer response there. -
Korean trigger keywords + routing boundary fixtures (#452 PR 1; proposal and Korean boundary phrases by @devCharlotte, who also authored the native-reviewed Korean README in #469). All four
SKILL.mdfiles gain a**한국어**trigger-keyword line plus a conservative Korean subset in the frontmatterdescription— intent-specific compounds only (논문 심사 / 논문 수정 / 초록 작성 / 체계적 문헌고찰 / 연구부터 논문까지 …), deliberately avoiding the broad standalone terms the proposal flagged (연구, 논문, 작성, 검토). The key 수정-vs-심사 disambiguation lands as two new routing smoke-test fixtures (tests/fixtures/issue_133_routing/09_korean_revision_not_review/,10_korean_review_not_revision/) using the proposer's native-authored phrases; all six boundary cases from the issue pass a routing smoke test on the current primary model (6/6, recorded in the PR). No changes to agents, IRON RULEs, integrity protocols, schemas, modes, or output-language behavior. Closes #452 (the Korean README half shipped in #469/#471).
Changed
-
WP advisory exemption sharpening — decorated title-form shells now caught (#505; direction from the #501 Part 2 measurement). The exemption clause in both
socratic_mentor_agent.mdfiles (deep-research + academic-paper) is narrowed: it now requires a named or operationalized specific (an actual instrument/scale name, a named theory/model/dataset/policy instrument, a named site or population, a specified causal pathway — through what mediator/condition/process A relates to B, not merely that it does — or a stated tension between two identified explanations), declares ordinary domain-flavored topic-label pairs swappable, and adds a decorated-compound-title rule (an evocative pre-colon phrase plus a generic "X and Y (in Z)" subtitle gains no specificity from the decoration — the noun-swap test applies to the part after the colon alone). This closes the failure mechanism the #501 Part 2 baseline measured: judges reading generic topical noun pairs as the exemption's "specific mechanism", which rescued 7/9 off-list shells (six decorated titles plus one interrogative). Measured against the held-out acceptance set per its README protocol in two rounds (initial wording, then a cross-model-review-driven refinement — demographic descriptors excluded from "named population", single-topic subtitles covered — re-measured from scratch; 2 replicates each, same judge model): overall miss 0.375/0.344 → 0.094 in all four post-#505 runs, off-list 0.778 → all 9 items fired in at least one final replicate (final rep2: 9/9), false-fire 0/16 preserved in every run (including the four hard negatives carrying listed surface substrings); no shell missed in both final replicates; on-list gold set unaffected (regex detector untouched, fnr=0/fpr=0). All #505 constraints held: WP table unextended, advisory stays non-blocking and surface-phrasing-only, sentinel contract (test_check_rq_framing_patterns.py) unchanged; the new in-prompt example strings were substring-checked against every held-out item (zero hits) so the set stays held out. Measurement JSONevals/heldout/rq_framing_offlist/measurement-2026-07-11-505.json+ reasoning excerpts appended; report ataudits/rq-advisory-505-exemption-sharpening-2026-07-11.md. Closes #505. -
Reviewer calibration protocol notes LLM-as-judge leniency direction (#484 → PR #506, merged).
academic-paper-reviewer/references/calibration_mode_protocol.mdgains a directional-prior subsection under "Failure cases this mode does NOT fix": when the simulated panel's output is read as a pass/fail signal, assume leniency relative to human expert review until your own calibration shows otherwise, anchored to FARS (Tang et al. 2026, arXiv:2606.31651 — automated reviewer mean 5.00 over 165 papers vs 3.23 paper-level mean from 282 human expert reviews over 140 papers; a descriptive ~1.8-point gap, and the automated score functioned only as a relative ranking). The direction is a working prior (heuristic extrapolation from one measured setup, default-until-measured); the magnitude is explicitly non-portable — never a correction factor or threshold change. Docs only; the panel remains advisory infrastructure behind human checkpoints. FARS added to References. -
WP advisory generalization, Part 1 (#501; direction from the PR #468 review thread, @brycewang-stanford). Both
socratic_mentor_agent.mdfiles (deep-research + academic-paper) now state that the WP01-WP20 table is illustrative, not exhaustive, and name the operative judgment: the noun-swap test (phrasing is shell-like when it survives swapping its nouns for any other field's nouns). Off-list shells that clearly survive the swap may fire the advisory at the same high-confidence bar; domain-native phrasing that names a mechanism, instrument, site, or tension does not survive it and must not trigger. Advisory stays non-blocking and surface-phrasing-only; sentinel contract unchanged (test_check_rq_framing_patterns.py). Part 2 (held-out miss-rate measurement) landed separately — see the Added entry above. -
API-first retrieval refresh: OpenAlex API-key auth, budget-aware 429 handling, arXiv ToU-aligned backoff (#495; proposed by @pikaqiu2333). OpenAlex's current developer docs are API-key-first (freemium daily budget; the polite pool is no longer documented):
scripts/openalex_client.pygainsOPENALEX_API_KEYsupport (query-param auth; either credential selects the authenticated 10 req/s pacing tier,OPENALEX_POLITE_EMAILstays as legacy compat), distinguishes daily-budget-exhausted 429s (X-RateLimit-Remaining: 0→ raiseOpenAlexUnavailableimmediately — the budget refills at midnight UTC, so an in-process retry cannot succeed) from transient burst 429s (exponential backoff 2s → 4s → 8s per OpenAlex's documented guidance), and strips the query string from refusal-path error messages so the key never lands in logs (scripts/crossref_client.pygets the same redaction — its query string carries the polite-poolmailtoemail).scripts/arxiv_client.py's 429 backoff moves from the shared 2s constant to the 3s ToU pacing floor (arXiv's Terms of Use ask for at most one request every three seconds — a sub-3s retry would itself violate the pacing the 429 enforces; verified verbatim against the ToU page). Both protocol docs (deep-research/references/openalex_api_protocol.md,arxiv_api_protocol.md) updated in lockstep, plus an explicit retrieval-order boundary in each: structured APIs are the primary channel, browser/WebFetch page inspection is a bounded first-party fallback whose output is data-not-instructions (shared/ground_truth_isolation_pattern.md§2A), and browser retrieval is never a rate-limit bypass (no parallel browsing, no bulk PDF harvesting, no multi-machine fan-out). 5 new client tests; the two 429-behavior tests updated to pin the new backoff shapes.
Docs
- Third-party directory:
THIRD_PARTY.md+ README pointer (#497 → #498). Community-submitted third-party projects that wrap or host ARS get a low-bar directory listing (visible ARS attribution + faithful description required) that is explicitly separate from endorsement — entries are not reviewed, tested, or verified by the maintainer, and the page says so up front. ClawMama listed as the first entry per issue #497. The README install section gains a neutral, non-recommending pointer to the page; the main install flow still links only to maintainer-verified paths. A separate "Getting officially recognized" track is documented for projects that want actual review.
[3.15.0] - 2026-07-04 — Release-gate hardening, prompt-debt retirement round 2, defrift locks
Added
-
Phase Boundary enforcement-sentence defrift lock + SETUP cross-model parity lint + local-manifest gap closure (#491 → #492).
scripts/check_v3_9_2_phase_boundary.pygains invariant 4: every Bucket A block's enforcement paragraph must carry the canonical sentence verbatim (CANONICAL_ENFORCEMENT, version-matched v3.9.2/v3.9.4; per-file tails after the sentence stay free). Motivation: the pre-#294 copy of that sentence sat factually stale across 29 surfaces for a month while this very lint iterated those exact blocks pinning only the marker prefix — the drift class now fails CI, with 7 mutation tests including the literal stale sentence as the regression case. Newscripts/check_setup_cross_model_parity.py(+9 tests, wired into spec-consistency.yml) pins thedocs/SETUP.md/docs/SETUP.zh-TW.mdARS_CROSS_MODELexamples to each other and to the canonical model tables inshared/cross_model_verification.md— membership is scoped to "API ID" table columns after a codex-review P1 (legacy ids backticked in the accepted-for-existing-setups note must not count as recommended) and excludes glob prefix tokens per the re-review P2, failing closed when extraction finds nothing. The local pytest manifest gainstest_check_v3_9_4_temporal_verification.py(58 → 60 entries): its CI-only coverage is what made #490 local-green/CI-red on the bibliography_agent sha256 F2 ownership pin.tests/test_helpers.pygainsload_module_from_path(); the enforcement-sentence mechanism is cross-referenced with thefirm_rules.mdcanonical-block framework in both directions (deliberately separate: factual status prose vs behavioral firm rules). -
Release-doc alignment gaps closed in the version-consistency lint — three new invariants + a tag-time gate (#487). A 2026-07-03 audit of
scripts/check_version_consistency.pyagainst the suite-level release-doc discipline found the #357/#345 extensions had already closed the docs-version-ceiling, zh-TW-parity, and package-metadata invariants, but four sub-items had no machine check. Now enforced: invariant 9 — the latest CHANGELOG entry's body must be ≥ 100 characters (a bare heading is not release notes; historical entries stay exempt); invariant 10 —.claude/CLAUDE.md"Last Updated" must lie within ±7 days of the latest CHANGELOG entry's date (the CHANGELOG date is the baseline, never "today", so re-running the lint later cannot flip the result); invariant 11 — the newest## vX.Y… Key Additionsheading must match the suite version at the heading's own precision. New--tagmode plus atag-version-match.ymlworkflow (push: tags: v*) require the pushed tag name to equal the suite version — the one comparison nothing performed at tag time — and re-run the full lint at the moment of tagging. A fifth candidate check ("every version cited in docs/ must exist in CHANGELOG") was evaluated and rejected: ARS's spec-version naming culture (e.g. the v3.6.6-design-shipped-as-v3.6.8 note) plus component versions make it structurally unenforceable (600+ legitimate references would false-flag). The latest-entry body ends at the next release heading via a fence-aware line scan, so a## [...]line inside a fenced code block in the release notes can't truncate the body (even one shaped exactly like a real entry); CHANGELOG / Last-Updated dates are right-bounded and shape-gated to strictYYYY-MM-DDthen parsed fail-closed, so a prefix like2026-04-222isn't mis-read as2026-04-22and an impossible date like2026-02-30reports a lint error instead of crashing. 20 new mutation tests (33 → 53), including the tag-gate hard-fail when the suite version is unreadable (a garbage tag co-occurring with a broken.claude/CLAUDE.mdmust not slip through the one gate meant to catch it). -
Command-invariants CI gate — announce list machine-checked against the actual command inventory (#486). Vendors release-discipline-toolkit v0.3.0 under
tools/release-discipline/and adds.command-invariants.toml+ acommand-invariants.ymlworkflow. The lint discoverscommands/*.md(16 today) and assertsscripts/announce-ars-loaded.shlists exactly that set — missing command, stale extra, and the hardcodedSlash commands (N)count all fail CI — plus a version-lockstep check pinning.claude-plugin/plugin.jsonto the newest CHANGELOG release entry. Motivated by the announce list drifting twice (PR #193: 10 listed vs 12 actual; this change: 14 vs 16); the validator was verified against the live drift before installation (caught exactly the two missing commands, zero false positives). -
CHANGELOG-covers-merges pre-tag release gate (#483).
scripts/check_changelog_covers_merges.pymachine-checks that every release-worthy commit merged since the previous release tag is documented inCHANGELOG.mdabove the previous release's section ([Unreleased] or a release-prep-promoted newer section — spec §0.2), with conventional-prefix exemptions (chore/test/ci/build,docs(design)/docs(superpowers)/docs(release)/docs(i18n)) and fail-closed behavior on missing tags/headings. Newchangelog-covers-merges.ymlworkflow gates everyrelease/**-headed PR into main; the CONTRIBUTING manual step covers branchless tag flows. Would have blocked the 16-entry Unreleased backlog rolled up in 3.14.0. 47 tests, registered in the CI pytest manifest.
Changed
- Prompt-debt retirement round 2: the 17 never-deep-scanned agents (#489 → #490). Continuation of the #476 track — the 2026-07 audit covered only the 22 Bucket A agents and explicitly deferred the rest. This pass deep-scanned the remaining 17 (all 5 academic-pipeline agents, 5 deep-research, 5 academic-paper, field_analyst, shared compliance_agent) plus 2 new commands and 3 carry-forward re-checks, via 4 parallel sub-agent batches + an independent codex cross-model challenge (12/12 excerpts verified; +1 finding, +1 scope amendment, +2 priority upgrades). 13 findings — 2 P1, 11 P2; the user rejected 2 (recorded so the next audit does not resurface them). The two P1s were live self-contradictions: both
socratic_mentoragents carried stale pre-v3.0 "suggest quitting after 15 rounds" copies against a documented typical run of 20-30 rounds — auto-end machinery is now a single authority per file (threshold unified at 30), with stagnation/max-round constants single-sourced. The stale "prompt-level only / deterministic hook deferred to v3.10 #134" enforcement sentence (false since PR #294 shipped the write-scope guard) was rewritten at 23 agents + 4 SKILL.md + 2 mirrors, then tightened 39 → 35 words in the /simplify pass. Few-shot and duplicated-process scaffolds trimmed across 7 agents (question-template tables, citation-form examples, resume exemplars, triple-written dispatch rules incl. a missingview progressrow restored to the authoritative table); SETUP model-lineup drift fixed in both languages. The 2026-06-10 F-007 deferred negative-framing item closes as verified, no rewrite needed — zero safely-reframeable negatives across the three top-density files, confirmed by both tracks.bibliography_agent's F2 baseline hash updated per the lint's documented procedure. Audit report:audits/harness-retirement-2026-07-04.md. Net −181 deleted / −98 substantive prompt lines excluding the report.
Fixed
-
SessionStart announce updated to the full 16-command set.
/ars-3wand/ars-rebuttal-auditwere missing from both the resume short-form list and the startup long-form listing, and the stated count said 14 — commands shipped after the list was last hand-synced were silently invisible at session start. Both forms now list all 16 with the correct count (announce script# versionbumped 1.0.0 → 1.1.0); the new command-invariants gate prevents recurrence. -
DOI badge served from shields.io (#482). Zenodo's badge endpoint rate-limits GitHub's camo image proxy (HTTP 429), intermittently rendering the README DOI badge as a broken image even though the DOI resolves fine. The badge image is now a static shields.io badge in all five READMEs; the link target stays the concept DOI (
10.5281/zenodo.20696614), which always resolves to the latest version.
[3.14.0] - 2026-07-02 — Claude Science importability, eval-comment rendering, prompt-debt retirement
Added
- Claude Science / GitHub-importer compatibility — explicit skill paths in the marketplace manifest (#480). The plugin entry in
.claude-plugin/marketplace.jsonnow declares"skills": ["./academic-paper", "./academic-paper-reviewer", "./academic-pipeline", "./deep-research"]. The repo-rootskills/directory holds symlinks, which GitHub-API consumers cannot traverse — Claude Science's "Import from GitHub" previously reported "Not importable: no skills/ dirs with SKILL.md" and found zero skills (same failure class as the #413agents/materialization). Per the Claude Code plugins reference, for a marketplace entry whosesourceis the marketplace root the listed paths REPLACE the defaultskills/scan, so Claude Code installs keep loading the same four skills from their real paths — no content change, no double-loading. Verified end-to-end on Claude Science (4/4 skills detected at the merge commit). New README / SETUP.md guidance documents the import flow and its limits: imported skills carry the methodology (research / writing / review protocols); Claude Code-specific machinery (slash commands, hooks, subagent orchestration) does not transfer, and imports are point-in-time snapshots that require re-import after ARS updates. - Eval-harness PR comments render as a verdict + per-task table (#479). The eval-harness workflow previously pasted the whole
eval_report.jsoninto every PR comment as one raw fenced block. New display-layer modulescripts/render_eval_comment.pyemits a one-line verdict (✅ N/M measured tasks passed · K pending (not wired)), a per-task markdown table (metric / value / threshold / result; pending tasks render—placeholders), and folds the full JSON into<details>. The row verdict mirrors the gate's failure signal (aggregate AND per-class, #328), with a test pinning agreement withscripts._eval_threshold_gateso the mirror drifts loudly in CI instead of rendering green on a blocked run; table cells escape pipes and all line boundaries so manifest-sourced strings cannot break or spoof rows (codex review finding, fixed in-PR). Evaluation logic is untouched:run_evals, the threshold gate, and the[eval-regression-acknowledged]ack contract are byte-identical. 13 unit tests, registered in the CI pytest manifest; the workflow-honesty test suite gains a pin so the comment cannot silently regress to a raw dump.
Changed
- Prompt-debt retirement: expired writing-harness scaffolds removed from four Bucket A agents (#476 → #478, net −111 lines). The 2026-07 harness-retirement audit (#477, report under
audits/) classified prompt scaffolds that encoded assumptions about what prior models could not do. The five P2 findings plus one hands-on finding were applied toabstract_bilingual_agent,citation_compliance_agent,draft_writer_agent(academic-paper) andresearch_question_agent(deep-research). Three-track verification: sub-agent audit + independent codex review (two fabricated back-references caught and fixed; re-review clean) + eval harness (citation_extraction and rq_framing_patterns both at 100%). - Platform Port Reminder CI (#473). A remind-don't-block workflow surfaces the "Platform ports (community-maintained only)" CONTRIBUTING.md policy when a PR adds a new top-level directory (motivated by PR #470, where a contributor could not have known the policy existed), plus a PR-template pointer.
Docs
- Native-reviewed Korean README (#469; contributor credit #471).
README.ko-KR.mdcontributed and translated by devCharlotte, with Contributors credit parity across the language READMEs. - GitHub Copilot repository instructions (#465).
.github/copilot-instructions.mdorients Copilot-based contributors to the repo's structure and conventions. - Permission-mode guidance (#464). Install docs now recommend Claude Code's
autopermission mode over Skip Permissions.
Rolled up from [Unreleased] (code landed before the v3.13.0 tag)
Provenance note: the entries below shipped in the repository between the v3.12.0 and v3.13.0 tags, but their changelog bullets had accumulated under
[Unreleased]across releases. They are versioned here so the release record is complete — no new code ships with these bullets in v3.14.0.
Added
- Diff/patch revision mode — Slice B revision-mode adoption (#89 Item 7, spec #390, sub-issue #424). The MVP ship-gate slice:
academic-paperrevision mode now runs anchorize → patch → deterministic apply → finalizer instead of full re-emission.draft_writer_agentgains the## Patch-Document Revision Emission (#390)contract (patch document as aphase6_*/revision_patch_round<N>.jsonsidecar — hashes copied from the block manifest, never computed;[PATCH-ESCALATION-REQUIRED:]pre-drafting escalation tag; retry-once; provisional Schema 8 items with mechanical fields left to the orchestrator).pipeline_orchestrator_agentgains## Revision-Round Patch Sequencing (#390)(five normative steps with a no-rewrite window between manifest generation and apply; two-layer escalation gate with the MANDATORY checkpoint wording; never auto-fallback to full re-emission; escalated rounds re-anchorize under a new ID generation and stampmode: full_reemission_escalated;preserved_ratiosurfaced next to the #389 round-trip count). Schema 8ResponseItemgains optionalchange_block_ids(orchestrator-populated from the apply report, §3.5 role split). New protocol docacademic-paper/references/revision_patch_protocol.md(exact Mode B commands, exit codes, apply report as a required re-review input, marker lifecycle). Two recorded ship decisions land as a spec §0 amendment with cross-model concurrence:touched_ratiothreshold = 0.6 (now the apply-script CLI default, strict>, 1.0 disables) and theinsert_afterheading-anchor exemption (anchoring on a heading no longer flags when the inserted text carries no headings; heading-bearing text still flags). §10 open items closed the verified way:formatter_agentgains## ARS Marker Stripping (#390)(all marker kinds stripped from converted final outputs only AFTER marker-dependent gates; working drafts keep markers) andword_count_conventions.mdgains the strip-<!--...-->-before-count rule (first-party check found NEITHER rule previously existed — the spec's "expectation" had nothing to point at); max single-opnew_textsize folded into the existing triggers (no separate cap). New lintscripts/check_390_revision_patch_discipline.py(8 invariants: writer/orchestrator/SKILL/Schema 8/protocol-doc/marker-rules block-scoped literals, threshold value lock, spec-example schema validation) + 30 mutation tests, wired intospec-consistency.yml+ the pytest manifest. - Diff/patch revision mode — Slice A deterministic toolchain (#89 Item 7, spec #390). First implementation slice of the DELEGATE-52 rank-1 item: the deterministic tools exist and are tested, zero prompts touched (Slice B wires revision-mode adoption). New shared parser
scripts/_block_parser.py(fail-closed §3.1 block segmentation: fence/heading/table/list/blockquote/text + skipped YAML frontmatter; setext underlines, line-initial raw-HTML openers, and footnote definitions rejected by name, never swallowed; duplicate-ID / orphan-marker / marker-stack rejection; read-side-only hash normalization). Newscripts/ars_anchorize_draft.py(script-owned<!--block:BNNNN-->stamping — the LLM never assigns IDs; idempotent and content-neutral; emits the block manifest<draft>.block-manifest.json, the ONLY legitimate hash source a patch may copy from). Newscripts/ars_apply_revision_patch.py(two-phase fail-closed apply: validate-everything-touch-nothing then byte-span splicing, so untouched blocks are byte-identical by construction; structural-shape triggers gated by--acknowledge-structural;touched_ratiorecorded in every report with the threshold VALUE deliberately deferred to Slice B; machine-verifiedpure_movepairs; atomic temp+rename writes; apply report withpreserved_ratiocounters). Two new schemas undershared/contracts/patch/(revision_patch.schema.json— theDOC-BODY-STARTbranch is the only legal hash-less op shape;block_manifest.schema.json). 86 new tests across three suites incl. the §8.3 byte-identity property test (seeded randomized patches; untouched blocks + marker lines + separator bytes asserted byte-equal), wired as 3 new CI pytest manifest entries. - Submission-package verifier Slice 4: terminality (#394 — closes the issue; all four slices landed). The opt-in blocking layer, shaped by a cross-model gate-1 plan review (1 P0 / 4 P1 / 2 P2, all adjudicated; the P0 resolved as maintainer Option B). New
terminal_policies.submission_packagekey (closed enum{advisory, strict}, per-key absence = advisory via the evaluator runtime convention — the citation_existence pattern; no JSON-Schemadefault). The §5.3 single-homed boundary is sharpened, not moved (Option B): the orchestrator stays the sole reader/selector of the policy and hands the resolved value down via the new--policyCLI flag; the script mechanically applies it — stampsheader.policy_slug(argparse default None: a flag-less standalone run is unevaluated, stamped null, and a null-stamped report never satisfies pipeline freshness — never "default advisory"), and understrictemits the terminal verdicts: a strict-eligiblefail→ stdout tokenTERMINAL-BLOCK policy=submission_package+ exit 1; else a strict-eligiblenot_checked→VERIFICATION-INCOMPLETE+ new exit 4 (fail-closed §5.2 — a missing parser/profile cannot waive the class the scholar opted into;not_applicablenever composes into either verdict, keyed on status not the eligibility bit). Terminal signals are the stdout tokens, never raw exit codes — exit 1 also carries nonterminal heuristic fails (gate-1 P1). New--check-freshnessmode (REQUIRES--policy): recomputes the package fingerprint + compares the stamped slug, mismatch/null/missing →STALE-REPORT+ new exit 5, no checks re-run, no writes. The fingerprint exclusion set grows to report +provenance_summary.md(gate-1 P1: the formatter appends the advisories section AFTER stamping — fingerprinting the advisory carrier would self-stale every evaluated report). Orchestrator gains the Stage 5 post-formatter Submission-Package Terminal Gate section (resolve-absence-to-advisory + always-explicit--policy, gate-on-tokens, fix loop bounded 2 rounds then surface, strict-needs-a-venue-profile remediation stated plainly, freshness-before-reuse, recompute-each-pass C-V6(h) mirror); formatter gains the Submission Package Advisoriesprovenance_summary.mdsection (mandatory and non-empty iff any fail/warn/not_checked under advisory; stamp-only Invariant 13 untouched). Newscripts/check_394_submission_policy.py(5 invariants) + 26-test companion — invariant 4 is an AST single-homed guard (Subscript/.getaccess ofterminal_policies), not a literal grep, because the verifier's docstring legitimately says the word (gate-1 P2). 17 new verifier tests (79 in the verifier file; 105 total with the lint companion) incl. anevaluate_policydecision-table unit pin (the advisory/strict divergence lives inside the evaluator, not at the call site), byte-equivalence by before/after hashes, and three killed mutations (eligibility gate, fingerprint exclusion, null-freshness reason token). Gate-2 cross-model diff review (2 P1 + 1 P2, all adopted) + an independent context-reviewer pass over the two prompt sections (2 P1 + 4 P2, all adopted): a FRESH report now re-emits its policy verdict on--check-freshness(same token + exit semantics as a live run — a recorded terminal verdict can never evaporate across a resume), the report header gainsinputs_fingerprintover venue-profile/passport/join-map bytes (a report produced under one venue profile is STALE under another; legacy reports without the field never read fresh), the v3.10policy_hashmarker stamp is scoped to CITATION-TIME keys (submission_packagenever stamps markers — a package-only strict passport no longer forces marker re-finalization or stale-refuses legacy markers; oracle + test updated), the orchestrator gate's advisory path now explicitly re-dispatches the formatter in append-only mode (the advisories section had no named writer), TERMINAL-BLOCK's stdout-vs-marker channel reuse is disambiguated in place (thepolicy=value is the discriminator), the fix-loop round is defined (dispatch formatter → re-run verifier; never a 3rd), VERIFICATION-INCOMPLETE remediation is routed away from the formatter fix loop (a missing profile is not formatter-fixable), token matching is pinned to line-prefix, and the freshnesspolicy_mismatchline reprs the stamped slug (a forged report cannot inject a fake token line into stdout). A final confirmation round (2 P2 + 1 P3, all adopted) added the reuse-side roster guard (a hand-editedchecks: []report isSTALE-REPORT reason=roster_mismatch, never a clean re-evaluation — the report file is outside the package fingerprint, so content validation must not be skipped on reuse),--join-mapto the orchestrator's live verifier command (live and freshness invocations must carry the same input set or the inputs fingerprint can never match), and direct-value enum comparison in lint invariant 5 (the string "None" must not pass for JSON null). - Submission-package verifier Slice 3: Family A blind-review residue scan + Family D assessment (#394). The high-embarrassment-cost class. Trigger is presence-or-declaration (§3.1): an anonymized variant in the package (filename stem token
anonymized/blind/…) or a declaredblind_review: double— untriggered packages report the newnot_applicablestatus (additive report-schema enum), visibly distinct fromnot_checkedso a single-blind package is not condemned to exit 3 forever. Checks: A1 PDF/Author+ XMPdc:creatorviapypdf(the only new parser dependency;defusedxmlis additionally declared in requirements-dev as XML-bomb hardening with a stdlib fallback; pypdf absent →NOT-CHECKED(parser unavailable), never folded into pass per §1.4), A2/A3 DOCX metadata + tracked-changes/comment authors read RAW from the zip parts via stdlibzipfile+ XML (defusedxmlhardening when available) — a refinement over the plannedpython-docx: closer to the §1.3 artifact≠rendered-view premise and the DOCX residue class has no missing-parser hole at all, A4 acknowledgments section in the blind variant (deterministic signal, strict-eligible ONLY when the profile declares the newacknowledgments_forbidden_in_blind: true— the §3.1 load-bearing two-axes rule, enforced via a downward-only eligibility override), A5 self-citation phrasing (heuristic by class; ships a first-party zh-TW draft list per §10 item 1; curated by the maintainer 2026-06-10, adding 本文作者先前), A6 author-name tokens from the non-anonymized artifacts' metadata appearing in package filenames (heuristic; the metadata-source originals themselves are exempt), A7 declared-double-blind with no anonymized variant = fail (the most basic residue: the blind version is missing). Family D ships nothing: the slice-3 deliverable is the adjudication-ready assessment (docs/design/2026-06-10-394-family-d-repro-lock-assessment.md, recommending no-check with the B4required_sectionsescape hatch — therepro_lockgates-don't-read-it boundary stands untouched; adjudicated Option 2 by the maintainer 2026-06-10). 16 new tests (55 total; corrupt-docx honesty, parser-absence honesty, A4 conditional-strictness mutation killed). - Submission-package verifier Slice 2: scholar-declared venue profile + Family B limits checks (#394). Second slice of the #394 spec. New
shared/contracts/submission/venue_profile.schema.json(standalone, Invariant 11 pattern;declared_by: scholaris the only provenance value and the CLI refuses a profile without the stamp) and a--venue-profileflag enabling five deterministic, strict-eligible checks: B1 manuscript word count, B2 abstract word count (both ±2% tolerance per §3.2), B3 keyword count range, B4 required sections (case-insensitive heading containment), B5 reference-count ceiling against the same machine-readable reference list Family C uses. The no-inference rule is structural (R-L3-2-D mirror): without a profile every Family B check reportsNOT-CHECKED(no venue profile); a partially-declared profile runs what it can andNOT-CHECKEDs the rest with the undeclared field named; declared limits whose actuals cannot be located (no abstract section, no keywords line) reportNOT-CHECKEDwith the reason, never a guess. This also makes the exit-code semantics visibly honest: a profileless run that is otherwise green exits 3 ("passed what was checkable", §8), not 0. Word counting reuses the canonical whitespace-split convention (shared/references/word_count_conventions.md); LaTeX counting adjudicated per spec §10 item 4 as naive detex + whitespace-split, with the method and counted file declared in the report detail. Intake Step 3 gains the optional declared-values-only venue-profile follow-up (plan mode exempt, mirroring Steps 12/13) + a PCRVenue Profilerow. 14 new tests (39 total) incl. tolerance-boundary and provenance-gate mutations; fixturevenue_cleanpasses every B check againstprofiles/full.yaml, fixturevenue_violationsfails all five againstprofiles/tight.yaml(mutation discipline). Cross-model review (codex) adopted in full: schema-strict CLI validation (closed field set, bool≠int),word_count_scope: allcounts everything, and canonical-name manuscript selection withNOT-CHECKED(ambiguous manuscript)instead of silently picking the wordiest candidate. - Submission-package verifier Slice 1: CLI skeleton + Family C reference integrity (#394). First implementation slice of the 2026-06-10 #394 spec (slices are dependency-ordered; Family C ships first because it needs zero new parser dependencies). New
scripts/verify_submission_package.pystandalone CLI: point it at any output package directory and it runs the two-way reference-integrity set check (orphan in-text citation =fail, uncited reference entry =warn) and writessubmission_verification_report.jsonvalidating against the newshared/contracts/submission/submission_verification_report.schema.json. The deterministic joined marker path consumes<!--ref:slug-->markers plus a real prose-reference join — the passport'scitation_verification_summary[](--passport), an explicit scholar-supplied map (--join-map), or a package.bibvia the documented slug==citation_key identity relation — and markers with NO join source reportNOT-CHECKED(missing prose-reference join), never a guessed comparison (§3.3). Non-ARS / post-converted packages fall back to format-aware best-effort extraction (\cite{}for LaTeX, author-year regex for Markdown), heuristic-classed: the schema STRUCTURALLY forbidssignal_class: heuristic+strict_eligible: true, so the fallback path can never be promoted to blocking by any later policy slice. Report header carriesextraction_path,not_checked_count(incompleteness is never folded into pass, §1.4),package_fingerprint(spec §10 open item 3 adjudicated: the audit-snapshot manifest convention — byte-sortedpath:sha256lines, fingerprint = SHA-256 of the manifest, report file excluded), andpolicy_slug(always null until the slice-4 orchestrator hook; the script never readsterminal_policies, §5.3). Exit codes separate "all checked, pass" (0) from "passed what was checkable" (3) per §8. 19 tests + 7 fixture packages with mutation discipline (orphan / uncited / no-join failures proven to fire); CI-wired via the pytest manifest. Advisory-only: no manuscript byte changes, no pipeline hook yet (slice 4). #394 stays open until all slices land. - Design doc: deterministic submission-package verifier (#394, blindspot-audit F-5, design-first — no implementation yet).
docs/design/2026-06-10-394-submission-package-verifier-spec.mddesignsscripts/verify_submission_package.py, the script-layer backstop for the mechanical subset of the formatter's prompt-layer submission checklists (the #182 promotion pattern: LLM self-check → deterministic gate). Three check families in adjudicated priority order — blind-review residue (raw-structure scan: PDF/DOCX metadata authors, tracked-changes/comment authors, self-citation phrasing; artifact ≠ rendered view is a stated premise), venue-declared limits vs actuals (scholar-declaredvenue_profileschema, never inferred from the journal name — R-L3-2-D mirror), reference integrity (two-way set check; the deterministic path requires an explicit slug↔key join source or reportsNOT-CHECKED) — plus a stretch assessment ofrepro_lockpresence/shape checking that leaves the recorded gates-don't-read-it boundary untouched. Two load-bearing rules:signal_classandstrict_eligibleare separate axes (heuristic checks are structurally excluded from strict; A4's deterministic signal still isn't block-worthy because the de-anonymization judgment is the scholar's), and strict fails closed on incompleteness (VERIFICATION-INCOMPLETEwhen a strict-eligible check can't run — a missing parser must not silently waive the one class the user opted into blocking on). Terminality via a new additiveterminal_policies.submission_packagekey, evaluated by the orchestrator against a fingerprint+policy-slug-stamped report (package-level freshness guard — explicitly a new post-format gate, not the ref-marker stamp path). Cross-model reviewed (codex): 2 P1 (Family C join requirement; strict fail-open on NOT-CHECKED) + 4 P2 + 2 P3, all adopted. 4 dependency-ordered slices; advisory-only until slice 4. Real-use findingsrelease-notes convention documented; showcase refresh tracked (#395, blindspot-audit F-9). CONTRIBUTING.md gains a Release checklist section documenting the convention: a release's CHANGELOG entry includes aReal-use findingssubsection when issues were discovered through actual use on a real paper — one line per issue naming the run — so lived-experience provenance has a fixed, greppable home instead of being buried in spec prose (the v3.6.7 production chapter run surfaced 17 drift patterns and nothing structural recorded that provenance class; release motivation since v3.8 has been almost entirely external papers, which is itself a per-release signal worth seeing). Omitted when empty, never padded. The showcase refresh half of F-9 (no post-v2.7 end-to-end artifact set exists) is split to tracking issue #404, blocked on the next real paper with no artificial deadline per the adjudication.- POSITIONING records two non-goals; cross-paper workflow guide ships (#397, blindspot-audit F-1B/F-7). POSITIONING.md's recording discipline (five Rejected mechanisms with rationale) had two adjacent boundaries existing only as silence. A new "Recorded non-goals" section records both with the same boundary-plus-review-criterion treatment: post-publication lifecycle (own-paper citation tracking / errata / OA self-archiving — the front is research-to-publication;
monitoring_agentis unaffected since it alerts on cited literature, not the scholar's own output) and research-program-level state (no cross-paper claim registry / limitations memory / reviewer-history profile — the per-paper Material Passport stays the only state carrier, a deliberate anti-leakage consequence). The supported no-mechanism path for returning authors ships asdocs/cross-paper-workflow.md: (1) re-feed the prior paper's passport through the existing input port — a priorokis a head start, not a waiver, stamps re-derive under current policies; (2) bring prior limitations / unresolved reviewer points to RQ incubation as scholar-supplied Socratic input — ARS asks about your reading, never derives next-RQ candidates (Kong L2 cross-linked); (3) Claude Code assistant memory may serve as a personal reminder layer with the load-bearing caveat that ARS gates never read or trust it — the workflow must work identically on a machine with no memory at all. Documentation only; no schema, flags, or cross-run mechanism. - Intake Step 13: citation-verification level surfaced at the configuration interview (#392, blindspot-audit F-8, adjudicated "keep default, add a prompt so the user chooses"). The v3.11 citation-existence gate's
strictmode existed only in README prose and the v3.10/v3.11 specs — a QUICKSTART user had no signal the choice existed. The intake interview gains Step 13: "Citation verification: mark only (default) / strict", with one sentence of field guidance (strict suits DOI-dense fields; mark-only suits grey-literature-heavy fields). Byte-equivalence is load-bearing: astrictanswer seedsterminal_policies.citation_existence: stricton the Material Passport at the point it is materialized (the finalizer stays the sole policy evaluator); amark onlyor absent answer records the PCR row and writes nothing — per-key absence already means advisory (Invariant 7), so an unprompted run is byte-identical to pre-#392. Plan mode exempt (mirrors Step 12). No default changes anywhere. Guarded byscripts/check_392_citation_verification_intake.py(4 invariants, mutation-verified): Step 13 heading present (rename = fail-loud parse error), the no-handoff directive affirmatively reaches Step 13 (then Step 13— the exact #327 P1 orphaning that hit Step 12), PCR row present, and the advisory write-nothing rule + strict seeding target retained. 8 unit tests; wired intospec-consistency.yml+ the CI pytest manifest. - Layer-5 contribution-significance probes extended into plan mode and revision coaching (#393, blindspot-audit F-2, adjudicated shape 1). ARS quality machinery was defect-oriented end-to-end — a paper could pass every gate and still be a micro-extension, because the only constructive contribution coaching (socratic_mentor Layer 5, SIGNIFICANCE & CONTRIBUTION) lived at the RQ-incubation stage. Layer 5 now defines three later-stage anchored forms with stable IDs — L5-W1 "Ten years from now, what will citers say this paper established?", L5-W2 "Remove this paper from the literature — what is missing?", L5-W3 "If this paper succeeds, who would make different decisions as a result?" — and two later-stage surfaces consume them strictly by ID (the question text lives in Layer 5 and only there; a Layer-5 edit propagates by reference instead of forking — the cross-model review's P1 on a first draft that carried labeled copies): (1)
academic-paperplan mode gains Step 2.5 CONTRIBUTION SHARPENING between chapter negotiation and the argument stress test — the mentor asks the user to articulate the contribution their own Chapter Summaries claim, quoting only user-written text; if the user articulates one,[INSIGHT: contribution_claim]records it in the user's words, otherwise the open question is carried into Step 3, never filled in; (2)academic-paper-reviewerPhase 2.5 gains step 3, a contribution framing probe alongside the existing prioritization steps (5→6 steps; no external step-number references existed), anchored to what the manuscript already claims. The orchestrator's Stage 3→4 coaching sketch now explicitly defers to the reviewer SKILL.md six-step list as authoritative (net-zero line edit — the surface has 1 line of v3.6.7 budget headroom left). Boundary is load-bearing (Kong L2 verb test, per the #393 adjudication that rejected shapes 2–3): questions only — never propose, substitute, rank, expand, or select a contribution claim. Prose-layer only; no schema, scoring, or agent-roster change. Two review gates, all findings adopted: codex cross-model (1 P1 + 2 P2 + 1 P3 — ID-based single-sourcing, verb-test tightening, orchestrator deferral, conditional INSIGHT) and an independent context reviewer (1 P1 + 1 P2 — the repo has TWO same-namedsocratic_mentor_agent.mdfiles and plan mode dispatches the academic-paper variant, which had no Layer 5 and no Step 2.5 in its own flow, so the probe would never have fired: the agent prompt gains a Step 2.5 section referencing L5-W IDs by full path, the protocol's inline reference is path-disambiguated, and L5-W3's anchor permission is tightened to noun-phrase-swap-only). - Interaction-count budget surfacing + Context Hygiene dispatch discipline (#388; DELEGATE-52 Items 4+5 from #89). The two cheap, high-confidence follow-ups from the re-ranked DELEGATE-52 work order (arXiv:2604.15597), both prose-layer. Item 4: the v3.2 Budget Transparency block in
academic-pipeline/SKILL.mdnow also presents an interaction-count budget at pipeline start — the paper's core result is that long-horizon corruption compounds with document round-trips, not token volume, so the block enumerates the round-trip caps the pipeline already enforces (2 revision loops, 8+5 Socratic coaching rounds, the integrity fix→re-verify loop), states the worst-case total for the chosen mode, and reports the accumulated count at every stage checkpoint. Advisory only — the per-loop caps remain the enforcement layer; exceeding the stated worst case signals an uncovered loop and must be surfaced, never silently continued. Item 5:pipeline_orchestrator_agent.mdgains a Context Hygiene at dispatch block targeting the paper's distractor ablation (non-target documents in context measurably worsen outcomes): each handoff carries the receiving agent's declared inputs plus the Material Passport — never the accumulated pipeline as a convenience bundle; scratch output and superseded drafts do not ride forward (later stages read passport entries, not raw transcripts); supersession means dispatching the current version only, with prior versions retrievable through the versioned-artifact trail. The passport carry-forward obligations (claim/audit aggregates,experiment_intake_declaration,slr_lineage) are explicitly exempt — trimming applies to loose materials, never passport fields. Carries an epistemic-status line (dispatch-assembly discipline, not a runtime guarantee). Scope note recorded in #388: this lands the single-dispatch-point version; #89's per-downstream-agent sketch stays open under the parent. Items 1, 2, 7, 8, 9 remain tracked in #89. - Repository-hygiene CI (#151). A
repository-hygiene.ymlworkflow runs gitleaks over the full git history on every PR and main push, with the upstream default ruleset and no custom rules. The binary is version-pinned (8.30.1) and checksum-verified rather than pulled via the marketplace action (which needs an org license key; a pinned release binary keeps the supply chain auditable), and--redactkeeps any genuine hit out of public CI logs. The only local configuration is a false-positive allowlist (.gitleaks.toml): the 2026-06-10 baseline evaluation found 43 findings across 518 commits — every one a bibliographic citation key (Becht2019UMAP,vaswani2017-arxiv-v1,forthcoming2024) in eval gold tuples / adapter fixtures / version-family examples matching thegeneric-api-keyheuristic's key-shaped-string pattern, zero true positives — so those literature-identifier paths are allowlisted by path, never by rule edit (a new tuple under an allowlisted path needs no config touch). A seeded-credential mutation test confirms the configured scan still exits 1 on a real-pattern secret outside the allowlist (github-pathit) — and recorded a method caveat: a low-entropy dictionary-word seed does NOT clear the entropy gate, so a valid mutation check needs a real-pattern, high-entropy seed. Closes thedefer:v3.10evaluation with an adopt decision: all three decision criteria pass (no infra burden — public pinned binary; bounded maintenance surface — path entries only; post-allowlist FP rate 0 on the full history). - Field-norm severity calibration across the reviewer surfaces (#215, Kim et al. 2026 arXiv:2605.20668v1 §5.1 W1 + §F.3.4). Closes the paper's largest documented AI-reviewer failure class: a critique that is content-correct against a discipline-neutral standard but severity-miscalibrated because the reviewer lacks the subfield's accepted-practice prior (W1, n=54 — the CERN/LHCb reproducibility example), plus the sibling significance-boundary error from the "would addressing this change the core result?" formula (§F.3.4, 56 errors). Three reviewer surfaces are hardened, each at severity-assignment time and applying to every field-norm-dependent finding (not only CRITICAL):
domain_reviewer_agent.mdgains a Step 5 hard rule — a severity that rests on a field norm MUST be grounded in an external checkable source (a reference, venue/data policy, community standard, reporting guideline, or documented expert practice — explicitly not limited to a literature citation, and not model knowledge), else down-rate to advisory +[FIELD-NORM UNVERIFIED];devils_advocate_reviewer_agent.mdgains a 9th challenge dimension (the DA turns the lens on its own findings, since adversarial intensity amplifies an ungrounded norm into a CRITICAL) plus two required CRITICAL/MAJOR output columnsfield_norm_boundary+evidence_crossing_rationale;calibration_mode_protocol.mdgains a Phase 3.5 severity-miscalibration measurement + a low/med/high histogram in the Calibration Report — a signal the binary FNR/FPR matrix cannot show, where the classifier rates whether the reviewer supplied external grounding, not whether the norm is factually correct (guessing norm-correctness would repeat the very W1 failure under audit). A first-party regression fixture ships atevals/gold/field_norm_severity/(10 cases — 5 W1 field-norm-boundary + 5 §F.3.4 significance-boundary — extracted verbatim from the paper with section/example-ID + paper-citation-token + verbatim-anchor provenance; the SAR 11.7T case flaggedexception: truebecause experts concurred with the AI there). Because there is no deterministic detector for field-norm severity miscalibration, the fixture is a regression set, not a calibration set:scripts/check_field_norm_severity.pyvalidates data integrity + first-party provenance (no FNR/FPR ritual), andscripts/check_215_field_norm.pyasserts all three reviewer surfaces carry their blocks with block-scoped keyword checks (fence-aware) so a stray keyword cannot mask a missing rule. The two lints survived a three-pass cross-model (codex xhigh) review that drove finding count 4 → 2 → 0; every fix is mutation-tested (28 tests). Additive and backward-compatible; CI-wired via the spec-consistency workflow + pytest manifest. (#216 — the §F.3.6 reviewer-type parity half — was split out: it needs a different gold set of human-phrased vs AI-phrased paired cases.) - Surface-Form Parity self-check (#216, Kim et al. 2026 arXiv:2605.20668v1 §F.3.6). Closes the paper's reviewer-type asymmetry: an AI meta-reviewer applying two standards keyed off prose style — demanding literal precision from informal/vague (human-typical) wording, so it over-rejects correct concerns (29 of 41 correctness false negatives involved human reviewers), and crediting technical specificity in precise (AI-typical) wording, so it over-accepts incorrect ones (10 of 13 false positives involved AI reviewers). The root cause the paper names is a learned prior that specificity correlates with correctness. Key design call (after a codex xhigh consult): the hook is prose style, NOT the author label — so the mitigation is a Surface-Form Parity self-check (not "authorship parity"), and authorship is kept out of the runtime reviewer-item schema entirely (not merely audit-only). Two verdict-time surfaces carry the parity self-check (a codex review found the editorial synthesizer also arbitrates reviewer sub-claims and down-ranks "too vague" criticisms — exactly where §F.3.6 fires):
devils_advocate_reviewer_agent.mdgains a verdict-time parity self-check (a marker block, distinct from #215's severity-time gate) andeditorial_synthesizer_agent.mdgains a Step 1c arbitration-time check + a reworded "reduce weight if too vague" rule that fires only when vagueness makes a sub-claim unevaluable. The DA check: extract the checkable claim → judge it against the paper not the polish → do not down-rate informal/vague wording unless ambiguity changes truth conditions → do not credit technical specificity without checking → run the opposite-style counterfactual and revise / mark ambiguous on a flip. A mixed-provenance regression fixture ships atevals/gold/surface_form_parity/(7 cases: 4paper_verbatim§F.3.6 examples + 2 maintainer-authoredcounterfactual_rewritepaired variants carryingderived_from+semantic_equivalence_rationale+ 1maintainer_boundarydocumenting the "unless unevaluable" clause). Because there is no deterministic detector for the surface-form bias and the 29/10 split is directional (§H), the fixture is a regression set, not a calibration set:scripts/check_surface_form_parity.pyvalidates integrity + provenance honesty (paper_verbatim quotes the paper; maintainer-authored items never claim paper-verbatim) + pair invariants (paired items hold claim + verdict constant, differ only in framing) + no rotting pdftotext line anchors — no FNR/FPR ritual. The schema decision is enforced at runtime byrender_judge_view(), a whitelist projection (judge sees only an index-derived opaquehandle+review_item_text) proven by a serializer-strip test to leak no blind field — including the nestedprovenance.reviewer_sourceauthor label and the answer-encoding fixtureiditself (-cf/-ambiguoussuffixes, per codex review).scripts/check_216_surface_form.pyasserts the DA carries every load-bearing clause block-scoped + fence-aware (six-class mutation suite).run_evalsdiscovers the fixture and marks itpending(no native measurer, by design — pinned by atest_run_evalstest so it cannot false-green through the eval gate). Negative scope: #273 (rubric-aware calibration) is NOT folded in — it is a different mechanism (an interpretive caveat with no detection claim); #216 carries a cross-reference only (design note + PR body +manifest.yamlrelated_issues), with no shared prompt / gold / lint / runtime wiring. Additive and backward-compatible; CI-wired via the spec-consistency workflow + pytest manifest. Design note:docs/design/2026-06-09-216-surface-form-parity-design.md.
Changed
- Plugin-root
agents/symlinks materialized as real byte-identical copies (#413, external audit). The threeagents/*_agent.mdfiles were relative symlinks intodeep-research/agents/(v3.7.0 Phase 2.1) — on Windows checkouts without developer mode /core.symlinks, and in zip-download installs, they materialise as one-line text files containing the link path, silently breaking the three plugin agents. Maintainer-adjudicated fix: real copies, with the single-source guarantee the symlinks provided (the v3.7.0 Pattern C3 rationale for symlinks-not-copies) taken over by a new CI lint,scripts/check_agents_mirror_sync.py— a hard-pinned mirror roster enforcing set equality (a deleted mirror silently un-ships an agent; an unrostered addition has no declared source), regular-file-never-symlink (the regression itself, checked before byte-equality because a symlink trivially byte-matches its own target), and byte-equality with the canonical source (fix hint names the copy direction: edit the source, re-copy, never edit the mirror). The two lints that leaned on symlink resolution adapt:check_version_consistency.pyinvariant 8 now excludes the mirror dir from the unique-agent count outright (real copies no longer dedup viaresolve(); the exclusion is sound because the mirror lint pins every file there as a pure alias), andcheck_v3_10_134_write_scope.pyI5 maps a root-agents/file BY NAME to itsdeep-research/agents/source before the roster check — with a negative test pinning that the mapping is not an allowlist (a name with no rostered source still flags as fail-open). 10 new mirror-sync tests (3 mutations killed: symlink-branch, byte-equality, unrostered-extra) + 5 adapted/added tests across the two existing suites; lint + pytest companion wired into spec-consistency CI. Cross-model review round (1 P2, adopted with an empirical repro): the I5 remap is restricted to DIRECT children of rootagents/— a nestedagents/sub/agents/<rostered-name>.mdno longer remaps to the deep-research source (which would have silently reopened the fail-open case the recursive glob exists to catch), pinned by a negative test. Theskills/directory symlinks are unchanged — materializing those means duplicating the four skill trees, a separate decision if Windows source-checkout support is ever pursued. (2026-06-10 audit; follows the #301/#347 4.7→4.8 pattern).** Trigger: the primary session model moved to Fable 5, which inverts the v3.7.0model: opusfrontmatter floor on the three heavy commands (/ars-full,/ars-reviewer,/ars-revision-coach) into a silent downgrade ceiling — those commands now inherit the session model (the 11 light-modesonnetpins are deliberate cost routing and stay; the plugin agents were alreadymodel: inherit). Display-name drift retired at the remaining pin sites: theshared/cross_model_verification.mdprimary-model row is now generation-agnostic ("the inherited Claude Code session model" — it stops needing a per-release bump), the SessionStart announce +docs/PERFORMANCE.md(+zh-TW) cost anchors are provenance-labelled ("measured on Opus 4.x", order-of-magnitude) instead of asserting a two-generations-stale "$4–6 on Opus 4.7", and the disclosure-protocol e.g. list is refreshed. OpenAI verifier lineup unified gpt-5.4 → gpt-5.5 / gpt-5.5-pro: the citation judge already defaulted togpt-5.5-xhighwhile the verification doc still taught 5.4 — and the availability case-globgpt-5.4*)rejected 5.5 ids outright; web_search-on-Responses support and pricing were verified first-party 2026-06-10, legacygpt-5.4*ids remain accepted, and the cost table is re-anchored on gpt-5.5 ($5/$30 per 1M). Routing smoke recalibration (#133 fixtures): 8/8 routing-class pass on Fable 5 (clarify/proceed plus all three escape-hatch behaviors — byte-0 honored, mid-message rejected, case-insensitive accepted); two destination picks additionally required the Routing-Rules/MODE_REGISTRY context the manual protocol provides. The acceptance threshold intests/fixtures/issue_133_routing/README.mdis reworded from "100% on Opus 4.7" to "100% on the current primary model" so the definition stops drifting per release. Two bare anti-hallucination tails on the compliance surfaces are kept as annotated debt (high-stakes domain, silent failure mode — in-fileharness-retirementannotations added). Deliberately out of scope, tracked separately: re-baselining the #272/#273/#274 model-behavior premises against the Fable 5 system card, and a negative-framing sample-reframe of the top-3 agent files at the next minor.
[3.13.0] - 2026-06-18 — Hook portability, provider-agnostic verification, guard correctness
Fixed
- Write-scope guard:
CLAUDE.mddropped from infra-protected globs (#459). Closes the residual half of #448/#449. #449 anchored infra self-protection onplugin_root(fixing #448 for the plugin install layout), but under the traditional git-clone + symlink-into-~/.claude/skillslayout there is noCLAUDE_PLUGIN_ROOT, theplugin_rootfallback resolves to the cloned repo root, and a user working IN that repo hasplugin_root == workspace_root— so the bareCLAUDE.md/.claude/CLAUDE.mdinfra globs matched the user's ownCLAUDE.mdand re-denied it (the #448 bug, on a layout #449 cannot distinguish: home turf and clone-as-user are the same runtime condition). Fix (codex-consulted, Option 2): removeCLAUDE.mdand.claude/CLAUDE.mdfromINFRA_PROTECTED_GLOBS. Unlike every other infra entry,CLAUDE.mdis NOT load-bearing — it documents the guard binding, it is not the binding, so editing it cannot fail the guard open. The load-bearing enforcement files (guard script, manifest, hooks, plugin metadata, agent frontmatter, lint) stay protected on home turf; protecting ARS's own instruction doc from agent edits belongs in review/CI, not the write-scope guard. 3 new tests + 1 retargeted, mutation-verified (re-adding the globs fails the new tests); 73 guard tests pass. - Windows Python hook portability + graceful no-Python degradation (#454). The
PreToolUsewrite-scope guard was wired as a barepython3 ".../ars_write_scope_guard.py". On Windowspython3is commonly a 0-byte Microsoft Store App Execution Alias stub, so the hook errored before the guard's own fail-safes could run and spammed the hook log every call. A new cross-platform launcherhooks/run_guard.sh(POSIX sh;hooks.jsonnow invokes it viabash) finds a REAL interpreter —py -3/python3/python, each verified by a marker probe that must exit 0 AND print the marker (a stub that prints then exits non-zero is rejected) — then runs the guard as a supervised, time-bounded subprocess. Plan A graceful degradation (the guard is optional v3.10 hardening; ARS core needs no Python): if no real interpreter is found OR the guard subprocess misbehaves (non-zero, timeout, empty, or non-JSON / missing-key output, validated by a realjson.loadnot a substring grep), the launcher emits a valid pass-through hook JSON and exits 0 — it never exits non-zero and stays silent on stderr on these degraded paths (PreToolUse is a hot path; per-call stderr is the spam #454 is about). Healthy-guard stderr advisories are relayed. Newscripts/test_run_guard_launcher.py(21 tests, run from a temp plugin layout so the guard is always resolved from the launcher's own../scripts/— no production env back door); thehooks.jsonCI assertion now requires a line-anchored non-comment guard assignment AND theGUARD_OUT=$(... | run_bounded ... "$GUARD")exec call site rather than a bare filename substring (a comment or anecho-wrapped decoy no longer false-passes). New.gitattributespins*.sh eol=lf. README documents the Git Bash prerequisite (without it Claude Code falls back to PowerShell, which cannot run the.shlauncher, so the guard is inactive and the hook logs per call instead of no-opping quietly).- Real-use findings: a two-model dual-track implementation review (codex + gemini, both POSIX-reproduced) hardened the launcher far beyond the original wiring fix, and the cross-model split was load-bearing — each model caught real bugs the other missed. Round 5 (codex) found the marker probe ignored exit status, the guard ran unbounded, and the JSON check was a substring grep. Round 6, once the tests exercised the REAL watchdog on a host with neither
timeoutnorsetsid, found three fail-open bugs the back-door tests had masked: the no-timeoutfallback fed the guard an EMPTY stdin (a realdenywas silently lost — the guard was dead on any timeout-less host), an un-reapable orphan grandchild could wedge the$(...)capture, and the watchdog could false-report a timeout for a command that finished within the bound. The independent gemini track then refuted codex's first race fix (a successfulkilldoes NOT prove the child is still alive — afterwaitreaps it the pid can be RECYCLED, so a blind kill could hit an innocent process and still false-flag a timeout) and added two fail-open findings codex missed: a predictable/tmpfallback whenmktempfails is a symlink-attack surface whose redirect failure reads as a broken guard, and the CI exec assertion was still gameable by an inline comment or anecho-wrapped call. Final state: stdin stashed on fd 3, stdout captured via a private temp file, timeout decided by a done-file handshake (the parent disarms the watchdog before reaping it; the watchdog kills/flags only while the done-file is absent — no pid-reuse race),mktempfailure degrades to pass-through instead of a guessable path, and the CI assertion is line-anchored. Orphan-grandchild leakage in the doubly-degraded no-timeout/no-setsidpath, and a multi-megabyte payload held in a shell variable, are documented as accepted trade-offs (the real probe and guard spawn no grandchildren and ordinary hook payloads are small; the robust alternatives add temp-file lifecycle / symlink surface to a hot path).
- Real-use findings: a two-model dual-track implementation review (codex + gemini, both POSIX-reproduced) hardened the launcher far beyond the original wiring fix, and the cross-model split was load-bearing — each model caught real bugs the other missed. Round 5 (codex) found the marker probe ignored exit status, the guard ran unbounded, and the JSON check was a substring grep. Round 6, once the tests exercised the REAL watchdog on a host with neither
draft_writerdual-phase static union documented + POSIX-safe Windows path matching (#451, #330). Documentation + portability fix for the draft writer's dual-phase static union; path matching made POSIX-safe for Windows checkouts.
Added
- Provider-agnostic cross-model verification (#455). The cross-model verification layer now accepts OpenAI-compatible endpoints (MiMo, DeepSeek, self-hosted) alongside first-party OpenAI via a normalized compatible-verdict path (
scripts/cross_model_verification/normalize_compat_verdict.py+check_cross_model_verification_sync.py). The grounded first-party OpenAI path is preserved and deliberately NOT routed through the standardOPENAI_BASE_URL(so an existing proxy user is never silently downgraded to the ungrounded compatible path — the grounded-proxy gap is tracked separately in #456). Design:docs/design/2026-06-16-453-provider-agnostic-cross-model-verifier-spec.md. - Opt-in Socratic adjacent-framing probe (STORM-borrowed perspective expansion) (#461;
deep-research2.10.0 → 2.11.0). WhenARS_SOCRATIC_ADJACENT_PROBE=1is set, the Socratic Mentor may, in exploratory sessions during Layer 1 (Problem Framing), surface ONE adjacent research framing the user has not raised — as a pure question ("an adjacent facet you haven't raised: — include it, or set it aside?"), never a proposed idea. Borrows the intent of Stanford OVAL STORM / Co-STORM (https://github.com/stanford-oval/storm): STORM's perspective discovery and Co-STORM's moderator inject framings adjacent to — but not directly answering — the current question to break local stagnation. ARS anchors framings in LLM internal knowledge (zero retrieval); majority-favour is a deliberate tradeoff for the novice target (mainstream-facet visibility helps researchers who haven't seen enough), with external-TOC retrieval left as a pluggable forward note. Hard-bounded by the Kong L2 verb test — never propose/substitute/rank/expand/select; surface-and-ask only, one facet at a time, max 2 per session ≥3 rounds apart. S4 (Scope Stability) is repurposed as an intensity knob (early scope-lock raises the tendency), reusing existing state — no new counter. One-push-then-retreat on decline;[ADJACENT-PROBE: ...]log tag flows into Stage 6 self-reflection (a high decline rate is the bias-visibility signal). Default OFF. No new agent / mode / schema — prose-layer only, same shape as the v3.5.1 Reading Probe. Gate fires exploratory (opposite of the goal-oriented Reading Probe). Two review gates, all findings adopted: a spec-compliance pass (✅) and an independent semantic red-line reviewer that caught a Critical — the canonical GOOD example originally used "the teacher's mediating role," a hypothesis disguised as a category word (semantically identical to the BAD-propose row, and contradicting the agent's own WP14 flag); replaced with a true perspective phrase so the GOOD/BAD boundary is semantic, not grammatical. New lintscripts/test_adjacent_framing_probe_lint.py(10 tests, mutation-verified; adding the sibling env var also exposed and fixed an over-broad regex intest_reading_probe_lint.pythat mis-flagged it as drift). Seedeep-research/agents/socratic_mentor_agent.md§"Optional Adjacent-Framing Probe Layer" anddocs/design/2026-06-18-socratic-adjacent-framing-probe-spec.md.
Chore
- Zenodo DOI added (#443, #434).
CITATION.cff+ README DOI badge wired to the Zenodo concept DOI.
[3.12.1] - 2026-06-15 — Reviewer-response triage modes (PR #433 integration)
Added
deep-researchthree-way-scanmode — a lightweight WHY/HOW/WHAT paper-comparison triage that sits betweenquickandlit-review. Produces a per-paper WHY/HOW/WHAT shortlist plus a cross-paper synthesis (common WHY, divergent HOW, strongest WHAT, unresolved gap), and escalates tolit-review/systematic-reviewfor full coverage. (deep-research2.9.4 → 2.10.0)academic-paperrebuttal-auditmode — standalone advisory QA of an author's existing rebuttal/response draft against the reviewer comments (per-comment coverage table + gap list + risk flags for tone/evidence/misread). It generates nothing and, because a standalone invocation runs outside the pipeline, it explicitly suppresses Schema 11 emission / Material Passport writes /ready_to_submitstatus — enforced by a newcheck_rebuttal_audit_guard()lint with mutation coverage. Routed by input shape: both reviewer comments AND an existing draft →rebuttal-audit; comments only →revision-coach.revision-coachscope extension — its trigger/docs now cover pushback/disagreement posture and non-journal scopes (conference rebuttal, grant-panel response, transfer-after-review)./ars-3wand/ars-rebuttal-auditslash commands.
Credit
Integrated from @Yaobin29's PR #433. The original PR proposed a standalone reviewer-response skill; this release folds its genuinely-novel parts into existing skills as modes, per ARS's mode-based architecture. The rebuttal-audit mode rescues that PR's audit concept. Suite mode count 25 → 27 (still 4 skills).
[3.12.0] - 2026-06-08 — Kong auto-research feature track: experiment provenance, figure fidelity, cross-paper contradiction, partial-evidence decomposition
Added
- Experiment Provenance Intake + claim→experiment alignment — a schema-first evidence-ledger layer for experiment-backed claims (#260, Kong et al. 2026 §3.3 + §7.4.3). ARS deliberately keeps experiment execution outside the pipeline; the scholar runs experiments externally and brings results back. This change adds the intake + alignment layer only — it does not run experiments, judge whether one was correctly designed/run/statistically-adequate/reproducible, auto-fill provenance, or require provenance for literature-only pipelines. Two blocks ship together. Block A —
experiment_provenance[]intake array: a new optional Material Passport aggregate (shared/contracts/passport/experiment_provenance_entry.schema.json) where each scholar-entered entry carries a nestedrepro_lock(the same inline-object shape as the passport-level lock, re-declared not$ref'd because the source is inline prose, not a schema file), aplanned_vs_executed[]record (eachexecuted:falseunit carries a gate-checkedskip_reason), andnegative_results[]/known_limitations[]arrays whose key must be present (an empty[]is well-formed and routes to a disclosure advisory; an absent key is malformed → gate FAIL, the absent-key rule ported from #261's C3). Block B — claim→experiment alignment: the claim manifest gains an optional per-claimplanned_experiment_ids[]join field (parallel toplanned_refs, minItems 1, optional-absent), and a new fourth ref_slug-less claim-finding aggregateexperiment_alignment_results[](experiment_alignment_result.schema.json) — alongside the existinguncited_assertions/claim_drifts/constraint_violationssiblings — with an experiment-specific MECE verdict enum{ALIGNED, OVERSTATED, NOT_SUPPORTED_BY_PROVENANCE, PROVENANCE_INSUFFICIENT}. The verdict is produced by the integrity verification agent AT the gate (Stage 2.5 sampling / Stage 4.5 full), not by the citation-audit agent at the Stage 4→5 boundary — mirroring #261's Phase C3, so the row is emitted and gated in the same pass and the stage-ordering race (a verdict landing after the gate ran) cannot occur. A mixed-evidence claim carrying BOTHplanned_refsandplanned_experiment_idsis audited by both paths and the gate decision is worst-verdict-wins (an OVERSTATED experiment path blocks even when the citation path is SUPPORTED).experiment_idis frozen at intake (a post-intake rename is a re-intake event, not a silent edit). Seven new cross-array invariants land inscripts/check_claim_audit_consistency.py(JSON Schema cannot express cross-array integrity): EP-INV-1 (experiment_id unique/passport), EP-INV-2 (planned_experiment_ids resolve — doubles as the rename + forward-reference dangling-pointer guard), EP-INV-3 (experiment ids ⟹ empirical kind; mixed literature+experiment allowed), EP-INV-4 (declaration↔provenance symmetry), EP-INV-5 (declaration well-formedness when present:statusenum /declared_by: scholar/ non-emptydeclared_at— so a malformed declaration likestatus: "garbage"FAILs deterministically instead of slipping past the symmetry check), EA-INV-1 (finding_id unique), EA-INV-2 (alignment-row references resolve; a danglingexperiment_idis a structural FAIL, never aPROVENANCE_MISSINGverdict — that value is deliberately absent from the enum, so no fake judge fields are forced for a row where no judge ran). A persisted passport-levelexperiment_intake_declarationcloses the anti-skip circularity with a fail-closed legacy boundary, split across two enforcement layers (stated precisely, not conflated): the lint deterministically enforces declaration↔provenance symmetry (EP-INV-4) and declaration well-formedness (EP-INV-5); the integrity gate (a Stage-1/Stage-4.5 check, NOT the lint) owns thears_versionnumeric legacy decision and the declaration-presence FAIL — a passport islegacy_unknown(advisory) only with positiverepro_lock.ars_version < #260-constantproof, everything else (including a passport with norepro_lock, or one with noars_version) is treated as post-#260 so the declaration is REQUIRED and its absence FAILs at the gate, meaning a new run cannot dodge it by making its version unprovable. Thears_versionnumeric half is deliberately left at the gate layer (not promoted to a lint constant) because the #260 release version it compares against is frozen at ship time, not at intake. Literature-only pipelines therefore still emit a one-lineno_experiments_declareddeclaration (noexperiment_provenance[]needed). Producers taught in lockstep (schema-first writer-binding discipline): the three manifest emitters (synthesis_agent/draft_writer_agent/report_compiler_agent) emitplanned_experiment_idswhen an experiment backs a claim; the integrity agent gains a new disclosure-only Phase (D6) carrying the POSITIONING non-goal verbatim ("does not judge whether the experiment was correctly designed, run, statistically adequate, or reproducible by ARS"); the orchestrator carriesexperiment_alignment_results[]+ the declaration forward; README intake detection sets the declaration. Drift guard: the repro_lock field set is single-sourced inscripts/repro_lock_validation.py(imported by bothcheck_repro_lock.pyand the new standalonecheck_experiment_provenance.py), with a drift test asserting the nested schema's required keys equal the shared constants. Three documented departures from the issue's literal text (each corrected after a first-party tracked-repo read):repro_lockis an inline-prose object, not a schema file, so "inherit repro_lock" means nesting the shape, not$ref'ing a non-existent file; the claim manifest had no experiment pathway, so the join is added, not assumed; and "Path X / Tier-1 required / writer-binding" are not named conventions in the tracked repo, so the discipline is described rather than cited by a name a reader cannot find. Schema + manifest edit + 7 lint invariants + standalone shape validator + drift guard + integrity/writer/orchestrator agent prompts + README mirrors +examples/passport_with_experiment_provenance.yaml(2 experiments, a mixed-evidence claim, an OVERSTATED alignment row) + full TDD suite (schema ±, fail-closed symmetry, declaration well-formedness, mixed-evidence two-row, verdict-derivation, mutation-verified non-vacuous invariants, reverse-invariant producer pins, drift, literature-only regression). The new schemas, the manifest field, and all seven invariants are additive and backward-compatible. Spec:docs/design/2026-06-08-260-experiment-provenance-intake-spec.md. - Cross-paper contradiction inventory — structured, inspectable enumeration in the synthesis layer (#262, Kong et al. 2026 §7.4.2).
synthesis_agentalready had prose-level contradiction handling (Anti-Pattern 3, the Step 3 Contradiction Resolution procedure, and the Contradictions & Resolutions table), but that prose narrative-discussed contradictions (including reconcilable-vs-irreconcilable verdicts) without making the set of assessed paper-pairs and the unresolved / checked-clear pairs enumerable for the scholar to confirm — the multi-paper relational-reasoning gap Kong et al. 2026 (arXiv:2605.18661 §7.4.2) document for research-synthesis systems. A new Step 3b — Cross-Paper Tension Inventory is added additive to (not a replacement of) the existing Step 3 prose: the agent emits across_paper_tensions[]markdown block — one entry per assessed candidate pair carryingpair_id,paper_a/paper_b,candidate_basis,overlap_topic,a_finding/a_evidence_pointer,b_finding/b_evidence_pointer,pair_assessment,resolution_status, an iff-resolvedresolution_pointer, andscholar_confirmation. Prose-layer only — no JSON Schema, no lint invariant, no gold fixture (mirroring the #214 / #261 prose-layer decision, NOT the #213 schema-layer one): the producer (synthesis_agent) and the readers (the scholar plus the report/integrity LLM agents) all read prose, there is no deterministic downstream parser, and the judgment that matters — "is this a genuine contradiction vs. a conditional difference" — is irreducibly semantic, so machine-validating the YAML shape would prove field presence, not contradiction fidelity. This deliberately departs from the issue's literal acceptance (which read "schema addscontradiction_pairs[]block" + "calibration gold set accuracy ≥ 0.75"): the named "downstream consumers" (formatter, integrity_verification) are themselves LLM agents reading markdown — there is no machine consumer — so a schema would be the exact false rigor #261 rejected, and 20 LLM-judged pairs are too few and too nondeterministic across runs to wire as a hard CI gate (no calibration artifact ships in this change — any future or manual calibration should be recorded out-of-band with its model/date/prompt + a confusion matrix and stay non-blocking, never a pass/fail gate). The field model is corrected from the issue's non-MECE draft: the issue's singleconflict_type ∈ {contradictory, conditional_difference, resolved_in_synthesis}folded conflict nature and resolution status into one enum and referenced aninsufficient_overlapvalue not in it — these are split into orthogonal axes (pair_assessment ∈ {contradiction, conditional_difference, no_material_conflict, insufficient_overlap}×resolution_status ∈ {resolved_in_synthesis, flagged_unresolved, not_applicable}), and each side gains anevidence_pointerso a finding cannot be stated as free text the paper does not support. Candidate-pair scoping is a recall-limited heuristic, not an algorithm: an LLM agent does not execute an O(K²) enumeration, so the design states it as bounded candidate-edge generation (include a pair on shared RQ subtopic / shared construct / opposite finding direction / bibliographic coupling / scholar flag) with two honesty rules — bibliographic coupling is an inclusion signal only, never an exclusion rule (same-camp papers cite the same priors and tend to agree; cross-camp contradictions have low coupling), and cross-neighborhood pairs can be missed, so every inventory carries a mandatory Coverage Note stating the denominator and the explicit recall limitation and the agent must never write "all contradictions addressed." Inheritssynthesis_agent's narrative-side discipline unchanged (advisory-only: the scholar makes the final call; the agent emitsscholar_confirmation: pending, never self-confirms, simulates no audit step, and reads no entry frontmatter). No#111dependency (that is a single boolean, per the issue's own correction). Addsexamples/contradiction_pairs_example.md(6-paper remote-work synthesis covering a genuine unresolved contradiction, a resolved conditional difference, an unresolved conditional difference (so both resolution states of one assessment are shown), a no-material-conflict pair, an insufficient-overlap pair, and a Coverage Note that names the still-unpaired cross-neighborhood paper). Agent-prompt + output-template + doc example only; no schema, lint, or executable change. - Figure/Table Fidelity Gate — the visual analog of the §F.3.2 partial-evidence trap (#261, Kong et al. 2026 §3.4). The VLM Figure Verification Protocol checked "does the rendered figure match the source data?" (a faithful-rendering check) but could not check "does the caption's interpretation follow from the data, and does the manuscript cite this artifact for a claim it actually supports?" — a figure can render perfectly while its caption overstates the data or the manuscript cites it for an unsupported claim (Kong et al. 2026, arXiv:2605.18661 §3.4). This is the visual counterpart of the prose partial-evidence trap addressed for citations in #213 and for review synthesis in #214; same trap, different artifact type, separate implementation. Prose-layer only — no JSON Schema, no lint invariant, no gold fixture (mirroring the #214 prose-layer decision, NOT the #213 schema-layer one): the
figure_table_trace[]producer (visualization_agent) and consumer (integrity_verification_agent) are both LLM agents reading a markdown Figure Package, so there is no deterministic downstream parser and machine-validating the YAML shape would be false rigor.academic-paper/references/vlm_figure_verification.mdgains a Figure/Table Trace section defining afigure_table_trace[]block — one entry per figure (or manuscript table that has an entry) carrying all six required keys —artifact_id,source_data,transformation({script, hash}OR a precise manual-derivation pointer — vague values like "computed manually" are treated as untraceable),caption_claim,supported_manuscript_claims(each as claim text + optional locator, not a bare id, since the visualization agent can run before the draft's claim manifest exists), andlimitations(present even when[]).visualization_agent.mdemits the block in the Figure Package (new Step 6.6) andintegrity_verification_agent.mdPhase C gains C3. Figure/Table Caption Fidelity running at Stage 4.5: entry well-formedness (a malformed entry missing any of the six keys short-circuits to FAIL) plus four fidelity checks — trace completeness, caption-claim support (does the interpretation follow from data+transformation, with compound captions decomposed into atomic sub-claims using the #213 idea as prose guidance only, noPARTIALverdict /sub_claim_breakdownimported; an entry takes its weakest sub-claim's verdict), bidirectional manuscript-claim linkage (each listed claim must reference the artifact and not overstate it, AND every substantive manuscript use of the artifact must be listed — incidental/structural mentions exempt), and limitation visibility (a known limitation must reach caption/Discussion/Limitations). Severity is split, not blanket-advisory: a caption that contradicts the data, an untraceable claim-bearing artifact, a missing/overstated manuscript link, or a dropped known limitation FAIL (block); only uncertainty signals are advisory — an emptylimitations: []emits a named[FIGURE-LIMITATIONS-EMPTY]note (never a silent pass) and a legacy figure with no trace surfaces a trace-unavailable note. At Stage 4.5, an updated Figure Package with nofigure_table_trace[](or one omitting an entry for a figure it contains) is a FAIL ("caption fidelity not verified"), so the check is not trivially skippable; a legacy figure with no Figure Package at all is the advisory case. C3 inherits the existing C1 data-cross-referencing layer (it does not re-render figures — that is VLM — or re-verify raw data — that is C1); its new coverage is interpretation and linkage. Addsexamples/figure_table_trace_example.md(3-figure + 1-table ML ablation walkthrough covering a normal trace, a decomposed compound caption, and the empty-limitations advisory). Reference + agent-prompt/protocol text + doc example only; no schema, lint, or executable change. - Sub-claim decomposition before citation judgment — the citation-layer half of the §F.3.2 partial-evidence trap (#213). The unified citation judge (
academic-pipeline/agents/claim_ref_alignment_audit_agent.md) emitted exactly one verdict per citation, so a compound claim ("X rose AND the effect held across Y") whose source supported one sub-claim but not the other was collapsed to a single binary check and the unsupported sub-claim was silently lost — the largest correctness-error class documented in AI meta-review (Kim et al. 2026, arXiv:2605.20668v1 §F.3.2). The judge now runs a required Step 0: decompose the claim into atomic sub-claims and judge each independently before choosing the citation-level verdict. A new prompt-layerPARTIALverdict (supports some sub-claims, not all; no active constraint violated) is normalized at Step 6 tojudgment=UNSUPPORTED, defect_stage=source_description, routing the unsupported sub-claim through the same gate-refuse path a fully-unsupported claim takes so partial support is never accepted as full resolution. Baseline correction: the issue body proposed addingPARTIALto the schemajudgmentenum; first-party reading showed that is the wrong baseline —PARTIAL(like the existingVIOLATED) lives at the prompt layer, NOT in the schema enum, so the 18 cross-field invariants and the allowed-(judgment, audit_status, defect_stage)-matrix stay untouched (the normalized triple was already in the matrix). The decomposition is persisted in a new additive optional schema fieldsub_claim_breakdown[]onclaim_audit_result(pre-#213 entries validate unchanged); its presence — not the defect_stage value — is the machine-readable partial-support signal for downstream consumers. A new lint invariant INV-19 pins the full normalization (breakdown present ⟹judgment=UNSUPPORTEDANDdefect_stage=source_descriptionAND true-partial: ≥2 items with ≥1 SUPPORTED AND ≥1 valid non-SUPPORTED sub_verdict), mutation-verified to discriminate. MalformedPARTIAL(breakdown absent / <2 items / not true-partial) takes theaudit_status=inconclusive [partial_breakdown_malformed]path, never a silent bareUNSUPPORTED. Calibration gains 5 partial-support gold fixtures + apartial_supportsubset metric (scripts/claim_audit_calibration.py): because partial fixtures carryexpected_judgment=UNSUPPORTED, a judge that stops decomposing and emits bareUNSUPPORTEDpasses the aggregate FNR gate; the subset metric counts a partial fixture as passed ONLY when the judge emitsUNSUPPORTEDAND a well-formed true-partial breakdown, so the regression surfaces asmiss_rate > 0while the aggregate stays green. The synthesis-layer sibling (#214) is out of scope. Schema + lint + judge-prompt + calibration + protocol-doc; the schema field and INV-19 are additive and backward-compatible. - Sub-claim inventory before consensus in the editorial synthesizer — the synthesis-layer half of the §F.3.2 partial-evidence trap (#214). The synthesis-layer sibling of the citation-layer #213. The editorial synthesizer (
academic-paper-reviewer/agents/editorial_synthesizer_agent.md) aggregated consensus over a whole weakness bundle, so a compound weakness whose sub-claims carried different reviewer support was collapsed to one verdict and the minority sub-claim was lost — the single largest correctness-error class in AI meta-review (Kim et al. 2026, arXiv:2605.20668v1 §F.3.2). Prose-layer only: the synthesizer emits a human-facing decision letter + revision roadmap, not machine-readable judge rows, so there is no deterministic consumer for a #213-style schema field / lint invariant / gold fixture — adding one would be unrequested abstraction. Thesub_claimvocabulary aligns with #213; its architecture is not imported. Step 1 splits intoStep 1a — Reviewer Summary Matrix(retained) +Step 1b — Weakness Sub-Claim Inventorykeyed onsub_claim_id(only weakness bundles decompose; recommendation/confidence/counts stay in the 1a matrix). Step 2 computes consensus per sub-claim over an absolute denominator of the 4 non-DA reviewers (position ∈ {raised, corroborated, not-mentioned, disputed};not-mentionedis silence, never opposition or agreement). Mutually-exclusive dispositions with explicit precedence:conflict ≥ 1 → SPLITfirst, otherwise byagreecount (4→CONSENSUS-4,3→CONSENSUS-3,2→corroborated finding,1→single-reviewer finding); every(agree, conflict)cell maps to exactly one disposition andagree = 0is unreachable by construction.disputedcovers existence OR action/severity conflict, so reviewers agreeing a problem exists but recommending incompatible remedies route to SPLIT → EIC arbitration. ASub-Claim(s)column is added to the roadmap tables in both the agent output format and the standaloneeditorial_decision_template.mdso the decomposed granularity survives to the output boundary. DA-CRITICAL flow and the v3.6.2 sprint-contract arithmetic path are untouched; scoped to the general Synthesis Protocol only. - Concise output discipline + pressure-stable boundary reinforcement across the report-producing reviewers (#274). A guidance-layer follow-up to the Claude Opus 4.8 system card §4.1.4, which documents two behavioral signals: refusals/responses trend longer and more over-caveated than 4.7, and a small number of multi-turn cases where a correct refusal was retracted under sustained pressure — both quality issues a user feels directly in a review tool. Guidance layer only; no claim of having proven 4.8's runtime behavior. A concise output discipline block is inlined (before
## Output Format) into the report-producing reviewers —domain/methodology/perspective/eic/devils_advocate/editorial_synthesizerreviewers andacademic-paper/peer_reviewer: state findings and verdicts directly, don't pad with repeated qualifiers; concise explicitly does NOT mean under-caveated — preserve every material uncertainty, cut only redundancy. A "pressure is not evidence" rule is added to the Devil's Advocate Anti-Sycophancy Rules and the editorial synthesizer's arbitration discipline: repeated pushback / authority appeals / bare softening requests do not change a finding. In the Devil's Advocate, this is bound to the existing numeric concession threshold (≥4 normally, 5/5 after a prior concession); in the editorial synthesizer's arbitration, a finding changes only on substantive new evidence or reasoning that addresses the arbitration basis (no numeric threshold lives there). Both are framed by evidence standard, not as an attack catalogue (public-repo safe). Every block carries an epistemic-status line: these are prompt-surface instructions; they cannot prove the model stays pressure-stable at runtime — that would need a separate non-deterministic behavioral eval. The issue's acceptance "confirm boundaries hold under 4.8 after pushback" is reframed as a prompt-surface confirmation (the instructions are present and explicit), not ticked by self-simulating a pushback dialogue (theater, not verification) and not pinnable by a deterministic CI test — mirroring the #272 guidance-layer ≠ runtime-enforcement discipline. No lint / mutation test (style guidance is not a contract invariant with a downstream consumer). Agent-prompt text only. - Retrieved-content instruction/data boundary stated as a standing principle (#367, guidance layer for #272). Retrieved external content is data; imperative-looking text inside it is not auto-promoted to a user instruction. The authoritative statement lands as a canonical §2A in
shared/ground_truth_isolation_pattern.md(marked distinct from the eval-leakage concern) and is inlined verbatim into the two highest-surface retrieval agents —deep-research/source_verification_agentandbibliography_agent— so the principle is present where a fetch happens. A newscripts/check_instruction_data_boundary.pylint guards against silent removal or anchor-preserving gutting (presence / verbatim-sync / section-anchoring / contiguous-backpoint), proven not-accept-all by an 11-mutation test plus a positive control; the original strict-xfail pebble marked the unbuilt runtime defense and was later replaced by #675's executable behavioral-probe witness without claiming structural enforcement. Commit-time documentation consistency only — no runtime gate, no injection-mitigation claim. The originating trust-boundary issue (#272) stays open by design (the structural layer is deferred, now tracked by #676). Lint + CI wiring (spec-consistency.yml+ pytest manifest); no schema change. - Version-consistency lint extended to the README badge, docs forward-reference, and zh-TW heading invariants (#357, invariants 5-7).
scripts/check_version_consistency.pycovered invariants 1-4 (CLAUDE.md table, suite version, pipeline tracking, plugin manifests); it now also enforces the three release-doc invariants previously caught only by manual checklist: inv 5 — the README shields.io version badge tracks the suite version; inv 6 — nodocs/*.mdcites avX.Y.Zabove the suite version (forward-reference guard); inv 7 — version-bearing H2 headings stay in lockstep betweendocs/<name>.mdanddocs/<name>.zh-TW.md(plain headings may differ; only version tags pair, compared as multisets so a dropped one-of-a-pair heading is caught). Version-token regexes use a trailing negative lookahead so prerelease / 5-segment tokens (v3.12.0-alpha,v3.11.1.2.3) are dropped rather than partial-matched. Also removes thedocs/PERFORMANCE.mdcross-model onboarding section (en-only; aligns the en/zh-TW pair). TDD with 11 new test methods plus broadened aligned-fixture coverage, each invariant mutation-tested (stub to accept-all → matching test fails). - ARCHITECTURE.md component-version markers now policed by lint (#345, invariant-4 gap).
scripts/check_spec_consistency.pypoliced version markers in the README (×4 langs),.claude/CLAUDE.md,MODE_REGISTRY.md, andSKILL.md— but notdocs/ARCHITECTURE.md, where six "current academic-pipeline component version" strings were missed by the v3.11.1 bump and caught only by a manual first-party sweep (#343/#344). A newcheck_architecture_component_version()parses the suite version from.claude/CLAUDE.mdand asserts the six current-component markers equal it (the mermaid orchestrator node + the component table row + the four stage rows). It anchors on theacademic-pipeline <ver>component pattern and never inspects thetimelineblock, so a stale current-component marker fails while a feature-history marker (vX.Y.Z : <feature>, which records which version shipped a gate and must not be bumped on a patch) is left alone — the distinction a naivev3.xgrep would corrupt. The version regex captures the repo's full 4-component grammar (the suite shipped v3.9.4.2) with a hard right boundary so a 3-component marker can't partial-match inside a longer one, and the component/stage row scan is anchored to markdown table rows so a narrative provenance mention isn't wrongly policed. Wired intospec-consistency.yml; 7 tests (aligned passes / stale component fails / stale timeline marker does NOT fail / missing markers fail / 4-component edge cases). - Same-family / rubric-aware calibration epistemic note (#273, Claude Opus 4.8 system card §6.3.7 / §6.6.3). An interpretive, doc-only follow-up to the system card's report of modest, partly-unverbalized grader-awareness signals (the model sometimes optimizes toward what a rubric appears to reward). ARS leans on rubric / gold-set judging (reviewer calibration, the citation-claim judge), so this affects how calibration numbers should be read — not what the suite does. Zero detection / mitigation claim: ARS does not and cannot detect or correct grader-awareness (the system card's own point is that it can be unverbalized); the only honest claim is interpretive. All changes land in
calibration_mode_protocol.md"Failure cases this mode does NOT fix" plus a one-line pointer inintegrity_verification_agent.md. An umbrella "same-source evaluation risk" framing names two forms — the existing factual form (same-source hallucination — fabricated references; canonical in the Anti-Hallucination Mandate, unchanged) and a new behavioral form (same-family rubric optimization), cross-referenced both ways; the integrity block's WebSearch counter-rules are explicitly scoped to the factual form only and are not edited to imply they mitigate rubric-aware judging. An epistemic note states that under same-family / rubric-aware judging the measured calibration error is a possible under-estimate, not a ceiling. A cross-model positioning clarification resolves the doc's own opt-in-vs-default-on tension (cross-model is opt-in "for best results" in ordinary reviewer / judge paths; calibration mode is the explicit default-on exception once invoked; absent cross-model is warn-and-continue, never a gate; the consent / privacy boundary for sending a manuscript to another provider is preserved). A single-model paraphrase spot-check is documented but honestly de-powered — reword the rubric and re-judge, stated plainly to reveal only surface wording sensitivity, unable to detect unverbalized grader-awareness, and no proof the judgment is correct (no score, no threshold, no gate). No schema, no lint, no gate, no calibration-threshold change. - Kong auto-research META closeout — negative scope + Tier D design lessons (#255, Kong et al. 2026). Closes the Kong et al. auto-research survey META after every feature sub-issue (Tier A #256–#259, Tier B #260–#262, Tier C #263, Schema follow-ups #266/#268/#269) had merged; the two remaining closing conditions were documentation-only and defined the project's negative scope.
POSITIONING.mdgains a "Rejected mechanisms (autonomous-research anti-patterns)" section placed after "What this is not", enumerating the five autonomous mechanisms ARS does not do — end-to-end pipeline, idea-generation agent, Paper2X auto-generation, autonomous experiment execution, wet-lab automation API — each with a Kong anchor and, for the three that abut shipped features, an operationally-checkable CONSIDER-vs-REJECT line (idea-generation ≠ shipped #257 wording advisory; Paper2X auto-gen ≠ fidelity audit; autonomous experiment execution ≠ shipped #260 provenance intake). Two Tier D design-lesson docs land under the existingdocs/design/…lX…convention (not a new directory): L1 frames copilot-vs-auto-research as a research-state-authority review test ("does this let ARS create / select / execute / advance a research object of record without a scholar-authored seed or confirmation?"), and L2 sharpens the advisory-vs-idea-generation line for research questions with a verb test, cross-linked from POSITIONING.md and from #257. Verification notes split a verifiable claim (no autonomous mechanism in first-party ARS today; #257 / #260 are advisory / provenance gates) from a design commitment (a recorded boundary and review criterion, not a runtime guarantee). Documentation only — no schema, agent, or lint change.
Fixed
- Originality weight reconciled to 20% across reviewer reference docs; rubric weights now lint-policed (#396).
review_criteria_framework.mdstated Originality at 15% (plus a 7-dimension weighted formula and its own score-to-decision mapping) while the operative scoring source —quality_rubrics.md, which the peer-review report template instructs reviewers to score against — andacademic-paper/SKILL.mdrule 14 both say 20% with a 5-dimension aggregate. The framework doc no longer restates any number: its dimension headers drop the weight suffixes (qualitative level descriptors stay) and §4 defers weights, formula, and decision mapping toquality_rubrics.mdby name, noting that Literature Integration and Significance & Impact are reviewer-specific optional dimensions outside the numerical aggregate. Recurrence is guarded by a new lint,scripts/check_rubric_weight_consistency.py: quality_rubrics dimension-header weights must match its own aggregation-formula terms, the weights must sum to 100%, SKILL.md rule 14 must agree, and the framework doc must not restate a weight (Weight NN%/(NN%)both fail). Mutation-verified on all four invariants; wired intospec-consistency.yml+ the CI pytest manifest. Surfaced by codex during cross-model review of the 2026-06-10 researcher-blindspot audit (F-14). - Score-trajectory scale contradiction reconciled to 0-100 (#399, found during the #396 reconciliation).
shared/handoff_schemas.mddeclared score_trajectory scores as "1-5 scale" while every producer and consumer is 0-100: the report template scores 0-100 perquality_rubrics.md, and the canonical Early-Stopping Criterion is explicitly "delta < 3 points on the 0-100 rubric" (academic-pipeline/SKILL.md). The 1-5 comment is a pre-v1.4 fossil — the reviewer changelog (2026-03-08) records "Dimension Scores upgraded from optional 1-5 to required 0-100". Schema comments now say 0-100 (scale sourced fromquality_rubrics.md; dimension names still from the framework doc), and the trajectory protocol's Stage 6 example — which mixed both scales in one table (1-5 scores, a "-0.2 within tolerance" verdict, and an "overall delta = 4" that matched neither) — is rebuilt on 0-100 with internally consistent deltas and verdicts. Exposure note (per the #399 acceptance): the thresholds themselves were never wrong — they were always defined against 0-100 in SKILL.md; the risk was a consumer reading onlyhandoff_schemas.md, whose 1-5 trajectories would make regression detection (delta < -3) near-unreachable and early-stop (delta < 3) near-always-on. No evidence either check ran on 1-5 data (no real re-review artifact set exists post-v2.7; see #395). The reviewers' Confidence Score[1-5]is a deliberately separate axis and is unchanged. - Cross-model verifier now actually grounds its lookups, and an ungrounded result can no longer be laundered into
VERIFIED(#346).shared/cross_model_verification.mdtold the cross-model verifier to "search the web to confirm," but the shipped OpenAI / Gemini API call patterns wired in no web-search tool — so a copied example produced a verifier that was told to search but could not, answering from parametric memory and confidently returningVERIFIED. For a hallucinated-citation gate that is the worst failure (a falseVERIFIEDmanufactures confidence), and it shares the generating model's exact failure mode — fluent-but-wrong from memory — for the one task (existence lookup) where grounding is the entire point. Two-part fix, both at the API-pattern layer: (1) the OpenAI pattern moves to the Responses API with the hostedweb_searchtool and the Gemini pattern enables thegoogle_searchgrounding tool, so "search the web" is executable; (2) both patterns gate the verdict text on proof a search ran — they emitNOT_SEARCHEDand discard the text when the API returns no grounding evidence (an OpenAI completedweb_search_callitem / a Gemini response whosegroundingMetadatacarrieswebSearchQueriesandgroundingSupportstying the verdict text to retrieved chunks), and aVERIFIEDcarrying no supporting source URL/DOI is downgraded toNOT_SEARCHED. The protocol moves from batched (≤5 refs/call) to one grounded call per reference so the grounding evidence maps 1:1 to each verdict (a single grounding trace on a 5-ref response proves something was searched, not that each reference was) — a deliberate cost-for-provenance trade (a 60-ref paper samples 30% capped at 15, so ~15 grounded integrity calls, documented in the cost table).NOT_SEARCHEDis a new status distinct from a transport failure: a transport failure (non-2xx HTTP —[CROSS-MODEL-ERROR]) means "no cross-model opinion" (fall back to single-model); aNOT_SEARCHED(2xx, but no grounding evidence) means "an opinion we have decided not to trust," counted separately and surfaced for re-run or human review, never as agreement with a ClaudeVERIFIED.academic-pipeline/agents/integrity_verification_agent.md(the consumer) is aligned in lockstep: its behavior summary drops the stale "batches of 5", adds theNOT_SEARCHED/ ungrounded handling, and splits transport-failure graceful-degradation from theNOT_SEARCHEDpath. Surfaced during the 2026-06 harness-retirement audit (#301) by a second-model cross-check pass and filed as a live correctness gap, not a harness-retirement item. Documentation + agent-prompt/protocol text only; no executable script or schema change. - Cross-model grounding guards are now behavior-tested, and a fail-open in the Gemini source extractor is closed (#349, follow-up to #346). The #346 grounding guards shipped as bash/jq inside
shared/cross_model_verification.mdwith no automated test — a future edit to the jq, or a provider response-shape change, could silently stop it failing closed (the exact silent-false-VERIFIEDclass the guard exists to prevent). The contract-bearing jq is extracted into canonical files underscripts/cross_model_verification/(5 filters: OpenAI search-guard / text / sources, Gemini grounded-guard / sources), the documented bash now loads them viajq -finstead of inlining, andscripts/test_cross_model_verification_guards.pyruns each filter against synthetic fixtures (grounded → extracts supported sources; from-memory / non-grounded →NOT_SEARCHEDwith blank sources), with two mutation tests proving the fixtures discriminate a working guard from an accept-all / naive one. Fail-open fix (malformed-response hardening): the source extractors trusted the shape and types of the model's grounding metadata. Several malformed-but-well-formed-JSON responses could fabricate a source (defeating the blank-source downgrade and resurrecting a falseVERIFIED) or crash jq: a negativegroundingChunkIndicessilently selected a chunk from the end of the array; a string index, agroundingChunks/groundingSupportsarriving as a string/object instead of an array, a Gemini chunkurior an OpenAIurl_citation.urlthat is a number/bool/object — each either crashed or surfaced a non-URL value as a "source". The canonical filters now fail closed on all of these: indices must be in-range non-negative numbers (select(type=="number" and . >= 0 and . < ($chunks|length))), the grounded-guard requireswebSearchQueries/groundingSupportsto be non-empty arrays (not merely truthylength, which strings/objects also have), every container on each extraction path is array-normalized before it is iterated or indexed (OpenAIoutput→content→annotations; Geminicandidates→groundingChunks/groundingSupports/groundingChunkIndices) so a container arriving as an object can't have its values surfaced, and extracted URLs are filtered to non-empty strings — so any malformed response yields blank sources →NOT_SEARCHEDrather than a fabricated or crashing result. A doc-sync lint (scripts/check_cross_model_verification_sync.py) pins that the doc keeps wiring every canonical filter viajq -fand retains theNOT_SEARCHED/CROSS-MODEL-ERRORbranches (withREQUIRED_FILTERScross-checked against the on-disk.jqset so a new filter can't escape the lint). Both the test and the lint are wired into the CI pytest manifest +spec-consistency.yml, which now also ensuresjqis present on the runner. Documentation + test/lint/CI only; no agent-prompt or schema change. - Cross-model Gemini guard is rederived from the source extractor; malformed array elements no longer crash the OpenAI filters (#351, post-ship review of #349). The post-squash review of #349 surfaced that the Gemini guard and the source extractor were two parallel jq programs asserted to agree, so each round found a new input where they diverged: a
groundingSupportslinking to no valid chunk (empty / negative / string / out-of-range / fractional index), a multi-candidate response where the guard'sany-candidate scan passed on a grounded candidate while the extractor read the unsupportedcandidate[0], or a non-stringuri. In each, the guard passed while the extractor returned blank — and the blank-source downgrade only rescuesVERIFIED, so an ungroundedNOT_FOUND/MISMATCHcould be trusted as grounded. The fix is structural:gemini_is_grounded.jqnow embeds the exact samecandidate[0]extractiongemini_sources.jqperforms and passes iff it yields ≥1 source AND a realwebSearchQueriessignal is present — so the safety invariant guard-pass ⟹ at least one source extractable holds by construction for every input shape, not by two predicates kept in sync by hand. (The guard is intentionally stronger than "has a source": a chunks-but-no-search response fails it.) Separately,openai_text.jqno longer crashesjoinon a non-stringtext, and all OpenAI filters type-check each array element as an object before reading.type, so a malformed element (output: [5]) is skipped rather than crashing. +17 behavior tests across the new invariant, multi-candidate / fractional / non-string-uri cases, and the array-element-crash paths (guards 27→44). Every hole and fix verified first-party. - Judge-verdict cache key partitioned by prompt version so a prompt revision invalidates stale entries (#361). The judge-verdict cache key included
judge_modelbut no prompt-version component, so a judge-prompt revision (e.g. #213's Step-0 sub-claim decomposition) did not invalidate stale entries — a verdict cached under the old prompt was still served until the TTL expired, silently bypassing the new prompt logic (a pre-existing cache-key design gap surfaced as P2#1 in the #355 post-squash review; affects every prompt revision, not just the decomposition path)._cache_keygains aprompt_versioncomponent kept separate fromjudge_model(independent axes), and invalidation keys onJUDGE_PROMPT_SHA256— the SHA-256 of the canonical judge-prompt section, the single source of truth — so any prompt edit changes the key and invalidates stale entries with no reliance on a human bumping a label (JUDGE_PROMPT_VERSIONis a decoupled human-readable label for logs/diffs only). Fail-CLOSED on unknown version: when the caller declares the prompt versionNone, the pipeline binds a run-local component (__unknown__:<audit_run_id>) so a stale entry is never served across an unknown-version boundary (cross-run hits disabled; within-run dedup for repeated citations preserved). A CI backstopscripts/check_judge_prompt_version.pyhashes the canonical section (between theJUDGE-PROMPT-CANONICALmarkers) and fails if it drifts from the pinned hash, forcing a re-pin in the same change (wired intospec-consistency.yml+ the pytest manifest). The agent-prompt contract and lint docstring — which described invalidation as keyed on theJUDGE_PROMPT_VERSIONlabel while the pipeline already falls back to the SHA256 — are re-attributed to the SHA256 fingerprint so a downstream implementer following the contract can't re-open the bug. RED→GREEN + mutation-verified. - Judge-supplied rationale bounded on success-path rows + null rationale guarded (#360). Judge-supplied rationale on success-path rows (
completed+constraint_violation) is now bounded to the schemamaxLength=2000via a shared length-budgeting choke point, and a non-string (null) rationale degrades to the default instead of aborting the audit run. RED→GREEN + mutation-verified. - Failure-rationale bounding + PARTIAL gold-fixture requirement — two #213 sub-claim-decomposition gaps (#359, #213 follow-up). Two correctness gaps in the new PARTIAL machinery (neither reachable on pre-#213 inputs), surfaced by the #355 post-squash integration review and confirmed first-party. (1) The malformed-PARTIAL fallback could emit a schema-invalid row: the parse error embedded the offending breakdown's repr in
detail, which became the fallback row's rationale and could exceed theclaim_audit_resultmaxLength=2000. Fixed by boundingdetailat a single choke point — a shared_AuditInvocationErrorbase whose__init__clampsdetailso the"{fault_class}: {detail}"rationale always fits (budgeted against the widest fault-class prefix);JudgeInvocationError/RetrievalInvocationErrorinherit it. (2) Calibration could silently skip the atomic-decomposition metric:validate_gold_setdid not require anexpected_prompt_verdict=PARTIALfixture to carry non-emptyexpected_sub_claims, so_breakdown_covers_expectedearly-returnedTrueand scoredmiss_rate=0for any generic breakdown; a new rule (e) rejects such a fixture at ingestion (fail-closed). RED→GREEN + mutation-verified for both. - Eval gold tuple 052 removed — a fabricated citation was mislabeled as a genuine unindexed paper (#250). Gold tuple
052-valid-unindexed-regional-paperwas labeledfabrication_intent: falseand its expert-verdict notes asserted it was a "GENUINE … real regional, non-English-indexed agronomy paper" (Sembiring & Ginting 2023, Jurnal Penelitian Pertanian Regional). First-party verification across all four resolvers (Crossref / OpenAlex / Semantic Scholar / arXiv) plus DOAJ, OpenAlex Sources, Crossref Journals, and general web search found no evidence the paper or the journal exists — a fabrication labeled as genuine, exactly the failure this repo exists to detect, and a direct violation of #250's closing condition (which requires a first-party-verifiable real-but-unindexed source). Functionally the tuple was redundant: the harness reduces pre-recordedresolver_outcomes(it does not live-query), and tuple 051 (the OQ-5 by-design false-negative, a no-identifier fabrication) already exercises the identical title-only-unmatched → unresolvable reducer path. Removes the tuple;expected_outcomes.jsondrops the 052 entry (51 entries);manifest.yamlsample_n52→51 + drops thevalid_unindexeddistribution row;check_evals_gold_set.pydropsvalid_unindexedfromKIND_ENUM;citation_verification_summary.py+ test comments drop 052 references (expert-concordance 12→11, unresolvable support 7→6), realigning manifest/tests/summary with the gold-set README's already-described 51-tuple set. #250 stays open — the verified-real-but-unindexed canary is still genuinely unfilled; only a mislabeled synthetic proxy was removed. - ACL/EMNLP disclosure rows regrounded to the ACL Admin Wiki canonical source (#242). The ACL disclosure row pointed at the 2023 conference blog (still live, HTTP 200) but its content had drifted from ACL's current Exec-approved policy. The Admin Wiki — which ARR / EMNLP 2026 link to for current paper-integrity guidance — places disclosure in the Acknowledgements section and graduates it by use type, contradicting the old row's "dedicated Use of AI Assistance subsection". First-party verification: the Admin Wiki returns HTTP 200 via browser navigation with the full "Guidelines for Generative Assistance in Authorship" section present (the 418 reported in #242 was a curl-UA challenge, not a stable block; the repo has no CI link-checker, so the humans-vs-tooling URL tension recorded in #242 does not apply). The ACL row's Source URL → Admin Wiki anchor (access date 2026-06-07) with summary / required phrasing / disclosure location / prohibited uses / authorship / notes regrounded from the first-party wiki text and graduated per its clauses a–f (language-only and short-form input not disclosed; literature search needs no special disclosure but normal citation-accuracy rules apply; low-novelty text and AI-suggested ideas disclosed); the EMNLP sibling row → the EMNLP 2026 Paper Integrity Policy page (which defers to ACL's guidelines) and is consolidated to "see ACL row";
disclosure_mode_protocol.mdprose aligned to Acknowledgements. An independent cross-model faithfulness pass against the first-party pages corrected two fluent-wrongness overstatements (the literature-search no-disclosure bucket; an EMNLP "adopts wholesale" claim). - Stale Opus 4.7 primary-model strings retired + repro_lock run-time fields documented as placeholders (#347). The 2026-06 harness-retirement audit (#301) found the agent prompts carry zero expired scaffolds, but two
shared/files still pinned Opus 4.7 as the primary model after the 4.7→4.8 migration, and arepro_lockexample hard-coded run-time snapshot values that readers copy verbatim.shared/cross_model_verification.md: primary model → Opus 4.8, with the primary "API ID" cell now reflecting that it is the inherited Claude Code session model rather than asserting an unverifiedclaude-opus-4-8id string (cross-verifier idsgpt-5.4*/gemini-3.1-pro-previewconfirmed current and left concrete), plus a note documenting why temperature is 0.1 (deterministic fact-check), closing the "undocumented sampling override" read.shared/artifact_reproducibility_pattern.md: therepro_lockexample block uses placeholders for the three run-time snapshot fields (ars_version,model.id,s2_api_protocol_version) so a copy-paste records the actual run, not a stale literal (feature-introduction labels likev3.3.5+stay concrete).examples/passport_with_repro_lock.yamlleft unchanged — a self-consistent historical snapshot, not a stale current-marker.
[3.11.1] - 2026-06-06 — Post-ship correctness, hardening, and provenance fixes (#182 follow-up)
A patch release rolling up the post-ship advisory fixes surfaced after v3.11.0: a
cross-model consent-gate extension to the integrity + collaboration paths (#322), a
per-entry backfill parallelization (#138), and seven correctness/hardening fixes across
the citation-existence gate, the v3.10 policy layer, the eval harness, the domain
evidence profiles, and the #310 security-boundary edge cases (#323/#327/#328/#329/#331/#332/#333).
No new features and no breaking schema changes. One API note: the #332 verify_citation/
verify_passport signature gains required keyword-only parameters. This is a fix to a
contract-violating code path that first shipped in v3.11.0, not a deliberate signature
revision — the old signature emitted a schema-invalid ref_slug: null, so any v3.11.0
caller relying on it was already producing contract-invalid output. The only in-repo
callers (the CLI + the internal verify_passport→verify_citation call) are updated in
lockstep; see the #332 entry below for the full C-V4 rationale.
Security
- Cross-model consent gate extended to the integrity-verification and collaboration-depth paths (#322). The explicit-consent gate that fronts every
ARS_CROSS_MODELupload — established for the two Devil's Advocate paths in #310 — now also fronts the two remaining agent paths that send user-derived material to an external provider on the env var alone:integrity_verification_agent(sampled citation/reference metadata) andcollaboration_depth_agent(raw dialogue turns, which can carry the user's private reasoning and unpublished material). The gate is also added at thepipeline_orchestrator_agentre-dispatch point so the observer's agent-internal gate cannot be bypassed at the orchestration layer (defense in depth). All three mirror the #310 wording: no automatic send, explicit user consent identifying provider + model + content class,[CROSS-MODEL-SKIPPED]+ single-model fallback when consent is declined, and a backpointer toshared/cross_model_verification.md. Thecollaboration_depth_agentadvisory-only / never-blocks contract is preserved — the gate gates only the upload, never the observer's scoring role. Agent-prompt text only; no schema or script change.
Performance
- Parallelize the OpenAlex + Crossref backfill lookups per entry in
migrate_literature_corpus_to_v3_9_0.py(#138). When bothopenalex_unmatchedandcrossref_unmatchedare missing for an entry, the two independent resolver calls (different hosts, per-instance throttle state, monotonic timing) now run concurrently via a 2-workerThreadPoolExecutorinstead of one-after-the-other, roughly halving per-entry network wait on a full backfill. Scope is deliberately bounded: only the two calls within one entry overlap — the corpus loop stays sequential (cross-entry parallelism is out of scope; the clients' per-instance throttle assumes serial use), all passport mutation / report bookkeeping / degradation logging stays single-threaded on the orchestrator thread, and an already-set field still never consults its client. A single missing field skips the pool and calls directly. Behavior is otherwise byte-equivalent to the sequential version, including the omit-on-Unavailablepartial-degradation contract (now surfaced viaFuture.result()). Adds 2 tests (barrier-verified parallel dispatch + the previously-untested API-down degradation path); the 6 existing migration tests pass unchanged.
Fixed
- Two edge-case correctness fixes from the #310 post-merge review (#323, closes #324). Post-merge
codexreview of #310 (security-boundary hardening) surfaced two issues #310's happy-path/crash-free tests did not catch, both verified first-party before fixing. (1) Inscripts/adapters/folder_scan.py, a symlink escaping the input root wrotereason: symlink_outside_input_roottorejection_log.yaml, but that value is not in therejection_log.schema.jsonreasonenum — so the rejection log was contract-invalid exactly in the new symlink-rejection path. It now usesother+detail(schema-valid; the schema'sallOfrequiresdetailwhenreason == other). (2) Inscripts/bootstrap_timeline_yaml.py, the lookup queried…/works/{quote(doi)}(encoded) butsource_locatorrecorded…/works/{doi}(raw), so provenance named a URL that was never queried — affecting every DOI, not only reserved-character ones (/encodes to%2F). It now records the encoded DOI to match the queried URL. Tests strengthened to assert emitted content (the rejection log isjsonschema.validate()d; a new test pinssource_locatorto the encoded lookup URL), not just exit code. - Domain evidence profiles wired end-to-end (#327). Three feature-logic gaps from the #259 post-ship review that survived on
mainbecausecheck_domain_evidence_profile.pyonly verified documentation-surface presence (C1–C7), never the control-flow bound, the consumer parse logic, or the date-gate semantics. [P1] Step 12 (the profile producer) was orphaned from the no-handoff flow directive (bounded at "Step 1-11"), so the profile silently never activated on the common path;intake_agent.md's directive now affirmatively reaches Step 12 (new lint C8). [P2] The reserved-fallback rowunknown_user_defined (requested: <reserved>)was misparsed as case (c), emitting a wrong[PROFILE-UNRESOLVED]malformed signal; the consumer now parses the effective token + parenthetical and emits a new[PROFILE-RESERVED-FALLBACK], with (c) narrowed to genuinely unresolvable rows (new lint C9). [P2] The currency (time-range) node was not profile-aware, so a canonical humanities source admitted at the peer-review node was re-excluded at the currency node (INVARIANT 5 violation); the currency node gains a humanities admit branch (purely additive — union/loosen-only, continues through the universal relevance + methodology gates, never short-circuits to Include) (new lint C10). TDD with a RED mutation fixture per defect. - Eval-harness gates honor binding per-class thresholds and exclude non-measured tasks (#328). Two correctness holes in the #263 eval-harness CI gates, invisible to the suite because no fixture exercised them. [P1]
scripts/_eval_threshold_gate.pyfailed_tasks()inspected onlyaggregate_metric.passed, but manifests declare binding per-class thresholds distinct from the aggregate (e.g. citation_extraction aggregateaccuracy ≥ 0.90and per_classaccuracy ≥ 0.85); a PR regressingcitation_extraction.false.accuracybelow 0.85 while the aggregate stayed ≥ 0.90 passed the gate when it should block.failed_tasks()now also iteratesper_class, keyed<task>.<class>.<metric>. [P2]scripts/check_ranking_lift.py_flatten_report()flattened any task carrying anaggregate_metricwith no status filter, so a not-yet-landed task's placeholdervalue: 0.0entered the lift baseline as a real metric — once the task landed, its real value hit the zero-baseline branch and was spuriously flagged as a regression. Both consumers now share the same positivestatus == "measured"skip-guard so a future status (e.g."error") is excluded consistently. Addsscripts/test__eval_threshold_gate.py(11 cases) + 5_flatten_reportstatus-filter tests. - v3.10 policy layer: laundering guard wired to real entries + per-block terminal-marker validation (#329). Two P2 enforcement/grammar gaps in the shipped v3.10 triangulation policy layer; the 45 policy-layer tests passed because each guard was only exercised in isolation, never wired to the surface it protects. [P2]
assert_venue_type_source_clean(rejects avenue_type_sourcenaming a lookup index undertrusted_source_declared) had no production caller — the entry schema's own description promises "enforced by check_v3_10_policy.py" but nothing ran it over real entries, so a passport laundering a k=3-unmatched signal into a declared-trust signal passed both validators. It is now wired intocheck_literature_corpus_schema.validate_passport's entry loop (a laundered source fails; a legitimate publisher/registry feed name still passes; string-guarded so a non-stringvenue_type_sourcesurfaces as a clean schema error, not a.strip()traceback). [P2]is_well_formedaccepted a terminalTERMINAL-BLOCKmarker missing the mandatorypolicy/reason/mode/policy_hashfields;_parse_innernow keeps per-block metadata andis_well_formedvalidates each block independently plus the marker-levelpolicy_hash, so a complete later block can no longer mask an earlier block's stripped metadata (C-V6(g) multi-policy co-emission handled correctly). - arXiv resolver no-ID skip + non-Atom 200 guard + miss-safe cache decode (#331). Three post-ship defects in the #182 Delta 1+2 citation-integrity data layer (arXiv resolver + verification cache), all verified first-party; the 106 PR tests never exercised these paths. [P2]
resolve_arxiv_unmatchedran a title search for citations with noarxiv_id(e.g. a DOI-keyed journal article) and returnedtrueon a title miss — inflating triangulationk(k=3→k=4, renderingCONTAMINATED-QUADRANGULATION-UNMATCHEDon a clean journal citation) plus a wasted ~3s request; it now skips the resolver whenarxiv_idis absent, matching the spec's ID-gatedskippedrule and the guard already inverification_gate._run_arxiv. [P2] A well-formed non-Atom 200 body (e.g. a proxy/CDN HTML error page) parsed cleanly and its empty entry list was cached as a real 90-day miss;arxiv_client._getnow validatesroot.tag == {atom}feedand raisesArxivUnavailable(omit-on-degradation, not cached) on a non-feed root, while a genuine empty Atom feed still resolves to a miss. [P3]VerificationCache.get's barejson.loadsaborted verification on a corrupt/non-dict payload; it now treatsJSONDecodeError/TypeError/non-dict as a miss (clean recompute), honoring the documented "malformed cache payload = miss" contract. Two tests that codified the buggy behavior were reversed. verification_gatereadsref_slugfrom the prose join, not the corpus entry (#332).verify_citation/verify_passportpreviously wrotesummary.ref_slug = entry.get("ref_slug"), butliterature_corpus_entry.schema.jsonisadditionalProperties: falsewith noref_slugproperty — so the normal (schema-valid) passport path emittedref_slug: nulland violated the summary contract (a required string). Two non-schema-conformant test fixtures masked it.ref_slugis now an explicit prose-sourced parameter parallel toanchor:verify_citation(entry, clients, *, ref_slug, anchor=None, …)andverify_passport(passport, clients, *, ref_slug_by_key, anchors=None, …), with aValueErroron any invalid join — a missing key, or a present-but-empty/non-string slug (validated once at theverify_citationemission point via a shared_is_valid_ref_slugso the per-citation and passport layers can't drift; the passport layer re-checks only to name the offendingcitation_key) — rather than a contract-invalid summary. The standaloneverify_passport.pyCLI (which has no prose document) now refuses by default with a clear error and offers an explicit--synthetic-ref-slug citation_keydiagnostic escape hatch instead of silently fabricating a slug. API-stability note (C-V4): these are new required keyword-only parameters. The spec's C-V4 freeze names v3.10.0, but #182 was specced-but-not-implemented in v3.10 (spec §0 amendment) and first shipped in the v3.11.0 minor release — so no v3.10.0 caller can depend on the old signature, and C-V4 itself permits a minor release to add required fields. The only in-repo callers (the CLI + the internalverify_passport→verify_citationcall) are updated in lockstep.check_evals_gold_setenforcesstatus↔queried_bycoherence via the shipped schema (#332). The gold validator's flatqueried_by ∈ {id, title, null}enum check under-enforced the conditional coherence the summary schema requires (a ran resolver must carryid/title, a skipped/unreachable one must carrynull, andqueried_bymust be present). It now validates eachresolver_outcomeagainstcitation_verification_summary.schema.json's$defs.resolver_outcome— single source of truth, matching the existing I9b reduce-and-compare philosophy — and the now-deadSTATUS_ENUM/QUERIED_BY_ENUMconstants are removed. The shipped gold set already satisfies the stricter check.- Citation-existence advisory visibility + terminal-marker grammar reconciliation (#333). Two P2 self-consistency issues in the #182 citation-existence gate, neither a gate hole (the formatter's generic
severity=HIGH-BLOCKrefusal catches the strict token regardless). Item 1 (#342): the spec was internally self-contradictory — C-V6(b) claimed an advisorylookup_verified == falseis BOTH "byte-equivalent to v3.9.x" AND "co-emitted in the ref marker", impossible for a firing row, and a second advisory marker token has nowhere to go (the v3.7.3 grammar caps one advisory slot, already taken by contamination'sCONTAMINATED-*suffix). Resolved by a third path: the marker stays byte-equivalent (no new suffix, no grammar churn), and the advisory's visibility is carried in the output package instead —formatter_agent.mdnow requires a mandatoryprovenance_summary.mdCitation Existence Advisoriessection listing every advisoryfalserow, andprovenance_summary.mdis added to the Output Package Files Delivered table so the carrier can't be dropped. Every "co-emitted in/alongside the advisory annotation" claim was removed from spec §0 / C-V6(b)/(c)/(e) / Rule 12 (the contamination strict clause, which legitimately does co-emit a suffix, is untouched); new C-V6(b) lint incheck_v3_10_policy.py+ 3 mutation tests. Item 2 (#338): the canonical "Two marker grammar shapes" terminal enumeration inpipeline_orchestrator_agent.mdlistedpolicy=<contamination_triangulation|temporal_integrity>, omittingcitation_existenceeven though the finalizer prose just below emitspolicy=citation_existencetokens; the enumeration is extended and themode=clause reconciled per-policy (citation_existenceisstrict-only), + 2 parser fixtures.
[3.11.0] - 2026-06-04 — Deterministic citation verification gate (#182)
The v3.11.0 minor release ships #182 — a deterministic citation-existence verification gate
that runs independently of LLM peer review. It cross-checks every cited reference against up to
four bibliographic indexes (Semantic Scholar + OpenAlex + Crossref + the new arXiv resolver) and
surfaces a per-citation lookup_verified status, so a fabricated citation with a provably-bogus
DOI/arXiv ID is caught by deterministic lookup rather than by hoping a reviewer agent notices.
The gate inherits the v3.10 terminal_policies opt-in model — default advisory, opt-in
strict — rather than introducing a second hard-block philosophy: detection always runs and
populates the summary, but a lookup_verified == false row is terminal only under
terminal_policies.citation_existence == strict. Default behavior is non-blocking (advisory,
/ars-mark-read-acknowledgeable); a user must opt into strict to make existence-failure
terminal. The false definition is deliberately narrowed to ID-keyed unmatched (an exact
DOI/arXiv lookup that provably fails), so a legitimately-unindexed humanities / non-English /
regional citation with only a title-unmatched stays unresolvable and never blocks (C-V6(a); an
acknowledged precision-over-recall tradeoff documented in the spec, mirroring strict_articles_only).
Five delta items (#182):
- Delta 1 — arXiv API resolver + four-index contamination rendering. New
scripts/arxiv_client.pyverifies citation existence againstexport.arxiv.org(metadata + existence; no API key, no polite-pool email — built-in rate-limit pacing per arXiv ToU; accepts both old-stylehep-th/9711200and new-style2605.07723IDs).scripts/contamination_signals.pyextends the v3.9.0 cross-index triangulation advisory matrix from three indexes (k=0..3) to four (k=0..4) with anarxiv_unmatchedsignal, and the orchestrator finalizer + formatter render the four new advisory suffixes (CONTAMINATED-ARXIV-UNMATCHEDat the k=1/k_max=1 arxiv-only carve-out;CONTAMINATED-QUADRANGULATION-UNMATCHEDat k=4/k_max=4; plus their twoPREPRINTcompositions). All advisory — the terminal gate / refusal list is unchanged (R-L3-2-E).arxiv_unmatchedfield added toliterature_corpus_entry.schema.json. - Delta 2 — persistent verification cache. New
scripts/verification_cache.py— a local SQLite store (~/.cache/ars/verification.db, override viaARS_VERIFICATION_CACHE_PATH; WAL mode; 90-day TTL) keyed by(citation_key, resolver_name, query_form), so the same paper cited across drafts is verified once. Each resolver entry point (crossref / openalex / S2 / arxiv) gains an optionalcacheparameter. New/ars-cache-invalidate <citation_key>command removes every cached row for a key (idempotent no-op when absent). - Delta 3 / C-V6 — citation-existence terminal policy. New
terminal_policieskeycitation_existence(closed enum{advisory, strict}, per-key absence = advisory) interminal_policies.schema.json, alongsidecontamination_triangulation. This replaces the original Delta-3ARS_CLAIM_AUDITdefault-flip as the gate's on/off control. The finalizer is the sole policy evaluator;formatter_agent.mdrule 12 refuses on alookup_verified == falserow only understrict, co-emitting[UNVERIFIED CITATION — lookup_verified=false: ...]alongside the advisory annotation.HIGH-BLOCKis terminal — not/ars-mark-read-clearable. Manual entries structurally exempt. - Delta 4 — unified per-citation status surface. New
shared/contracts/passport/citation_verification_summary.schema.json+scripts/citation_verification_summary.pywrite alookup_verified(enum{true, false, unresolvable}) +anchor_present+resolver_outcomes(per-resolver{matched, unmatched, unreachable, skipped}) row per citation. The classification is anti-fabrication-biased (one ID-keyedunmatchedis positive evidence of non-existence; a single transient outage does not cancel it) and thefalseform is narrowed to ID-keyed unmatched per C-V6(a). - Delta 5 — standalone
verification_gateAPI. Newscripts/verification_gate/__init__.pyextracts the gate logic into a callable API composing the four resolvers + the unified summary writer (a second caller of the same lower-layer infrastructure as the v3.8 audit, not a duplicate). Newscripts/verify_passport.pyCLI runs the gate over a Material Passport standalone.
Lint + CI:
scripts/check_v3_9_0_triangulation.py(the canonical cross-version contamination-suffix oracle) rule 1 upgraded from subsection token-presence to a matrix-row oracle: each Delta-1 token must sit on the finalizer suffix-table row carrying its exact(k, k_max)cell, so deleting or mistokening an operational row fails even when the same token survives in surrounding prose. The formatter pass-through allowlist set-equality oracle extends 9 → 13 tokens.scripts/_ci_pytest_manifest.tomlbackfills 5 data-layer test entries (citation-verification-summary / verification-gate / arxiv-client / verification-cache / verify-passport-cli) that shipped with the data layer but were not wired into the manifest runner at the time.
Spec: docs/design/2026-05-21-v3.10-182-promote-citation-gate-spec.md (§0 v3.11 amendment +
INVARIANT C-V6).
[3.10.0] - 2026-06-01 — Triangulation policy layer, Kong et al. survey adoptions, eval harness, scoped-write guard
The v3.10.0 minor release bundles the opt-in contamination-triangulation terminal policy
layer (#127 PR-B — default behavior byte-equivalent to v3.9.0), several Kong et al. 2026
survey adoptions (Rebuttal Commitment Ledger #256/#266/#268/#269, discipline-relative
domain evidence profiles #259), the v3.10 measurement infrastructure (generalized eval
gold set + ranking-lift gate, #184), the #134 scoped-write guard MVP (a deterministic
PreToolUse hook fencing the 23 single-phase agents to their own phase directory; all Bash
denied for those agents), the /ars-mark-read plugin commands (#190) + a broken-on-arrival
fix (#195), a Simplified-Chinese README (#185), and CI hardening (#156/#155). Default
citation-policy behavior is byte-equivalent to v3.9.0 unless a user opts into a strict
mode (#127). The one default-on behavior change is #134's PreToolUse write-scope guard:
the 23 single-phase agents are now fenced to their own phase directory and denied Bash —
this constrains those subagents, not the user-facing skill outputs.
v3.10 triangulation policy layer (#127 PR-B — opt-in terminal modes, default behavior byte-equivalent to v3.9.0):
- #127 PR-B — terminal policy layer. Ships the contamination-triangulation policy layer deferred by v3.9.0 (#102): opt-in
strictmodes that promote the advisory k=3 triangulation signal to a non-acknowledgeable terminalHIGH-BLOCKat the citation-emission boundary. Default behavior is byte-equivalent to v3.9.0 — an absent or all-advisoryterminal_policiesblock changes nothing (Invariant 7). Built on PR-A's canonical firm-rules + sync-lint base.- Schema. New passport-level
shared/contracts/passport/terminal_policies.schema.json(standalone, NOT inside the entry schema — Invariant 11):contamination_triangulation∈ {advisory,strict,strict_articles_only};temporal_integrityaccepts onlyadvisory(forward-reserved namespace — a schema-accepted temporalstrictwith no wired behavior would be a false-safety bug, Invariant 3).literature_corpus_entry.schema.jsongainsvenue_type(closed enum incl. explicitunknown),venue_type_provenance(closed enum; the API-_inferredvalues are deliberately absent per R-L3-2-D), andvenue_type_source(required ifftrusted_source_declared). Pair dependencies: type ⟺ provenance (bidirectional);venue_type == unknown ⟹ provenance == unknown(one-way — a known type may carryunknownprovenance, no data loss). All adapter-declared only; never inferred from free-formvenue.check_literature_corpus_schema.pyextended to validate a passport-levelterminal_policiesblock before iterating entries. - Finalizer (sole policy evaluator).
pipeline_orchestrator_agent.mdgains a## Cite-Time Provenance Finalizer — v3.10 extensionsection. Under a non-advisory passport it stampspolicy_hash=<slug>on every ref marker (a fully-encoded human-readable canonical token of the non-advisoryterminal_policieskeys — sortedkey.valuejoin — so two distinct configs never collide). Under an all-advisory passport NO stamp is emitted: the marker is the bare v3.9.0 shape (byte-equivalent, Invariant 7) — the absence of a stamp is the advisory signal. Understrict, a k=3 ref co-emits aTERMINAL-BLOCK severity=HIGH-BLOCK policy=... reason=... mode=... policy_hash=...token ALONGSIDE (not replacing) its advisoryCONTAMINATED-*suffix, so the "why" survives.strict_articles_onlyis a deliberate PRECISION mode — k=3 promotes only when DOI present ∧venue_type ∈ {journal-article, conference-paper}∧ declared provenance; a DOI-less or unknown-venue journal article stays advisory by design (humanities / non-English / regional coverage gap). Audit trail gains aterminal_blocked[]bucket; aggregate counts dedupe by ref slug across advisory + terminal buckets (non-additive). Manual-entry exemption preserved (k=3 structurally unreachable).HIGH-BLOCKis terminal —/ars-mark-readdoes NOT clear it. - Formatter (STAMP-ONLY two-gate).
formatter_agent.mdgains refusal rule 11 (genericseverity=HIGH-BLOCKinside a<!--ref:...-->, NOT a per-subtype list) plus a## Cite-Time Terminal Policy Gate (v3.10)section. Two ordered gates, never short-circuited: Gate 1 freshness (stamp mismatch / missing-stamp-under-non-advisory →[STALE-POLICY-EVALUATION]; missing-stamp-under-advisory passes, Invariant 7), Gate 2 HIGH-BLOCK refusal applied to every gate-1-passing marker (a stripped-stamp marker still carryingTERMINAL-BLOCKis still refused). The formatter never re-evaluatesstrict_articles_onlylogic (Invariant 13 — the finalizer is the sole evaluator). A bare-proseHIGH-BLOCKoutside any ref marker never refuses (Invariant 12). v3.9.0 advisory pass-through allowlist unchanged. - Firm rule. R-L3-2-A reworded in
firm_rules.mdto the broad default-advisory + opt-in-strict form (covering contamination AND the forward-reserved temporal namespace; the wording explicitly states no temporal strict path exists yet, no over-promise). Contamination mirrors stay intentionally by-ID references (not full-block copies); the wording is single-sourced in the canonical block.check_firm_rules_sync.pygains a contradiction guard scoped to the R-L3-2-A reference sentence in each contamination-context file (rejects unqualified "advisory only / never block" claims now that strict can block) — deliberately NOT scanning the whole file, so the Collaboration Depth Observer's legitimate "never blocks" wording is not false-flagged. - Migration + adapters.
scripts/migrate_literature_corpus_to_v3_10.pyseeds passport-levelterminal_policies(deep-merge — only absent keys, idempotent, dry-run; never backfillsvenue_typefrom free-formvenue; clear error on a non-mappingterminal_policies; pre-v3.9.0 passports reported out-of-scope, not silently skipped). The three reference adapters (folder_scan/zotero/obsidian) now declarevenue_type+venue_type_provenance(Zotero item type →adapter_declared; folder_scan →unknown/unknown; obsidian honors a frontmattervenue_typeasuser_declared, elseunknown/unknown). - Lint + CI. New
scripts/check_v3_10_policy.py(runs ALONGSIDEcheck_v3_9_0_triangulation.py, not a rename) covers the schema fields, the_inferred-rejection, the pair dependencies, the trusted_source laundering guard, the standalone schema home, the marker grammar (with a reusable parser + the five required fixtures: terminal co-emit / non-terminal advisory / non-terminal clean / legacy-no-stamp / bare-prose-no-refuse), the generic rule-11 shape, the formatter STAMP-ONLY two-gate, and the closed enums. Wired intospec-consistency.yml+_ci_pytest_manifest.toml. Spec:docs/design/2026-05-31-ars-v3.10-policy-layer-rescope-spec.md.
- Schema. New passport-level
Added
- #134 Slice 1 — scoped-write guard MVP (the Active Conductor rescope). New
PreToolUsehookscripts/ars_write_scope_guard.pyfences the 23 single-phase (Bucket A) subagents to their own phase directory: forWrite/Edit/MultiEditit normalizes the single top-levelfile_path(realpath, so../symlink traversal resolves in true filesystem order), denies workspace escapes, unconditionally protects the enforcement surface (hooks.json, the hook/manifest/lint, agent definition files,.claude/CLAUDE.md), then enforces the agent'sallowed_write_globswith a segment-aware iterative glob matcher (*never crosses/;dir/**is descendants-only; no recursion-limit crash on deep paths). All Bash is denied for a Bucket A agent — it uses the Grep/Glob tools to search and the structured editing tools to write. (The spec's "best-effort literal-target Bash" was taken to its sound conclusion: neither "this Bash writes a file" nor "this Bash is read-only" can be decided reliably from a command string without a sandbox, so all-deny is the only zero-fail-open Bash policy; spec carries an Implementation-outcome note + aligned §3.2/§3.3 wording.) Backed byscripts/ars_phase_scope_manifest.json(machine-readable scope for the 23 agents) and the fail-open guard lintscripts/check_v3_10_134_write_scope.py(three-way name cross-check: classification roster == manifest keys == on-disk frontmatter names, + filesystem exhaustiveness at any nesting depth, so rename/typo/new-agent drift can't silently fail the hook open).hooks.jsonPreToolUse wiring + CI steps (lint + hooks.json wiring assertion) + pytest manifest entries; TDD throughout with lint mutation tests. The structured-tool determinism is the load-bearing win; the Bash deny closes the direct-shell-write path for fenced agents entirely. Slices 2-5 (write-provenance ledger, task envelopes, return contracts, persistent conductor) remain forward-scope. Spec:docs/design/2026-06-01-ars-134-conductor-rescope-deterministic-write-guard-spec.md. Closes #134. - Kong A4 (#259): Discipline-relative domain evidence profiles. New
academic-paper/references/domain_evidence_profiles.mddefines 4 ship-ready profiles (cs_ml,general_social_science,humanities_interpretive,unknown_user_defined) + 5 reserved.intake_agentStep 12 emits a scholar-selectedDomain Evidence ProfilePCR row (never auto-selected; reserved selections fall back to neutral with a surfaced advisory).literature_strategist_agentresolves the row and applies loosen-only gate + upstream-filter changes — monotonic admit-only, and profile-admitted sources still flow through the universal relevance + methodology gates. Newscripts/check_domain_evidence_profile.py(C1–C7 documentation-surface lint, including a SHA-256 pin of thesource_quality_hierarchy.mdField-Specific Adjustments block) + mutation suite, wired intospec-consistency.yml+ the pytest manifest. Advisory only. Closes Kong et al. 2026 §7.4.6 domain-evidence-standards gap. - Kong A1 (#256): Schema 11 R&R Traceability Matrix gains
commitment_extracted/fulfillment_status/unfulfilled_rationaleoptional fields.revision_coach_agentStep 3.5 extracts commitments;re_review_mode_protocolstep 5 verifies + surfacesCOMMITMENT_GAPadvisory. Worked example atacademic-paper/examples/commitment_ledger_example.md. Calibration seed atevals/calibration/commitment_ledger_seed.yaml(10 cases). Advisory only — author retains final responsibility. Closes Kong et al. 2026 §7.4.3 commitment-fulfillment gap. - Kong A1 follow-up (#269): Schema 11
required_evidence_typeenum widened from 7 to 9 values, addingprose_editandother.prose_editis a seventh manuscript-evidence type for sentence-/paragraph-level changes too granular to bucket structurally (typo fixes, terminology clarifications, equation formatting, citation-style corrections); it verifies atrevision_locationlike the other manuscript types.othermirrors the existingcommitment_typeescape hatch for genuinely uncategorizable evidence and triggers a new softEVIDENCE_TYPE_UNSPECIFIEDadvisory at re-review (orthogonal toCOMMITMENT_GAP; fires wheneverrequired_evidence_type == other, regardless of fulfillment status). The prior 7-value closed set forced typo-level comments into wrong buckets (methods_paragraph) or out of the ledger entirely, violating the every-comment extraction rule. Synced acrossshared/handoff_schemas.mdSchema 11,revision_coach_agentStep 3.5,re_review_mode_protocolCommitment Ledger Verification, andrevision_tracking_template.md; worked example and calibration seed (now 12 cases, +E1/E2) extended. Advisory only. Surfaced by Gemini R3 review of PR #264, Finding 3. Closes #269. - Kong A1 follow-up (#268): Schema 11 Commitment Ledger refactored from three index-aligned parallel lists (
commitment_extractedobjects + top-levelfulfillment_status[]+unfulfilled_rationale[]) to a nested-object shape —fulfillment_statusandunfulfilled_rationalenow nest INSIDE eachcommitment_extractedobject. This makes length-mismatch / index-desynchronization structurally impossible, closing the Gemini R3 (PR #264) Finding 1 fragility where a dropped Markdown<br>or numbering error silently mispaired a status with the wrong commitment and produced a falseCOMMITMENT_GAPadvisory. REPLACE, not coexist (spec §2): the parallel-list shape is removed entirely — no executable consumer, lint, or fixture carried it (the #263 calibration harness is unshipped; the seed is a non-runnable seed), so coexistence would only preserve the failure mode. Lifecycle fields are absent at extraction time (revision_coach_agentStep 3.5) and appended per-object during revision execution; the oldunfulfilled_rationale: ""placeholder for fulfilled commitments is dropped (omitted, not empty-string). The equal-length validation invariant is retired (now structurally impossible); a legacy-normalization note instructs zipping any pre-#268 top-level arrays onto the nested objects before re-review. Synced acrossshared/handoff_schemas.mdSchema 11 (incl. the #266residual_actioncoherence prose, reworded fromunfulfilled_rationale[i]index notation to object-field notation),revision_coach_agentStep 3.5,re_review_mode_protocolCommitment Ledger Verification,revision_tracking_template.md(three fragile<br>-separated columns collapsed into one per-commitment nested YAML ledger), worked example, and the 12-case calibration seed.author_fulfillment_claim(Gemini's promised-vs-claimed-vs-verified split) deferred — not required for the structural fix (spec §2). Newscripts/check_268_nested_commitment_ledger.py(N1-N5 + N3b: seed extraction-field presence, no retired parallel-list keys, per-commitment lifecycle coherence via a_blank_rationalehelper that treats missing/null/whitespace uniformly, case-levelexpected_commitment_gaporacle coherence with a real-boolean guard, no surviving index notation) + 18 mutation tests, wired intospec-consistency.yml+ the pytest manifest. Advisory semantics unchanged. Surfaced by Gemini R3 review of PR #264, Finding 1. Spec:docs/design/2026-05-31-ars-268-schema11-nested-commitment-ledger-spec.md. Closes #268. - Kong A1 follow-up (#266): Schema 11
residual_action(concern-level) vsunfulfilled_rationale(per-commitment) coherence. Documented their semantic relationship (different granularity and tense —unfulfilled_rationale[i]is backward-looking and per-commitment,residual_actionis forward-looking and concern-level, so a row may carry both without redundancy or contradiction), the multi-commitment single-string shape convention (residual_actionstays one concern-level string, not expanded into a list), and are_review_mode_protocolnote that a populatedresidual_actionalongside somefulfillment_status[i] == fulfilledis not a contradiction, cross-referencing theshared/handoff_schemas.mdSchema 11 convention. Doc-only; advisory semantics unchanged. Closes #266.
Bug fixes (no version bump — corrects a broken-on-arrival behavior from #190):
- #195 —
/ars-mark-readcrashed on real YAML passports.scripts/ars_mark_read.py:_load_corpus_keysusedjson.load()to read the Material Passport, but every adapter (folder_scan / zotero / obsidian) and every other ARS tool produces / consumespassport.yaml. The existing 11-test fixture inscripts/test_ars_mark_read.pywrote JSON-formatted passports, so the suite was green while real-world/ars-mark-read smith2024 --passport-path ./passport.yamlexited withjson.JSONDecodeErrorbefore reaching citation-key validation. Two new TDD tests pin the adapter-format expectation (YAML happy path + YAML invalid-key hard error);_write_passporthelper switched toyaml.safe_dump. Companion P2 also closed: existing-but-unwritable read-log file now surfaces the canonical[ARS-MARK-READ ERROR: ...]fail-fast rather than a barePermissionErrortraceback, via an extraos.access(log_path, os.W_OK)check after the parent-W_OK gate. 14 ars_mark_read tests pass (was 11), full suite 1623 / 3 skipped. Surfaced by post-squash codex review of PR #191 (issue #192).
Plugin commands (prep for v3.10 — no behavior change to existing skills):
- #190 —
/ars-mark-read+/ars-unmark-readplugin commands. v3.6.8 spec §3.6 + Step 7 (round-2 R2-002, round-5 R5-003 amends) designed these commands as the user-facing affordance for the human-read signal, but the command surface itself was never shipped —commands/carried only the 10/ars-<mode>skill triggers. Newscripts/ars_mark_read.pydeterministic CLI implements the four §3.6 R5-003 fail-fast modes (no active passport / passport not found / parent unreadable / read-log unwritable), the §3.6 firm-rule-2 hard error on invalidcitation_key, batch-level all-or-nothing semantics (any invalid key rejects the whole batch), and the §3.6 firm-rule-3 append-only write to<passport-stem>_human_read_log.yamlnext to the active Material Passport./ars-unmark-readwritesrescinded_at: <ISO 8601>to the matching entry, never deletes. Two new thin markdown command files (commands/ars-mark-read.md,commands/ars-unmark-read.md) invoke the CLI via Bash; both declaremodel: sonnetrouting perfeedback_no_haiku.md. Newscripts/check_v3_6_8_mark_read_commands.pyCI lint per spec Step 7 acceptance: 2 commands exist, carry theliterature_corpus[]validation reference, reference thehuman_read_log.yamlpeer-file write target (NOT entry frontmatter, per §3.1 firm rule 3), and declaremodel: sonnet. 11 unit tests for the CLI + 6 unit tests for the lint./ars-list-readandcommands/ars-mark-read.zh-TW.mdwere spec-marked optional and remain deferred. Closes #190.
v3.10 measurement infrastructure (prep for v3.10 — no behavior change to existing skills):
- #184 Phase 1a — citation-extraction gold subset. New top-level
evals/directory holds v3.10 generalized gold-set corpora forverification_gate.verify_citationmeasurement targets. Shipsevals/gold/citation_extraction/with 50 hand-curated tuples (all populated in this PR) +manifest.yaml+expected_outcomes.json. v3.10.0 binding thresholds: aggregateaccuracy >= 0.90across 50 tuples, per-classaccuracy >= 0.85for each oftrue/false/unresolvable(changing requires spec amendment per #184 §3.1.1 / E-V2). Distribution: 20 valid_doi + 10 valid_arxiv + 5 manual_exempt + 15 fabricated (= 50). The originalvalid_unresolvablesource class was removed as unbuildable — no stable first-party-verifiable real-but-unmatched citation exists under current index coverage; tuples 031-040 were reassigned tofabricated; coverage gap tracked in #250. Tuple shape (locked per codex consult Q1-Q5): self-containedcorpus_entrymirroringliterature_corpus_entry,arxiv_idas tuple-level field (forward-looking — see #234 for #182 implementation alignment),human_expert_verdictoptional (10/50 = 20% per Delta 5),fabrication_intentboolean enforced on fabricated tuples. Newscripts/check_evals_gold_set.pyenforces 9 invariants (I1 set equality / I2 tuple_id ↔ filename / I3 kind distribution / I4 no-dup-JSON-keys / I5 label ↔ kind / I6 arxiv_id placement / I7 fabrication_intent marker / I9 resolver_outcomes shape / I10 corpus_entry schema) via 17 mutation tests on a 3-tuple clean fixture. CI step wired into.github/workflows/spec-consistency.yml. Spec:docs/design/2026-05-21-v3.10-184-extend-eval-harness-spec.md. - #184 Phase 1b — eval harness + ranking-lift gate. New
scripts/run_evals.pymulti-task harness (python -m scripts.run_evals [--task <name>] [--baseline <path>] [--compare <path>] [--output <report.json>]): discovers everyevals/gold/<task>/manifest.yaml, measures each task, and emits a report shaped by the newshared/evals_lift_report.schema.json(requiredharness_version/run_id/gold_set_version/per_task[]/caveats[]with the v3.8 honesty-disclosureminItems:1convention). Forcitation_extractionthe harness computes the predictedlookup_verified3-class enum itself from each tuple'sresolver_outcomes.*.statusvia the #182 Delta 4 reducer (verification_gate.verify_citationhas not shipped — reconcile when it does); the metric is symmetric 3-class accuracy,unresolvableis never collapsed intofalse. Forrq_framing_patternsit dispatches to the existingscripts/check_rq_framing_patterns.pyrunner and adapts its FNR / FPR / balanced-accuracy output into the per-task lift shape.--baseline+--compareproduce a side-by-side report carryinglift_pre/lift_post;expert_concordanceis emitted per class over the 10human_expert_verdict-labeled tuples (advisory, never gates per E-V3). Missing entrypoint module / Phase-2 gold set yields apending/skippednotice, never a traceback. Newscripts/check_ranking_lift.pylift gate: purecompute_signed_lift(baseline, compare, direction)(higher-is-better(compare-baseline)/|baseline|, lower-is-better numerator inverted, zero-baseline+inf/-inf); blocks on anysigned_lift < -0.05or zero-baseline change unless the PR body carries[ranking-regression-acknowledged]+ an OPEN issue URL and the declaredAffected metric: <task>.<class>.<metric>matches the observed change (E-V4); OPEN-issue check via a monkeypatchable_issue_is_openseam (never networks in tests). New CI workflow.github/workflows/eval-harness.yml(Delta 3 path filter; concurrency group includesgithub.event_name; OQ-3 skip-guard for absent Phase-2 gold sets; deterministic[eval-regression-acknowledged]+ OPEN-issue PR-body gate) and net-new.github/pull_request_template.mdEval-impact section. Tests:scripts/test_run_evals.py,scripts/test_check_ranking_lift.py,scripts/test_evals_citation_extraction.py,scripts/test_evals_lift_report_schema.py(incl. trivial-accept-all schema mutation). Spec:docs/design/2026-05-21-v3.10-184-extend-eval-harness-spec.md.
Localization (no version bump — no behavior change to skills):
- #185 — Simplified Chinese README. New
README.zh-CN.md(630 lines, mirroringREADME.zh-TW.mdstructure) translated by external contributor @xpfo-go (PR #181). Language switcher updated across the four READMEs (en / zh-CN / zh-TW / ja-JP);CONTRIBUTING.mdREADME sync guidance extended to four locales.scripts/check_spec_consistency.pyrefactored to share zh-TW / zh-CN logic viaZH_README_CONFIGStuple; both locales covered bytest_aligned_zh_cn_readme_passes+test_stale_zh_cn_badge_failsregression tests (symmetric with the ja-JP tests added in #170).
CI / infrastructure (no version bump — no behavior change to skills):
-
#156 — Unified pytest invocation manifest. Twelve
pytest scripts/test_*.pyinvocations in.github/workflows/spec-consistency.ymlare now declared inscripts/_ci_pytest_manifest.tomland run viascripts/run_ci_pytest_manifest.py. Drift guardscripts/check_ci_pytest_manifest.pyrejects (a) missingpath, (b) duplicateid, (c) duplicate(path, args), (d) malformedargs, (e) anypytest scripts/test_*.pyre-introduced in the workflow outside the runner.pip install pytestconsolidates from 12 redundant installs to one. 17 unit tests for runner + lint.python3 -m unittest scripts.test_*invocations stay inline (out of scope for #156). 41 disktest_*.pyfiles that the manifest does not list remain unclassified — separate follow-up. -
#155 — Re-attempt F4: harden
test-count-monotonic.ymlto fail on pytest collection errors. Both head and base count steps now capture pytest's exit code separately from the pipe, treat exit 5 (no tests collected) as a tolerable degenerate case, and fail the gate on any other non-zero exit. Previously, a2>/dev/null | grep -c '::' || trueswallow on the base step would silently set BASE_COUNT to 0 on a broken-import or fixture-missing error in the base commit, making the head-vs-base monotonic check vacuously pass. The original F4 fix landed in PR #153 commit8121dfaduring the v3.9.4.2 cycle but was reverted in4abf9dewhen it surfaced #154 (now closed by PR #158). With #154 fixed and #156 keeping CI test discovery clean, F4 v2 ships symmetrically across head and base.
[3.9.4.2] - 2026-05-19 — Post-ship hotfix for PR #149 CI discipline gates
Trigger: Codex post-ship review of PR #149 (7 CI discipline gates mechanizing the release-cycle review chain) surfaced 4 P2 findings. v3.9.4.2 hardens 3 of 4; the 4th (test-count-monotonic harden) was reverted because it surfaced a pre-existing scripts/ package issue, tracked as #154 (since fixed by PR #158) and re-attempt #155.
CI gate hardening (PR #149 + #153):
- F1 — harness-retirement scheduler context:
harness-retirement-monthly.ymladdsGH_REPOso scheduled runs have repo context forgh issue create(workflow was silently failing on cron without it). - F2 — release-cooldown tag filter:
release-cooldown.ymlfiltersPREV_TAGlookup tov*tags so non-release tags (e.g., legacy plugin tags) cannot bypass the cooldown gate. - F3 — release-cooldown hot-fix detection:
release-cooldown.ymlalso reads annotated tag subject + accepts thehot-fixspelling variant; v3.9.2 was previously a false-negative hotfix under the old detector. - F4 (reverted):
test-count-monotonic.ymlharden landed in8121dfaand reverted in4abf9dewhen it surfacedscripts/package import errors (ModuleNotFoundError: No module named 'scripts') — pre-existing latent defect masked by the prior2>/dev/null | || truepattern. Tracked as #154 (now closed by PR #158) and re-attempt #155.
Release-cooldown symmetry follow-up (PR #157):
- Override token
[skip-cooldown]now read from both the commit message AND the annotated tag message. This v3.9.4.2 tag itself is the self-bootstrapping fix — the gate correctly identified v3.9.4.1 (3h prior) as the previous hotfix and fired the 24h cooldown, proving F2+F3 work end-to-end. The override symmetry patch makes the tag shippable.
Closes: #152. Follow-ups: #154 (closed by PR #158), #155, #156.
[3.9.4.1] - 2026-05-19 — Post-ship hotfix for v3.9.4 temporal verification
Trigger: Codex post-ship review of v3.9.4 squash commit af09cf5 surfaced 4 real bugs that per-task subagent reviewers missed during v3.9.4 implementation. v3.9.4 tag remains immutable; v3.9.4.1 patches the verifier and schema layer + brings docs in alignment.
Bug fixes:
- #135 P1 (audit wiring):
audit()now passescitation_provenancethrough to_pass_2_anachronismand_pass_4_causal. When a ref slug hasconfidence: loworconflictin citation_provenance.yaml, the verifier emitsTEMPORAL-METADATA-MISSINGinstead of using timeline dates as arithmetic ground truth. v3.9.4 dropped citation_provenance on the floor — spec §3.4 first-party safety check was structurally broken. - #135 P1 (date parser):
_date_to_interval()now parses all schema-valid date shapes includingYYYY-MM(Crossref month-precision output) andYYYY-MM-DD..YYYY-MM-DD(interval precision used by effective_date_range). v3.9.4 only handled day/year/prose-month forms — schema-valid month/interval shapes raised ValueError and P2/P4 silently skipped the check via the existingexcept ValueError: continueguard. - #135 P2 (P4 direct-date binding): P4 now binds each side of a causal trigger to either a
<!--ref:slug-->marker OR a direct date capture in the sentence. v3.9.4 required refs on both sides, silently dropping sentences like "The 2026 policy enabled the 2020 rollout."bound_dates.sourcedistinguishestimeline_reffromdraft_capture;bound_refsis empty when both sides came from direct date capture. - #135 P2 (schema absent-property bypass):
citation_provenance.schema.jsonconfidence:highallOf branch now requires bothcrossref_issuedandpdftotext_cover_first_lineto be present in addition to non-null (then.requiredadded). v3.9.4 usedthen.propertiesonly, which doesn't fire when a property is absent — so entries withconfidence:highand both source fields omitted silently passed validation.
Documentation:
docs/ARCHITECTURE.mdupdated from stale v3.8.0 baseline to v3.9.4.1; Section 8 Evolution Timeline filled in v3.8.1 / v3.8.2 / v3.9.0 / v3.9.1 / v3.9.2 / v3.9.3 / v3.9.4 / v3.9.4.1 entries; Section 9 Skill Modes table aligned to current versions.- Suite-version needles aligned across MODE_REGISTRY.md, README.md badge + tag URL + section heading, README.zh-TW.md badge + tag URL + section heading, academic-pipeline/SKILL.md frontmatter,
.claude-plugin/plugin.json,scripts/check_spec_consistency.pyexpected-text constants,.claude/CLAUDE.mdskill suite table.
Test count: 1549 → 1561 (+12 net new tests covering all 4 fixes, 0 regression).
[3.9.4] - 2026-05-18 — Temporal Verification Layer (advisory)
External motivation: Issue #135 — LLM next-token objectives are systematically blind to deterministic factual classes including temporal ordering. v3.9.4 adds a deterministic advisory verifier at the Phase 4 → 5 boundary covering 5 failure modes.
Mechanisms:
- M1: new Phase 2 sibling
timeline_extraction_agentowningphase2_investigation/timeline.yaml+phase2_investigation/citation_provenance.yaml - M2: Phase 4 → 5 deterministic verifier
scripts/temporal_integrity_audit.py(5 passes) - M3: Temporal Integrity Iron Rule in
report_compiler_agent+draft_writer_agent - M6-minimal: First-party Crossref
issued+ pdftotext cover verification - M7-minimal: Date provenance + comparator materialization
- M5-stub: User-declared
version_family_idonly
Zero modification to literature_corpus_entry, claim_audit_result, claim_intent_manifest. bibliography_agent unmodified (F2 invariant). 3 new sidecar schemas (aggregate-level with $defs).
Coverage estimate: 55-70% baseline / 65-75% with M7 minimal (LLM extractor blindness on tuple extraction is structural; advisory architecture acknowledges this).
Out of v3.9.4 scope (deferred to v3.10): M4 reviewer integration, M5 full version discovery, M6 full PDF audit, M8 relation manifest, CC5 catalog-completeness semantics, hard-block policy, OpenAlex lookup.
Spec: docs/design/2026-05-18-ars-v3.9.4-temporal-verification-spec.md.
[3.9.3] - 2026-05-18 — Housekeeping (#128 §1-3, §5-6)
Pure refactor + one latent-bug fix carrying over from the v3.9.0 /simplify review backlog. The v3.9.0 cross-index triangulation client family (Semantic Scholar + OpenAlex + Crossref) shipped intentionally byte-equivalent across 3 client modules for code locality; now that the family is stable, the dedup prevents sibling drift when threshold tuning, normalization rules, or throttle measurement need adjustment.
Refactor — extracted helpers (no behavior change)
scripts/_text_similarity.py— extracts 4 helpers + 4 constants previously triple-implemented byte-equivalent insemantic_scholar_client.py/openalex_client.py/crossref_client.py:_PUNCT_TRANSLATION,_normalize_title,_similarity,_TITLE_SIMILARITY_THRESHOLD = 0.70,_BACKOFF_SECONDS = 2.0,_MAX_RETRIES = 3. 14 new tests on the shared module.scripts/_passport_yaml.py— extracts ruamel.yaml round-trip config (preserve_quotes = True,indent(mapping=2, sequence=4, offset=2)) +load_passport/dump_passportfunctions previously duplicated byte-equivalent inmigrate_literature_corpus_to_v3_7_3.py+migrate_literature_corpus_to_v3_9_0.py. 7 new tests on the shared module.contamination_signals._resolve_by_doi_then_title— private helper for the identical DOI-then-title control flow shared byresolve_openalex_unmatched(§3.4) +resolve_crossref_unmatched(§3.5). Both public wrappers preserve the v3.9.0 spec API surface; exception-type differentiation stays at the wrapper. 10 existing resolver tests verify byte-equivalent behavior.
Latent-bug fix — throttle measurement standardized on time.monotonic
- OpenAlex + Crossref clients now use
time.monotonic()for_throttle()elapsed measurement +_last_request_atanchor refresh, matching Semantic Scholar (which had standardized on monotonic per #115 R5-2). NTP / manual clock adjustments could pushtime.time()backward, producing negative elapsed and either inflated sleep (negative compared less than min_interval) or zero sleep — latent throttle-bypass / API-spam bug. Documented as a "maintenance smell" in #128 §6. - New tests (
test_openalex_client::test_throttle_uses_monotonic_clock+test_crossref_client::test_throttle_uses_monotonic_clock) lock NTP-safe semantics: throttle readstime.monotonicand never readstime.time.
Dual-path import infrastructure
- All 5 module-level cross-imports in
openalex_client.py/crossref_client.py/semantic_scholar_client.py/migrate_literature_corpus_to_v3_7_3.py/migrate_literature_corpus_to_v3_9_0.pyuse the dual-path try/except pattern (sibling-first, namespace-package fallback). Followsscripts/slr_lineage.pyprecedent but inverted for class-identity preservation (pytest uses sibling-path imports;SemanticScholarUnavailablefromscripts.contamination_signalsis a different class instance thancontamination_signals.SemanticScholarUnavailable). - Latent fix:
scripts.semantic_scholar_client+scripts.migrate_literature_corpus_to_v3_7_3are nowimport scripts.X-clean from repo root (were silently broken on main due to pre-existing absolute cross-imports). Caught by codex round-1 reasoning trace.
Deferred from #128
- §4 — parallelize OA + CR per-entry calls in v3.9.0 migration tool carried to #138 (target v3.9.4 or v3.10). Introduces new behavior + ThreadPoolExecutor + test-rebuild scope; incompatible with v3.9.3 patch boundary.
Regression status
- 1482 → 1505 passed + 3 skipped + 111 subtests (+23 new tests, 0 regression).
scripts/check_spec_consistency.py+scripts/check_version_consistency.pygreen.- 6/6
import scripts.Xpaths verified clean from repo root (3 from-OK-to-OK, 2 latent-broken-now-OK, 1 OK throughout). - Cross-model review: codex round 1 + 2 both 0 explicit findings (one P1 self-caught from R1 trace, closed pre-R2). Gemini 3.1-pro-preview round 1: 0 findings.
[3.9.2] - 2026-05-18 — Phase boundary hot-fix (#133)
Hot-fix for issue #133 (phase scope inflation). A user incident showed that ARS auto-dispatched a single-phase agent (bibliography_agent) when given ambiguous cross-phase input (pre-written abstract + pre-collected literature), and the dispatched agent then autonomously executed Phases 3-6, skipping mandatory independent crosschecks (DA / EIC / Ethics).
This release ships the prompt-discipline + advisory-verifier hot-fix. The deterministic gate (PreToolUse hook + multi-phase task envelope schema + author provenance) is tracked separately as v3.10 active conductor (#134) — long-term architectural fix.
Design history: 4 design rounds (v1-v4) + mid-impl review. Triple-track reviewer use cases (codex review --base main + inline opus subagent + self-review). Codex 0.130 broke on this repo context 5x consecutive per memory feedback_codex_0_130_docs_review_broken.md (49 files / 1529 lines on full branch is firmly in the broken corner); inline opus was the substantive reviewer throughout. Net effect: design has been challenged thoroughly; honest framing applied where prompt-only mitigation is known insufficient.
Added
- Routing Discipline (Phase L1) —
.claude/CLAUDE.mdgains a new "Routing Discipline (v3.9.2)" section before existing Routing Rules 1-5. 3 routing classes: explicit intent → proceed directly; cross-phase materials → clarify with a-d options; no-materials ambiguous → clarify.[direct-mode]byte-0 escape hatch (case-insensitive; bracket-form strict). Anti-pattern explicitly named. - Intent clarification protocol — new
shared/references/intent_clarification_protocol.md(~200 lines): trigger condition table, pipeline phase reference (Phase 0-7 marker conventions), clarification message template (a-d options, no AskUserQuestion tool),[direct-mode]mechanism spec with 5 worked examples, v3.10 carry-over notes. - Phase Boundary block on 22 Bucket A agents (Phase 1) — single-phase agents (deep-research × 9, academic-paper × 7, academic-paper-reviewer × 6) gain a
## Phase Boundary (v3.9.2)block customized per agent: phase number, deliverable type, MUST-NOT cross-phase writes, MAY-READ upstream context (Phase 5 reviewers granted explicit cross-phase READ for review), explicit coexistence with skill-specific protocols (v3.6.2 / v3.6.5 / v3.6.6 / v3.6.7 / v3.7.1). 16 Bucket B/C/D agents (multi-phase / phase-orthogonal / cross-phase-meta) intentionally NOT fenced — honest framing per opus HIGH-2 (placebo prose creates false-enforcement illusion). - Phase-by-phase invocation contract (Phase 3) — 4 SKILL.md files gain a "Phase-by-phase Invocation Contract (v3.9.2)" section: Mode A (orchestrator-driven, default) vs Mode B (phase-by-phase cross-session resume), Bucket A enforcement scope, coexistence with skill-specific protocols.
- Advisory verifier (Phase 4) — new
scripts/check_pipeline_integrity.py: scans working directory forphaseN_*/(N=1-6), flags STRUCTURAL finding when phase5 dir lacks DA/EIC/Ethics filenames (the #133 pattern). HEURISTIC adjacent-phase-mtime rule (--strict, default OFF). Cross-platform, user-invokable, advisory output (exit 0 on findings), JSON + text output modes. Normative filename convention documented; v3.10 envelope provenance replaces filename matching. - Phase Boundary coverage lint (Phase 5) — new
scripts/check_v3_9_2_phase_boundary.py: enforces 22 Bucket A agents have block, 16 Bucket B/C/D agents don't, and each Bucket A block contains 4 load-bearing phrases (Phase Boundary v3.9.2, MUST NOT, MAY READ, Enforcement v3.9.2). Wired to.github/workflows/spec-consistency.yml. - Classification spec — new
docs/design/2026-05-18-ars-v3.9.2-agent-phase-classification.md: canonical 38-agent table with 4-bucket model (A=22, B=4, C=8, D=4) + per-agent out-of-scope inflation risk column. - 8 behavioral smoke test fixtures —
tests/fixtures/issue_133_routing/: cross-phase abstract+lit (the #133 root case), single-phase explicit, no-materials ambiguous, /ars-slash command,[direct-mode]byte-0 honored, mid-message NOT honored, case-insensitive accepted, full draft+abstract+lit+reviews. Honestly framed as LLM-behavior assertions with cross-model spot-check criterion (100% Opus 4.7, ≥75% Sonnet 4.6 + GPT-5.5). - Plugin metadata bump —
.claude-plugin/plugin.jsonversion 3.8.2 → 3.9.2 (was stale; also catches v3.9.0 + v3.9.1 deferrals); description updated for 38-agent ensemble and v3.9.2 phase boundary feature.
Fixed
.claude/CLAUDE.mdSuite version was stale at 3.9.0 — v3.9.1 ship missed bumping it (latent lint bug surfaced during v3.9.2 work). v3.9.2 atomic bump fixes this.
Tests
- 12 new tests in
scripts/test_check_pipeline_integrity.py(verifier). - 3 new tests in
scripts/test_check_v3_9_2_phase_boundary.py(boundary coverage lint). - 4 additional tests after Phase 6 mid-impl review absorption (dotfiles ignored, multiple phase5 dirs independent, Unicode stem matching, nested subdir recursion).
- Regression baseline: 1463 → 1482 passed (+19); 3 skipped + 111 subtests unchanged; 0 failures.
Out of scope (carry to v3.10 conductor, issue #134)
- PreToolUse hook (Phase 0.1 verified Claude Code payload includes
agent_typefield; hook implementation requires multi-phase schema first — both deferred to v3.10). - Multi-phase
ars_phase_writes+ars_phase_readsenvelope schema (scalarars_phasecannot represent agents likedevils_advocate_agentat Phases 1/3/5 orreport_compiler_agentat Phases 4/6 — design correctly with envelope, not retrofit scalar). - Deterministic verifier with author provenance (advisory v3.9.2 filename-heuristic version flagged FP-prone in docstring).
- Orchestrator cross-phase intake capability (
pipeline_orchestrator_agentcurrently keyword-matches user phrasing; cannot reconcile cross-phase artifacts without explicit user signal — this is the conductor's core feature).
Migration notes
Existing in-flight projects: no break expected. v3.9.2 only adds prompt sections and an opt-in advisory verifier. Existing slash commands (/ars-*) continue to work without change.
User-facing behavior change: if you previously dropped pre-existing materials (abstract + literature) into a fresh session without invoking a specific slash command, ARS may now clarify with a-d options instead of silent dispatch. To bypass clarification for direct agent dispatch, prefix your first message with [direct-mode]. To run the full pipeline on pre-existing materials, invoke /ars-full.
If you see a Bucket B multi-phase agent (devils_advocate, report_compiler, argument_builder, visualization) producing out-of-scope content, this is a known v3.9.2 limitation — recurrence is expected for these 4 agents until v3.10 envelope ships. Remediation: switch to orchestrator-driven Mode A via /ars-full or report the case to issue #134 with transcript excerpt.
[3.9.1] - 2026-05-18 — v3.9.0 client hardening (#129 + #130)
Two-bug hotfix surfaced by codex review of ars-codex PR #13 (vendor sync to v3.9.0 74413a4). Both bugs exist in v3.9.0 main: #129 violates the v3.9.0 §3.7 per-API degradation contract; #130 crashes a defensive lint on malformed input. Neither changes the spec or schema.
Fixed
-
#129 — OpenAlex / Crossref response-read failures now translate to
*Unavailable. Inscripts/openalex_client.py:_getandscripts/crossref_client.py:_get,urlopensucceeded butresp.read()/body.decode("utf-8")/json.loads()failures (socket drop mid-stream, truncated body, garbled UTF-8 body, HTML 503 page returned with 200 status) escaped the client as rawOSError/http.client.IncompleteRead/UnicodeDecodeError/JSONDecodeError.scripts/migrate_literature_corpus_to_v3_9_0.pyonly catchesOpenAlexUnavailable/CrossrefUnavailable, so one transient response failure during a 500-entry backfill aborted the whole migration instead of dropping just the affected field. Narrow except block around read+decode+parse now catches(OSError, http.client.HTTPException, UnicodeDecodeError, json.JSONDecodeError)—HTTPExceptioncoversIncompleteRead(canonical mid-stream socket drop, inherits HTTPException not OSError, R1 codex P2 closure). Mirrors the existing 5xx-skip pattern: per-API tolerant per the v3.9.0 spec §3.7 documented degradation contract andbibliography_agent.md"Triangulation Extension". -
#130 —
check_claim_audit_consistencynon-stringmanifest_idguard._build_manifest_index(line 644) and_build_manifest_constraint_index(line 675) usedmanifest_idas a dict key viasetdefault(mid, set())/out[mid] = bucketbefore checking type. For malformed passports where the schema validator already notedmanifest_idasarray/object, the index builder raisedTypeError: unhashable type: 'list'and terminated lint with a traceback beforevalidate_passport()could return the schema finding cleanly. Addedisinstance(mid, str) and midguard at both sites, matching the surrounding_check_inv_17_for_manifest/claim_idinvariant-walker pattern. Schema validator still records the type mismatch — the guard just lets the lint surface findings cleanly instead of crashing.
Tests
scripts/test_openalex_client.py: +4 tests covering OSError onresp.read(), invalid UTF-8 body, invalid JSON body, andhttp.client.IncompleteRead(R1 codex P2 closure).scripts/test_crossref_client.py: +4 symmetric tests.scripts/test_claim_audit_schema.py: newTSManifestIdNonStringGuardclass with 2 tests (manifest_idas list / dict).- Regression baseline: 1453 → 1463 passed (+10), 3 skipped + 111 subtests unchanged, 0 failures.
Out of scope
- Spec / schema / CHANGELOG narrative not touched — the degradation contract is already documented in spec §3.7; this just makes code honor it.
ars-codexadapter sibling: the same two fixes will surface on next vendor sync (v3.9.1 → ars-codex v0.1.8). No action needed in this release.
[3.9.0] - 2026-05-17
Added
- Cross-index triangulation as v3.7.3 contamination_signals Vector 3 (issue #102). Two new optional boolean fields (
openalex_unmatched,crossref_unmatched) insideliterature_corpus_entry.schema.json. Manual-entry not-rule extended symmetrically to forbid all three lookup fields (preprint flag remains exempt — heuristic, not lookup). - OpenAlex API protocol (
deep-research/references/openalex_api_protocol.md) + production client (scripts/openalex_client.py). - Crossref API protocol (
deep-research/references/crossref_api_protocol.md) + production client (scripts/crossref_client.py). bibliography_agent.mdTriangulation Extension subsection — parallel S2/OpenAlex/Crossref lookups, per-API degradation, manual exemption, R-L3-2-D constraint, per-entry ingest log format.- Finalizer 4-tier advisory annotation in
pipeline_orchestrator_agent.md: k=1 →CONTAMINATED-COVERAGE-NOISE(or legacyCONTAMINATED-UNMATCHEDfor k_max=1 S2-only), k=2 →CONTAMINATED-PARTIAL-UNMATCH, k=3 →CONTAMINATED-TRIANGULATION-UNMATCHED. All tiers advisory; gate refusal list unchanged. formatter_agent.mdpass-through allowlist extends from 3 v3.7.3 suffixes to 9 (3 legacy + 6 v3.9.0). Refusal rules 1-10 unchanged.- v3.9.0 lint (
scripts/check_v3_9_0_triangulation.py): set-equality on formatter allowlist, refusal-list-unchanged guard. Exact-token extraction prevents substring collisions (R3 P2 closure). - Migration tool (
scripts/migrate_literature_corpus_to_v3_9_0.py): backfill v3.7.3 → v3.9.0; stable-fields idempotency; per-API degradation tolerant; dry-run mode; daisy-chained migration scope (pre-v3.7.3 entries require v3.7.3 migration first). - 3 new firm rules in spec §3.3: R-L3-2-C (k computed over present fields, absent ≠ false), R-L3-2-D (no OpenAlex
primary_location.source.type/ Crossreftypeused for v3.9.0 classification logic), R-L3-2-E (refusal list unchanged; pass-through allowlist extends).
Design philosophy
- v3.9.0 is the measurement layer for cross-index triangulation. The policy layer (strict modes, hard-block tier, venue-type-scoped strict,
triangulation_policyfield,venue_typefield) is deferred to v3.10 per spec §2.3. - The k=3 marker is
CONTAMINATED-TRIANGULATION-UNMATCHED(describes observable condition), notCONTAMINATED-LIKELY-FABRICATED(would infer cause unsupportable on humanities / non-English / dissertation references where coverage gaps are real). - R-L3-2-A preserved verbatim: contamination signals never block emission on their own.
Migration path
- v3.7.3 corpora: run
python scripts/migrate_literature_corpus_to_v3_9_0.py PATHto backfill the two new fields. - Pre-v3.7.3 corpora: run
python scripts/migrate_literature_corpus_to_v3_7_3.py PATHFIRST, then v3.9.0 migration (daisy-chained per spec §3.7).
Review trail
- R1 (commit
d9280bf): 15 findings (3 P0, 8 P1, 4 P2) — closed. - R2 (commit
7d51215): 12 findings (0 P0, 3 P1, 9 P2) — closed. - R3 (commit
4297c27): 4 P2 findings — closed in Task 1 of impl plan. - Both tracks (codex gpt-5.5 xhigh + Gemini 3.1-pro-preview) READY-FOR-IMPL after R3.
[3.8.2] - 2026-05-17 — #118 uncited audit_tool_failure surface
Fixes the #118 carry-over from #103 R3 codex P2 #5. The ARS_CLAIM_AUDIT=1 uncited constraint-judging path used to silently substitute {"judgment": "NOT_VIOLATED", "rationale": "..."} on JudgeInvocationError, suppressing HIGH-WARN constraint checks on transient judge outage (judge timeout, API 5xx, network error, etc.). v3.8.2 routes those failures through a dedicated uncited_audit_failures[] aggregate at MED-WARN advisory tier, mirroring INV-14 semantics on the cited path but using a separate schema because claim_audit_result.ref_slug is required and the uncited path has no ref to bind.
The #118 issue body listed four candidate options. Option 1 (extend constraint_violation.schema.json) would have broken the judge_verdict: const VIOLATED invariant and re-derived every CV-INV. Option 3 (overload uncited_assertions[] with a fault_class field) would have polluted the D4-c LOW-WARN advisory channel with audit-time infrastructure signal. Option 4 (re-raise JudgeInvocationError and abort the audit pass) would have dropped audit coverage for the entire run on a single transient outage — bad UX for N>50 papers running against flaky judge endpoints. Option 2 (new aggregate) ships here: structural honesty, schema integrity preserved, audit coverage preserved.
Added
shared/contracts/passport/uncited_audit_failure.schema.json— new aggregate per spec §3.6. Required fields:finding_id(UAF-NNN),claim_text,section_path,scoped_manifest_id,fault_class(closed enum mirroring INV-14),rationale(MUST begin with fault_class prefix),judge_model,judge_run_at,rule_version: D4-c-v1-uaf-v1. Optionalmanifest_claim_id(non-null when failure was against an NC-C claim-level constraint, null when against MNCs only).- UAF-INV-1..UAF-INV-6 lint coverage in
scripts/check_claim_audit_consistency.pyrule 4d:- UAF-INV-1: finding_id uniqueness across the aggregate
- UAF-INV-2: scoped_manifest_id cross-array integrity
- UAF-INV-3: (scoped_manifest_id, manifest_claim_id) pair integrity when manifest_claim_id non-null
- UAF-INV-4: per-(sentence, manifest) dedup with key
(scoped_manifest_id, section_path, claim_text_hash) - UAF-INV-5: rationale fault_class prefix matches the row's own
fault_classfield - UAF-INV-6: cross-aggregate exclusivity vs
constraint_violations[](VIOLATED and audit_tool_failure are mutually exclusive verdict states at per-(sentence, manifest) level)
- Finalizer §5 MED-WARN advisory row: annotation
[CLAIM-AUDIT-TOOL-FAILURE-UNCITED — <fault-class>]next to the offending sentence. Always advisory; gate passes — retry on next pipeline pass is the remediation. Formatter REFUSE list unchanged (UAF is advisory, not gate-refuse). UAF_RULE_VERSION = "D4-c-v1-uaf-v1"constant inscripts/_claim_audit_constants.pyfor shared use by pipeline runtime and lint.- 18 new tests keeping the regression baseline 0 (694 → 712 tests):
- 15 schema + lint tests in
scripts/test_claim_audit_schema.py::TSUAFUncitedAuditFailureInvariants - 3 pipeline integration tests in
scripts/test_claim_audit_pipeline.py::TP23UncitedJudgeOutageEmitsUAFproving the swallow is replaced with UAF emit and no synthetic NOT_VIOLATED leaks into any aggregate
- 15 schema + lint tests in
Changed
scripts/claim_audit_pipeline.py: swallow site at line 1211-1224 (the syntheticNOT_VIOLATEDsubstitution) replaced with_uncited_audit_failure_entry(...)emission +continue. Pipeline return now includesuncited_audit_failuresalongside the other five aggregates.docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md: amended with new §3.6 (schema + UAF-INV-1..6 + co-emission rules), §4 step 5 stream (d) routing clause, §4 step 9 fourth error-handling bullet, §5 finalizer outputs list + advisory paragraph, §6 lint rule 4d + precedence rule 6 cross-aggregate exclusivity reference.academic-pipeline/agents/claim_ref_alignment_audit_agent.md: Output emission table grows seventh row foruncited_audit_failures[]. Error handling table grows from 3 failure surfaces to 4 (the new uncited-path UAF row mirrors the cited-pathaudit_tool_failurerow).
Fixed
- #118: uncited judge failure no longer swallowed as NOT_VIOLATED; the HIGH-WARN constraint check path is now observable on transient outage. Pre-v3.8.2 a flaky judge endpoint could silently pass a draft with a real MUST-NOT violation; v3.8.2 surfaces the operational failure at MED-WARN advisory tier so a retry pass picks it up.
Review trail
Single-PR ship after spec → TDD → impl. UAF schema design followed the design-phase brainstorming rule: option 1-4 trade-off analysis happened in conversation with the user before any code, captured in a local gitignored decision memo. Implementation followed strict TDD RED → GREEN — 15 schema/lint tests + 3 pipeline tests all failed in their intended way (no schema file, no lint logic, swallow site still active) before the schema, lint, helper, and pipeline change landed. No regression on the 694 pre-existing tests.
[3.8.1] - 2026-05-17 — claim_audit lint hardening (#119 + #120 4×P2 closure)
Defense-in-depth patch on ARS_CLAIM_AUDIT=1 opt-in lint paths. Five fixes carried over from #103 R6 + R8 independent review, consolidated into one v3.8.1 release. No schema semantic change, no behavior change for well-formed payloads — pre-fix surfaces all crashed the CLI with TypeError / AttributeError instead of returning actionable lint findings or routing through the INV-14 audit_tool_failure translation boundary.
Fixed
- #119 / #120 P2-2 — nested schema-invalid shapes no longer crash invariant walkers. Added
_iter_dictshelper and narrowisinstance(str)guards in_check_inv_17_for_manifest,_check_manifest_invariants,_build_manifest_index,_build_manifest_constraint_indexso that nestedclaim_intent_manifests[].claimsas string,claims[].claim_idnon-string, oraudit_sampling_summaries[].audited_indicesmixed types now surface as clean schema findings instead of crashing onfor claim in "broken":, regex against non-string, orint <= strcomparison. The schema validator still records the type mismatch separately — narrow walker guards prevent the second-stage crash without masking schema-vs-invariant double coverage (option 2 refined, not aggregate-level skip). - #120 P2-1 — CV-INV-4 dedupe scoped by
scoped_manifest_id. Dedupe key extended from(section_path, claim_text_hash, violated_constraint_id)to(scoped_manifest_id, section_path, claim_text_hash, violated_constraint_id). Per M-INV-4,manifest_idis unique across the passport but constraint ids (MNC-*/NC-*) are only unique WITHIN a manifest — two manifests in the same passport may legitimately carry colliding constraint ids, and the same sentence may then violate both. Pre-fix, the dedupe false-positived these as duplicates. Spec wording in §3.5 + §7.1 4b updated. - #120 P2-3 — judge
judgmentisinstance(str)guard before set membership._validate_judge_dictnow rejects a non-string judgment (e.g. malformed{"judgment": [1, 2], "rationale": "..."}) asjudge_parse_error → audit_tool_failurevia the INV-14 translation boundary instead of bubblingTypeError("unhashable type: 'list'")out of the set-membership test. - #120 P2-4 — retrieve
ref_retrieval_methodisinstance(str)guard before set membership. Symmetric to P2-3 on the retrieval boundary._invoke_retrieverejects a non-string method asretrieval_api_error → audit_tool_failureinstead of crashing on set membership.
Tests
scripts/test_claim_audit_schema.py: 3 new tests inTS9MalformedPassportGuard(nested string / non-string claim_id / mixed-type indices) + new test classTSCVDedupeManifestScopewith 2 tests (cross-manifest collision must keep both; within-manifest true duplicate still caught).scripts/test_claim_audit_pipeline.py: 2 new tests inTP12JudgeFailureAuditToolFailure(non-string list + dict judgment) + 1 new test inTP14RetrieveFailureAuditToolFailure(non-string list method).- Regression baseline: 682 → 690 tests (+8), 0 failures, 0 errors across full
scripts/test_*.pydiscovery.
Design memo
A local, gitignored design memo carries the option-1 vs option-2 analysis, CV-INV-4 dedupe key shape rationale, and the release-framing decision.
Closes #119. Refs #120 P2-1, P2-2, P2-3, P2-4 (all four R8 findings).
[3.8.0] - 2026-05-16 — L3 Claim-Faithfulness Locator + Audit (v3.7.3 + #103 paired milestone)
v3.7.3 + v3.8 close the L3 (claim-faithfulness) gap end-to-end. v3.7.3 ships the locator infrastructure (every citation carries a three-layer anchor so the audit can fetch the cited passage); v3.8 ships the audit pass that consumes those anchors, judges whether the cited source supports the claim, and gate-refuses HIGH-WARN violations at the formatter terminal hard gate. The release also bundles 5 audit-trail-shipped feature PRs accumulated on main since v3.7.0 (#104 / #105 / #108 / #111 / #115). External motivation: Zhao et al. arXiv:2605.07723 (2026-05) — 146,932 hallucinated citations across arXiv / bioRxiv / SSRN / PMC in 2025.
#103 — v3.8 claim ↔ reference faithfulness audit agent (2026-05-16)
Parent issue: #103 — closes the L3 (claim-faithfulness) gap left open by v3.7.3 (which closed the locator-channel half). Spec: docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md + decision doc docs/design/2026-05-15-issue-103-claim-alignment-audit-decision.md (D1-D6 settled).
Why: Zhao et al. arXiv:2605.07723 (2026-05) shows 146,932 hallucinated citations across arXiv / bioRxiv / SSRN / PMC in 2025; v3.7.3 stopped the "no locator" path but a present-but-wrong claim ↔ source mismatch was still undetected. v3.8 adds a Stage 4→5 audit pass that judges every sampled citation against its retrieved excerpt, emits 5 new passport aggregates, and drives 5 new HIGH-WARN annotation classes through the formatter terminal hard gate.
New components:
claim_ref_alignment_audit_agent(1 new agent,academic-pipeline/agents/) — opt-in (ARS_CLAIM_AUDIT=1, default OFF for v3.8.0) audit agent dispatched between v3.7.1 cite finalizer and formatter hard gate. Takes citations + manifests + corpus + Stage 4 draft sentence stream (full uncited + D4-c filtered subset).- 5 new passport schemas (
shared/contracts/passport/):claim_audit_result,claim_intent_manifest,claim_drift,uncited_assertion,constraint_violation. Cross-field invariants INV-1..INV-18 / M-INV-1..M-INV-4 / U-INV-1..U-INV-4 / D-INV-1..D-INV-4 / CV-INV-1..CV-INV-4 lint-enforced (JSON Schema can't express the conditional matrix relating judgment / audit_status / defect_stage / ref_retrieval_method). - Runtime pipeline (
scripts/claim_audit_pipeline.py) — implements §4 step 1-6 + manifest set-diff (D6 set-of-text semantics). Per-citation judge wrapping (_invoke_judge+_invoke_retrievetranslate transient failures to INV-14audit_tool_failurerows: judge_timeout / judge_api_error / judge_parse_error / cache_corruption / retrieval_api_error / retrieval_timeout / retrieval_network_error). Cache hits re-validated through the same surface. Per-manifest uncited judge calls to prevent MNC id collisions across manifests. - 8-row finalizer matrix (
scripts/claim_audit_finalizer.py) — discriminates paywall (LOW-WARN advisory) / fabricated reference (HIGH-WARN gate-refuse) / anchorless (HIGH-WARN defense-in-depth) / audit_tool_failure (MED-WARN advisory) viaref_retrieval_methodalongside(judgment, defect_stage). - 5 new HIGH-WARN annotation classes in
formatter_agentREFUSE list:[HIGH-WARN-CLAIM-NOT-SUPPORTED]/[HIGH-WARN-NEGATIVE-CONSTRAINT-VIOLATION]/[HIGH-WARN-FABRICATED-REFERENCE]/[HIGH-WARN-CLAIM-AUDIT-ANCHORLESS]/[HIGH-WARN-CONSTRAINT-VIOLATION-UNCITED]. Mirrors v3.7.3 R-L3-1-A asymmetry —/ars-mark-readdoes NOT clear; remediation is fixing the prose. - "Claim Intent Manifest Emission" sibling section added to
synthesis_agent/draft_writer_agent/report_compiler_agentper v3.6.7 PATTERN PROTECTION pattern. The §3a SHA-pinned blocks stay byte-equivalent to commite7e775a0e1b4. - Calibration runner (
scripts/claim_audit_calibration.py+scripts/test_claim_audit_calibration.py+scripts/fixtures/claim_audit_calibration/gold_set.json) — 20-tuple gold set (12 alignment + 8 constraint); T-C1 threshold gate (FNR < 0.15 + FPR < 0.10), T-C2 per-class FNR/FPR, T-C3 gold-set shape integrity. Re-run:PYTHONPATH=. python3 -m unittest scripts.test_claim_audit_calibration -v. - 2 new lints + 1 new pytest module + 7 new unittest modules wired into CI (
.github/workflows/spec-consistency.yml):check_claim_audit_consistency.py(38 invariant checks + schema validation),check_v3_8_annotation_literal_sync.py(formatter-finalizer literal drift gate). Test suite: 194 unittest tests across the 7 modules.
Review trail (Step 13 dual-track, 2026-05-16): 8 rounds codex (gpt-5.5 xhigh) + 1 round Gemini 3.1-pro-preview before Gemini quota exhausted. Trajectory R1 4P1+2P2 → R2 0P1+3P2 → R3 0P1+5P2 → R4 2P1+2P2 → R5 0P1+2P2+1P3 → R6 1P1+1P2 → R7 1P1+1P2+1P3 → R8 0P1+4P2 → ship. Per feedback_codex_review_surface_loop_design_phase.md design-phase P2 noise floor doesn't auto-converge; the user declared ship signal at R8 with all P0/P1 closed and 4 R8 P2 carried over to v3.8.1 (#120).
Carry-over follow-up issues:
- #118 — uncited path NOT_VIOLATED swallow on judge failure (schema-level decision)
- #119 — nested schema-invalid shapes still crash invariant helpers
- #120 — 4 R8 P2 findings (CV-INV-4 dedupe scope / invariant walker short-circuit / judgment + method type-check before set membership)
Regression baseline (post-ship):
- pytest: 1356 passed, 3 skipped, 103 subtests (was 1107 pre-#103, +249 tests across schema / pipeline / detector / manifest / finalizer / e2e / calibration / lint coverage)
- v3.x lints: 7/7 PASS (v3.6.7 / v3.6.8 ×4 / v3.7.3 / v3.8)
- personal-boundary: 0 violations (614 files scanned)
- SHA-pinned zero-touch:
shared/sprint_contract.schema.json0 lines diff,shared/contracts/passport/audit_artifact_entry.schema.json0 lines diff against main
#115 — Semantic Scholar client maturity: throttle + outage latch (2026-05-15)
Parent issue: #115 — follow-up to #105 PR codex round-5 [P2]×2 findings (R5-2 throttle + R5-3 outage latch). Both deferred during #105 ship per architectural-inflection discipline; this entry closes the SS-client maturity gap.
Modified files:
scripts/semantic_scholar_client.py— two additions:- Throttle (#115 R5-2): new ctor params
clock+min_interval_seconds. Defaults: 1.0s unauthenticated (1 req/s per protocol), auto-drops to 0.1s whenS2_API_KEYdetected (authenticated 10 req/s tier). Pre-request pacing tracks_last_request_at; sleepsmax(0, min_interval - elapsed)before each call. First request passes through. - Outage latch (#115 R5-3):
_latched_unavailableflag set onURLError. Subsequentlookup()calls short-circuit withSemanticScholarUnavailablewithout invoking urlopen. Newreset_outage_latch()method lets long-running tools retry between passport batches. HTTP 5xx does NOT latch (server-side error ≠ transport outage).
- Throttle (#115 R5-2): new ctor params
scripts/test_semantic_scholar_client.py— 9 new tests (5 throttle: first-no-sleep / back-to-back / past-interval / authenticated-tier / override; 3 latch: URLError short-circuits / reset restores / 5xx does not latch; 1 efficiency: 429-retry refreshes throttle anchor).scripts/contamination_signals.py— newreset_client_outage_latch(client)helper. Production clients implementing the outage-latch pattern exposereset_outage_latch(); mocks may not. Helper invokes when present, no-ops when absent — avoids AttributeError when callers swap clients. 2 new tests.scripts/migrate_literature_corpus_to_v3_7_3.py—migrate_directoryresets the SS client's outage latch between passports so a transient network blip on one passport doesn't permanently disable lookups for the rest of the directory. Within a single passport the latch still short-circuits to protect a dead service from N retry waves.
Production behavior change:
_build_default_ss_client()API unchanged (SemanticScholarClient()no-arg). New throttle is automatic per protocol — no migration tool changes required.- For a 5000-entry unauthenticated migration: same ~1.5hr runtime (already constrained by 1 req/s); now achieves it via deterministic pacing rather than 429-retry exhaustion.
- For an authenticated migration (
S2_API_KEYset): drops to 0.1s/call = ~8min for 5000 entries. - Network outage during large corpus: previously retried every entry independently (up to 30s timeout per entry on the slow path); now the first URLError latches the client and subsequent entries short-circuit until the next batch boundary calls
reset_outage_latch(). Themigrate_directoryhelper does this reset automatically between passports.
Out of scope: migration tool (migrate_literature_corpus_to_v3_7_3.py) — #105 partial-fill / provenance contract correct as shipped. Protocol doc — already correct; this issue is implementation alignment.
Regression: 472 unittest (+8 #115 tests) + 201 pytest adapters + spec_consistency + preprint_venues all green.
#105 — v3.7.3 contamination_signals backfill migration tool (2026-05-15)
Parent issue: #105. Spec anchor: v3.7.3 §3.2 R-L3-2-B (the deferred batch operation; bibliography_agent computes signals at ingest, this tool delivers post-hoc backfill on legacy corpora). Design: docs/design/2026-05-15-issue-105-contamination-signals-backfill-design.md.
New files:
scripts/contamination_signals.py— two pure-function resolvers + emission rules +SemanticScholarClientprotocol.compute_preprint_signal()(Signal 1, deterministic year+venue check against 10-server closed list).compute_ss_unmatched_signal()(Signal 2, dependency-injected SS client, returnsNoneon manual exemption + API degradation per spec).scripts/migrate_literature_corpus_to_v3_7_3.py— CLI tool:[--dry-run] [--verbose] <passport_or_dir>. Usesruamel.yamlround-trip to preserve comments + key order + quoting style. Reportsprocessed / patched / skipped_already_migrated / skipped_insufficient_datacounts. Idempotent.scripts/test_contamination_signals.py— 25 unit tests covering Signal 1 (15 cases: 10 preprint venues × year boundary, non-preprint venue, missing year, missing venue), Signal 2 (6 cases: manual exemption / match / no-match / API degradation × 2 paths / unexpected exception), emission rules (4 cases).scripts/test_migrate_literature_corpus_to_v3_7_3.py— 9 unittest cases covering dry-run, full migration per emission rules, idempotency, insufficient-data skip, empty-corpus passport, directory scan (non-recursive), comment preservation.docs/migration/v3.7.3-contamination-signals-backfill.md— user-facing migration guide (when to run, dry-run workflow, idempotency, SS API rate-limit considerations, what's out of scope).
Modified files:
shared/contracts/passport/literature_corpus_entry.schema.json— purely additive: new optionalcontamination_signals_backfilled_atfield (ISO-8601 date-time string). Existing v3.7.3 ingest-time entries (which lack this field) remain valid; pre-v3.7.3 entries (which lack both this field andcontamination_signals) remain valid.scripts/adapters/tests/test_literature_corpus_entry_schema.py— 3 new tests for the additive field (valid present / absent / non-string rejected).requirements-dev.txt— addruamel.yaml>=0.17.
Open-question resolutions (user-chosen 2026-05-15):
- Q1 API rate-limit handling: backoff-only via existing SS protocol (429 → 2s × 3); no resumable checkpoint (YAGNI per minimal scope)
- Q2 schema field naming: scalar
contamination_signals_backfilled_atISO-8601 timestamp; strictly additive upgrade path if v3.7.4 needs structured provenance - Q3 multi-passport batch mode: directory-scan only; no
--input-list(YAGNI) - Q4 YAML library:
ruamel.yamlround-trip to preserve user-owned passport formatting (memoryfeedback_toml_duplicate_table_corruptionspirit)
Spec discipline (per v3.7.3 R-L3-2-B):
- Migration is offline + opt-in: user explicitly invokes; pipeline doesn't auto-trigger
- Idempotency keyed on
contamination_signalspresence: first-migration timestamp preserved across re-runs obtained_via=manualexemption preserved at migration time (semantic_scholar_unmatched field omitted, matches the v3.7.3 schema cross-field rule)- API degradation → field omitted (NOT set to False, per "absence ≠ negative confirmation" rule)
Files explicitly NOT touched:
deep-research/agents/bibliography_agent.md— v3.7.3 ingest-time computation frozenacademic-pipeline/agents/pipeline_orchestrator_agent.md— finalizer behavior unchanged- Existing
scripts/adapters/*— adapters produce ingest-time entries; migration is downstream
Regression status: 1053 #108 baseline + 17 #111 baseline + 25 resolver + 9 migration + 3 schema = 1107 total. All green. No regression on the existing 4 allOf cross-field invariants (manual exemption + preprint year=2024 boundary verified by adapter pytest).
#104 — README motivation: add Zhao et al. corpus-scale evidence anchor (2026-05-15)
Parent issue: #104. Doc-only — no code changes.
Adds a third evidence anchor to the ### Why human-in-the-loop, not full automation? README section, between the ARS positioning paragraph and the PaperOrchestra paragraph. Closes the gap where v3.7.x trust-and-locator machinery appeared in the codebase without its corpus-scale motivation surfaced in the public-facing README.
Modified files:
README.md— new Zhao et al. paragraphREADME.zh-TW.md— translated equivalent
Three motivation anchors now read in sequence:
- Lu et al. (Nature 651:914-919) — case-study evidence of autonomous-pipeline failure modes
- Zhao et al. (arXiv:2605.07723) — corpus-scale evidence of the citation-faithfulness problem (111M references / 2.5M papers / 146,932 conservative 2025 estimate / mid-2024 inflection / 85.3% bioRxiv-to-PMC persistence)
- PaperOrchestra (Song et al., arXiv:2604.05018) — method-level technique source
Discipline (#104 acceptance criteria):
- Statistics verified directly against Zhao et al. abstract (111M / 2.5M / 146,932 / conservative qualifier) + v3.7.3 spec which carries the body-level numbers (85.3% bioRxiv→PMC specificity, mid-2024 inflection) through prior 10-round codex + gemini cross-model review.
- No claims that v3.7.x "closes" L3 — only "adds locator infrastructure" / "advisory risk signals".
- L3 attributed to ARS terminology, not the paper's.
- "Motivated by" not "responds to".
#111 — slr_lineage emission on systematic-review → academic-paper full handoff (2026-05-15, unreleased)
Parent issue: #111, follow-up to #108 (PR #110, merged 70c8678) round-8 P2 #1. Design: docs/design/2026-05-15-issue-111-slr-lineage-emission-design.md.
Version label
v3.7.4below is provisional and will be confirmed at the next release sweep perfeedback_version_bump_sweep_checklist.md. If this work ships as part of v3.7.3 (the in-progress release at writing time), the version stamps in this entry and the prose files below are swept to the final label at release tag.
Closes the pipeline-plumbing gap surfaced by #108: disclosure --policy-anchor=prisma-trAIce now dispatches automatically when the documented deep-research systematic-review → academic-paper full → disclosure path runs, without the user manually supplying mode=systematic-review at cold-start.
New files added:
scripts/slr_lineage.py— two pure functions: (a)resolve_from_stages(stages)returnsTrueiff any stage was produced bydeep-researchin systematic-review mode (bound to the deep-research producer specifically — a non-deep-research stage carrying mode='systematic-review' does NOT trigger SLR lineage); (b)emit(stages, incoming_slr_lineage)is the monotonic-OR wrapper the orchestrator calls at every handoff. The OR preserves any signal already persisted on the incoming passport (load-bearing forresume_from_passport=<hash>sessions whosestate_tracker.stagesis empty — codex round-1 [P2] closure).scripts/test_slr_lineage_emission.py— 17 conformance tests: resolver semantics (7 cases: positive / non-SLR / mid-entry / empty / aliasslr/ non-deep-research / missing-mode), renderer integration (3 cases: pipeline-emitted dispatches withoutmode_param/ non-SLR still blocks / pre-#111 cold-start fallback preserved), end-to-end pipeline handoff (2 cases), and monotonic-OR emit semantics (5 cases: resume preserves true / in-session false-to-true / no-evidence false / None incoming / default arg ergonomics).
Modified files:
shared/handoff_schemas.md— Schema 9 Material Passport gains optional top-levelslr_lineage: booleanrow + dedicated "Run-level lineage signal (v3.7.4)" subsection documenting semantics, producer, consumer, backward compat, and G1 boundary note (passport-level vs corpus-entry-level distinction).academic-pipeline/agents/pipeline_orchestrator_agent.md— §4 Transition Management gains a "Run-level lineage emission (v3.7.4+)" step computed at every handoff transition before dispatch. Passport carry-line updated to referenceslr_lineagefrom v3.7.4+.
Files explicitly NOT touched (matches #111 §Scope out-of-scope):
scripts/policy_anchor_disclosure_referee.py— #108 referee, contract unchangedacademic-paper/references/policy_anchor_disclosure_protocol.md— #108 protocol, unchangedacademic-paper/references/policy_anchor_table.md— #108 anchor table, unchangedacademic-paper/references/disclosure_mode_protocol.md— already referencesslr_lineageas pipeline-suppliedshared/contracts/passport/literature_corpus_entry.schema.json— G1 invariant frozen (corpus entry schema, not passport schema)
G1 boundary clarification: Decision Doc §4.4 #11 G1 invariant scope is literature_corpus_entry.schema.json (corpus entry data schema). Schema 9 Material Passport top-level extensions follow the v3.6.3 (reset_boundary[]) / v3.6.4 (literature_corpus[]) / v3.6.7 (audit_artifact[]) precedent and are permitted per Decision Doc §4.4 #11's "non-renderer code changes for §4.4 concerns are permitted" provision.
Backward compat: passports written by pre-v3.7.4 runs lack the slr_lineage field; renderer treats absence as false (cold-start path requiring explicit mode_param='systematic-review'). Identical to pre-v3.7.4 behavior.
Regression status: 1053-baseline frozen (no #108 contract drift); +17 new tests cover this issue's acceptance criteria #1-#3 plus codex round-1 [P2] (monotonic-OR emit across resume).
#108 — AI disclosure policy-anchor renderer (2026-05-14, audit-trail-shipped)
Parent docs: Decision Doc (docs/design/2026-05-14-ai-disclosure-schema-decision.md, PR #109, merged commit 20ed72d) + implementation spec (docs/design/2026-05-14-ai-disclosure-impl-spec.md).
Migration note (G1 + G6 invariants): no migration required. Decision Doc §2.1 G1 invariant: no ai_disclosure field is added to shared/contracts/passport/literature_corpus_entry.schema.json. Decision Doc §3 G6: no deprecation horizon — legacy entries (which by §1 fact-check do not carry any AI-disclosure field today) stay byte-equivalent. The implementation extends the runtime renderer path, not the data schema.
New files added:
academic-paper/references/policy_anchor_table.md— 4-anchor (PRISMA-trAIce / ICMJE / Nature / IEEE) × 16-field source-of-truth reference table carrying verbatim policy quotes lifted from discovery doc §4.3-4.6 (PR #107, commit299c4b6) + per-anchor renderer rules.academic-paper/references/policy_anchor_disclosure_protocol.md— LLM-prose runtime protocol for the new--policy-anchor=<a>track: 7-section flow covering inputs / G10 7-row precedence table / per-anchor render flows / auto-promotion forbiddance / venue-anchor conflict resolution / three-state completeness flag / 11-concern resolution map.shared/policy_data/nature_policy.md— canonical Nature substantive policy source; both the policy-anchor track and the v3.2 venue track cross-reference this path for the G4 dedup invariant.scripts/check_policy_anchor_table.py+scripts/test_check_policy_anchor_table.py— anchor table structural lint with 13 mutation tests + Nature dedup guard wired into the main lint command.scripts/check_policy_anchor_protocol.py+scripts/test_check_policy_anchor_protocol.py— protocol doc lint with 12 mutation tests covering §4.3 8 invariants + §4.4 11 concerns + G10 7-row precedence table + auto-promotion forbiddance + anchor inventory closed-enum.scripts/policy_anchor_disclosure_referee.py+scripts/test_policy_anchor_disclosure.py— executable specification (referee) of §3 G10 7-row decision table + 8 invariant predicates; 61 conformance tests covering every (input × expected output) combination + forbidden-path negative fixtures.
Modified files:
academic-paper/references/disclosure_mode_protocol.md—--policy-anchor=<a>track added in parallel to v3.2--venue=<v>track. Phase 1 dispatch becomes selector-aware (step 1a / step 1b venue / step 1c anchor). Venue-only flow unchanged; anchor flow delegates Phase 3+4 topolicy_anchor_disclosure_protocol.md. Concern #7 venue+anchor conflict resolution enforced.academic-paper/references/venue_disclosure_policies.md— Nature entry gains derivation note + dedup pointer toshared/policy_data/nature_policy.md. v3.2 venue rendering content unchanged (derived view, manual sync to canonical source until future refactor)..github/workflows/spec-consistency.yml— 5 new CI steps wiring the new validators and conformance test suite into the existing spec-consistency job.
§4.4 11 open concerns resolved (4 user-chosen, 7 inline; full table in impl spec §3):
- Track-selection lookup: explicit
slr_lineageinput from pipeline orchestrator (user-chosen). - Tool identity collection: auto-detect from session metadata (mirror v3.2 Phase 4).
- Prompt scope: per-(tool × task) tuple per PRISMA M6.a.
- IEEE section locator: free-form list with recommended IMRaD exemplars.
- Nature image metadata: hybrid output channel (annotation block + suggested inline patches) (user-chosen).
- UNCERTAIN per-facet finalization: USED-full + per-facet annotation alongside still-UNCERTAIN (user-chosen).
- Venue+anchor conflict: reject conflicting selectors with explicit error.
- Three-state completeness flag: full computation logic encoded in §6 of protocol doc.
- Test set scope: 86 new tests covering 8 invariants + 10 concerns × {positive, negative}.
ai_used:truesubstantive-content gate: force v3.2 categorization flow (user-chosen).- G1 invariant scope: data layer untouched; non-renderer pipeline plumbing permitted.
Known follow-up (out of #108 scope): the academic-pipeline orchestrator does not yet emit slr_lineage on the documented systematic-review → academic-paper full handoff. Authors targeting --policy-anchor=prisma-trAIce must supply mode=systematic-review manually until that plumbing lands in a separate PR (touches academic-pipeline/ + shared/handoff_schemas.md, outside §4.1 items 1-5 NO-CHANGE boundary).
Regression status: 967 baseline + 86 new tests = 1053 passing / 3 skipped / 0 failed. Public-repo boundary clean. Eight rounds of codex gpt-5.5 xhigh review (R1 4 P2 → R8 2 P2); shipped audit-trail-complete per user decision rather than pushing past Decision Doc 11-round high water mark. R8 P2 #1 captured as the known follow-up above.
v3.7.3 — claim faithfulness locator + contaminated-source advisory (2026-05-12, in progress)
External motivation: Zhao, Wang, Stuart, De Vaan, Ginsparg, Yin "LLM hallucinations in the wild: Large-scale evidence from non-existent citations" (arXiv:2605.07723, 2026-05). Corpus-scale audit of 111M references across 2.5M papers across arXiv / bioRxiv / SSRN / PMC finds 146,932 hallucinated citations estimated for 2025 alone, with the inflection point at mid-2024, 85.3% of preprint hallucinations surviving into the published record, and Google Scholar increasingly indexing citation-only entries. The paper names the L3 (claim faithfulness) gap explicitly: "real citations deployed to support claims the cited references do not actually make ... remains an open challenge for which reliable detection methods remain under active development." v3.7.3 closes the locator-channel half of that gap (anchor infrastructure for future L3 audit) and surfaces two contamination signals (preprint post-LLM-inflection + Semantic Scholar unmatched) as advisory cite-time markers.
L3-1 — Three-Layer Citation Emission (claim faithfulness locator):
deep-research/agents/synthesis_agent.md,academic-paper/agents/draft_writer_agent.md,deep-research/agents/report_compiler_agent.mdgain## Three-Layer Citation Emission (v3.7.3)H2 section that extends v3.7.1 Two-Layer with a third hidden marker:<!--anchor:<kind>:<value>-->where<kind>∈{quote, page, section, paragraph, none}. Production-mandatory locator rule (R-L3-1-A) requires<kind>≠nonefor every visible citation; emittingnonetriggers finalizer MED-WARN-NO-LOCATOR (gate-refused). Quote anchors capped at 25 words by whitespace split (R-L3-1-B). Anchor values come from corpus context only — no frontmatter reads (R-L3-1-C, inherits v3.6.7 partial-inversion discipline).academic-pipeline/agents/pipeline_orchestrator_agent.mdgains a## Cite-Time Provenance Finalizer — v3.7.3 extensionH2 section: 4-cell matrix becomes 5-cell along a new precedence-zero locator-presence axis. NO-LOCATOR resolution:[UNVERIFIED CITATION — NO QUOTE OR PAGE LOCATOR]<!--ref:slug--><!--anchor:none:-->.academic-paper/agents/formatter_agent.mdgains a## Cite-Time Provenance Hard Gate (v3.7.1 + v3.7.3)section formalizing the terminal hard-gate refusal across all three v3.7.x severity tiers (HIGH-WARN-NO-ORIGINAL, MED-WARN-NOT-CROSS-CHECKED, MED-WARN-NO-LOCATOR).
L3-2 — Contaminated-source advisory signals:
shared/contracts/passport/literature_corpus_entry.schema.jsonadds optionalcontamination_signals: { preprint_post_llm_inflection, semantic_scholar_unmatched }object. Both sub-fields optional within the object; both default to absent (signals not computed).additionalProperties: falseenforced on the sub-object. Backward compat: entries without the field stay valid.deep-research/agents/bibliography_agent.mdgains## Contamination Signal Computation (v3.7.3)section. Signal 1 (preprint_post_llm_inflection):year >= 2024 AND venue ∈ {arXiv, bioRxiv, medRxiv, SSRN, Research Square, Preprints.org}. Signal 2 (semantic_scholar_unmatched): existing Semantic Scholar API protocol returns no match by DOI or title; exempted whenobtained_via: manual; omitted (notfalse) on API degradation.- Pipeline finalizer (in pipeline_orchestrator) annotates
ok/LOW-WARNmarkers withCONTAMINATED-PREPRINT/CONTAMINATED-UNMATCHED/CONTAMINATED-PREPRINT+UNMATCHEDsuffix percontamination_signalsstate. Annotations are advisory only — they do NOT change the gate decision (v3.5 Collaboration Depth Observer precedent).
Lint + tests:
- New
scripts/check_v3_7_3_three_layer_citation.pystatic lint: every<!--ref:slug-->must be followed by<!--anchor:<kind>:<value>-->;quotevalues ≤25 words; orphan anchors rejected. - New
scripts/test_check_v3_7_3_three_layer_citation.py: 14 tests covering positive (5 kinds × passing cases, contamination-suffix marker, LOW-WARN-resolved marker, multi-citation) + negative (bare ref, orphan anchor, invalid kind, 26-word quote). - New 6 contamination_signals tests in
scripts/adapters/tests/test_literature_corpus_entry_schema.py: absence / empty / both-false / both-true / unknown-subfield-rejected / non-boolean-rejected. - New
V373ExtensionLineBudgetTestinscripts/test_v3_6_7_phase_6_6.py: 60-line budget for## Cite-Time Provenance Finalizer — v3.7.3 extensionblock; existing Phase 6.6 +60 v3.6.7 budget test updated to subtract both v3.7.1 Step 3b AND v3.7.3 extension lines.
Regression status (final, post round-10 convergence): 967 tests pass, 3 skipped, 0 failed (42 new tests across rounds 1-10 fixes; pre-review baseline was 925). v3.6.7 + v3.6.8 + v3.7.1 + v3.7.2 lints all PASS unmodified. v3.6.7 PATTERN PROTECTION blocks remain byte-equivalent (SHA gate v2 unchanged). Material Passport literature_corpus_entry schema backward compatible (new contamination_signals field optional; cross-field rules only fire when explicitly set). New v3.7.3 lint wired into spec-consistency.yml CI workflow per F18.
Cross-model review closure (2026-05-12, 11 rounds total — 10 codex + 1 gemini cross-model):
| Round | Reviewer | Findings | Closures |
|---|---|---|---|
| 1 (initial) | Codex | 0 P1 / 2 P2 | F3 (untracked artifacts → closed at commit), F4 (NO-LOCATOR acknowledgment contradiction → removed /ars-mark-read promise from formatter+finalizer+spec Q5) |
| 1 (initial) | Gemini 3.1-pro-preview | 2 P1 / 2 P2 / 1 P3 | F1 (hyphen-encode → 3 prompts + lint + 3 tests), F2 (whitespace/newline tolerance → finalizer clarification + 4 tests), F5 (year<2024 schema cross-field → allOf + 4 tests), F6 (venue list 6 → 10 added ChemRxiv / EarthArXiv / OSF Preprints / TechRxiv), F7 (fenced code block isolation → helper + 4 tests) |
| 2 | Codex | 0 P1 / 2 P2 | F8 (lint regex widened to {0,2} suffix tokens → 3 tests), F9 (empty non-none anchor value rejection → 5 tests) |
| 3 | Codex | 0 P1 / 2 P2 | F10 (premature HTML comment terminator sentinel scan → 3 tests), F11 (schema manual-entry exemption → 4 tests) |
| 4 | Codex | 0 P1 / 1 P2 / 1 P3 | F12 (orphan_pattern lookbehind removed → 3 tests), F13 (schema venue list description sync 6 → 10) |
| 5 | Codex | 0 P1 / 1 P2 | F14 (malformed ref broad-scan detector → 4 tests) |
| 6 | Codex | 0 P1 / 1 P2 | F15 (prompt-vs-lint alignment on -- rule → 2 tests; prompts loosened to match lint's narrower contract) |
| 7 | Codex | 0 P1 / 3 P2 | F16 (finalizer status-suffix-tolerant for revision-loop reruns), F17 (standalone deep-research self-gate), F18 (CI workflow wires v3.7.3 lint into spec-consistency.yml) |
| 8 | Codex | 0 P1 / 3 P2 | F19 (decode value before empty check → 3 tests), F20 (formatter raw anchor:none gate), F21 (F17 self-gate scoped to standalone mode only via prompt mode-detection) |
| 9 | Codex | 0 P1 / 1 P2 | F22 (self-gate also rejects bare refs without anchor — parity with pipeline finalizer's precedence-zero "no anchor = anchor=none" rule) |
| 10 (final) | Codex | 0 findings | Convergence achieved. |
- No cross-finding overlap across reviewers. Codex and Gemini found complementary defect classes — Codex caught contract gaps + regex completeness + architectural integration; Gemini caught HTML comment parsing edge cases + cross-field schema rules + venue completeness. This is the canonical value split documented in
feedback_codex_workflow_consolidated.md. - Cascade pattern: each round's closure introduced no new defects in its OWN scope, but interactions with other v3.7.3 surfaces surfaced new layers — F19 was an F9 layer (encoded-whitespace bypass after the F9 raw-value fix), F21 was a F17 regression (self-gate ran in pipeline mode and interfered with finalizer), F22 was an F17+F21 boundary (only catching explicit
nonemarkers missed bare-ref legacy form). The 10-round convergence trajectory is consistent with the v3.6.8 18-round implementation precedent andfeedback_complex_spec_review_inventory_pattern.md. - F23+ not yet observed. Round 10 returned no findings on the 9th amended branch state, providing the convergence signal. Future codex challenge mode (adversarial scope) may surface architecturally deeper gaps; tracked separately as a v3.7.4+ concern.
Out of v3.7.3 scope (tracked as follow-up issues):
- v3.7.4 retrieval-side hardening: OpenAlex + Crossref triangulation as second contamination signal (Vector 2 currently single-source via Semantic Scholar only).
- v3.8 L3 full audit:
claim_ref_alignment_audit_agentrunning LLM-as-judge over (claim, ref full-text) pairs. v3.7.3 anchors are the input; v3.8 verifies anchor content faithfulness. - AI disclosure schema split (per-stage: drafting / editing / reference suggestion / data analysis) — Zhao et al. Fig. 1l correlates AI-writing-signature with hallucination rate.
- Public README motivation update citing arXiv:2605.07723.
- Migration tool for legacy
literature_corpus[]entries lackingcontamination_signals.
Spec: docs/design/2026-05-12-ars-v3.7.3-claim-faithfulness-and-contaminated-source-spec.md.
Backlog — gbrain harness borrow analysis (2026-05-10, post codex review)
Source: 2026-05-10 analysis of garrytan/gbrain (14.2k★ agent harness for OpenClaw/Hermes), with codex cross-model review same day. Two candidates surfaced; they have different risk profiles and are tracked separately.
Candidate A — Shared shared/_invariants.md cross-skill rules file (gbrain pattern P3). Status: backlog, low-risk.
ARS cross-cutting rules are scattered today: Iron Rules in adapter overview, hedging contract in protected_hedging_phrases.md, citation precedence in agents' frontmatter, integrity gates referenced from multiple SKILL.md. When a rule evolves (e.g. v3.6.5 corpus protocol Iron Rules), secondary mentions drift.
Shape if adopted:
shared/_invariants.mdenumerating positive invariants only (no rejected-reasoning column; that was the contamination vector in the 2026-05-10 anti-pattern-table evaluation)- File stays short, normative, and example-free — additional examples turn invariants into demonstrations and re-introduce few-shot drift
- Each SKILL.md references it via a stronger convention than
## See Also(which reads as optional reading); proposed wording at adoption time - Frontmatter
validated_against: <version>enables a stale-reference grep job on minor bumps. The grep job detects version drift only — it does NOT validate semantic compliance. Semantic checks remain a human / codex review responsibility.
Candidate B — Declarative shared/_review_pairs.yaml cross-model review config (gbrain pattern P6). Status: needs design spike before becoming a real candidate, higher-risk.
ARS cross-model review is currently invoked imperatively: ARS_CROSS_MODEL=1 env flag + manual codex review per phase. A declarative (deliverable_kind, reviewer_model, dimensions, when_to_invoke) map could improve reproducibility for Stage 2.5 / 4.5 integrity gates and Phase 6 in-pair evaluator review.
Three open problems before this is shippable:
- Refusal-routing semantics conflict. gbrain's chain (primary → DeepSeek → Qwen → Groq, silent switch) routes past refusal; ARS treats reviewer disagreement as signal. Borrowing the YAML format without resolving this imports the wrong invariant. Likely answer is "borrow the declarative-pairing shape, drop the refusal-routing chain entirely."
- Embedding governance in config. A YAML that decides "this deliverable triggers this reviewer with these dimensions" is workflow policy. Wrong shape locks in a bad routing decision across all phases. Needs a usage survey of existing manual invocations before designing the schema.
- Lower confidence than Candidate A. ARS already has review phases and cross-model invocation working manually; the missing piece is reproducibility, not the capability. If manual invocation isn't causing missed reviews or inconsistent reviews in practice, this should drop too.
Rejected from same gbrain analysis: P1 RESOLVER.md dispatcher (10 slash commands serve dispatch), P4 trust boundary (research tool, no untrusted caller class), P5 pain-triggered subagent routing (covered in user CLAUDE.md, repo-level not relevant). P2 friction protocol is a soft reject — codex review pointed out a first-class friction CLI captures pain at the moment of pain, which 5+ round codex review at deliverable-time does not. Re-examine if ARS skill development surfaces recurring author-time pain that retrospective review doesn't capture.
Meta-lesson from this analysis: "we already do something adjacent" is weaker than it sounds as a reject reason. The test is whether the existing mechanism captures the same signal at the same time with the same enforcement strength.
Added (v3.6.7 Step 6 Phase 6.8 — Step 8 evaluation case)
- 17 micro-fixtures + 1 chapter-level integration fixture under
tests/fixtures/v3_6_7_pattern_eval/exercising the 17 numbered downstream -agent patterns (A1–A5, B1–B5, C1–C3, D1–D4) per spec §7. Each micro fixture:manifest.json(fixture_kind: "micro") +upstream_context/(passport_snippet.yaml+prior_artifacts/) +bad_run/+good_run/withdeliverable.md,expected_audit_findings.yaml,expected_orchestrator_action.yaml. Integration fixture underintegration/chapter_level_run/exercises A3+C2+D4+C1 across 3-round MATERIAL escalation → ship_with_known_residue acknowledgement per §7.3. scripts/check_pattern_eval_manifest.py— fixture_kind discriminator routing micro (§7.2) vs integration (§7.3) JSON Schema 2020-12 manifest schemas;audit_verdict.schema.jsonvalidation on everyexpected_audit_findings.yaml; path-safety rejects absolute paths and..segments; coverage cross-check enforces 17/17 numbered IDs covered (with hard-fail on unknown directory names per §7.5).scripts/test_pattern_eval_runtime.py— 112-test parametrized harness reading expected verdicts as synthesized output and asserting against expected orchestrator action. Per-pattern parametrized tests (BAD signal + GOOD passes + run_id F1 regex + BAD/GOOD uniqueness); integration state runner driving §7.3 5-step procedure (load verdicts → drive §5.6 → verify pipeline state per round → feed escalation user_response → verify final passport state); Path A re-verification axis (≥6 A7 happy-path legs at rounds 2+3); finding-id lineage carry-forward per audit-template Section 6; per-phase synthetic injections (24 of 26 PHASE_TO_PASSPORT_MUTATION rows validated for "none" / "appended"); A1.5 supersession-preflight axis tests.scripts/test_run_codex_audit_e2e.py— Phase 6.1 deferred end-to-end dispatch test (Linux Bash 4+ only; macOS stock Bash 3.2 self-skips). Mocks codex CLI via PATH-prefix shim emitting canonical Phase 2 JSONL stream. Validates wrapper produces 4 contract files + 3 diagnostic files; proposal entry validates againstaudit_artifact_entry.schema.json --mode proposal(Pattern C3 defense —verified_at/verified_byabsent);--dry-runwrites nothing;--round=2without--previous-findingsrejected withEX_USAGE..github/workflows/spec-consistency.yml— 4 new CI steps: Phase 6.8 manifest validation, pattern-eval-unit (micro fixtures + phase inventory + synthetic non-supersession), pattern-eval-integration (integration fixture- synthetic supersession), Phase 6.1 wrapper E2E (Linux runner only).
docs/design/TODO-l-doc-1-18-patterns-prose-retirement.md— files L-doc-1 follow-up enumerating 8 retirement locations for the docs-only PR retiring "18 patterns" prose to "17 patterns" per §9.2.- Spec amendments at
docs/design/2026-04-30-ars-v3.6.7-step-6-orchestrator -hooks-spec.md: §7.4 success criterion 1 prose updated for C2 MINOR special case + D2 PASS convergence-policy assertion; §7.4 phase example updatedescalation→B11; §7.6 deployment note explaining named-step CI deployment (vs literal "two separate jobs"); §9.2 L-doc-1 row points at the TODO file; §7.3 example manifest snippet updated to F-101/F-103.
Notes
- 11 codex review rounds converged to 0 findings. Cumulative 24 findings closed (4 P1 + 18 P2 + 2 P3) across rounds 1-10.
- 135 Phase 6.8-specific tests; total repo regression 742 pytest + 251 unittest = 993 green + 3 skipped (macOS Bash 3.2 wrapper E2E gate).
- v3.6.7 Step 6 + Step 8 now structurally complete: prompt-level pattern protection (Step 1+2) + version sweep (Step 7) + runtime audit-artifact gate (Step 6 §1-§11 + Phases 6.1-6.7) + synthetic evaluation case (Phase 6.8) deliver the §10 ship-quality target.
[3.7.0] - 2026-05-05
Claude Code plugin packaging. ARS now installs in one line on Claude Code CLI / VS Code / JetBrains via
/plugin marketplace add Imbad0202/academic-research-skills
/plugin install academic-research-skills. The traditionalgit clone + symlink to ~/.claude/skills/flow continues to work — both tracks are first-class.
Added
- Plugin manifest + marketplace metadata (Phase 1, PR #68).
.claude-plugin/plugin.jsondeclares the suite..claude-plugin/marketplace.jsonregisters the plugin so a single GitHub-hosted endpoint serves both the marketplace listing and the plugin source.skills/directory carries relative symlinks to the four existing skill directories so the plugin loader auto-discovers them without moving repo layout. - 10 slash commands at
commands/ars-*.md(Phase 2.1, PR #69) mappingMODE_REGISTRY.mdentries to/ars-<mode>triggers. Model routing pinned in each command's frontmatter —opusforfullandrevision-coach(architectural / review-interpretation depth),sonnetfor the other 8. No Haiku perfeedback_no_haiku.md. - 3 plugin-shipped agents at
agents/*_agent.md(Phase 2.1, PR #69) as relative symlinks to the v3.6.7-hardened downstream agents indeep-research/agents/:synthesis_agent,research_architect_agent,report_compiler_agent. Underscore filenames preserved to matchscripts/check_v3_6_7_pattern_protection.pyhard-pinned paths and the INV-3 manifest-confined Clause 1 invariant. Symlinks (not copies) preserve a single source of truth and prevent the Pattern C3 attack surface that v3.6.7 §6 inversion sweep + INV-1/2/3 lint closes. model: inheritadded to those three source agent frontmatters (PR #69 R1 codex finding). Inherit chosen over pinningsonnetso an Opus session running the full pipeline keeps Opus agents (instead of being capped) while the user's existing PreToolUsewarn-agent-no-model.shhook gates Haiku at the dispatch boundary.- SessionStart announce hook at
hooks/hooks.json+scripts/announce-ars-loaded.sh(Phase 2.2, PR #70). When the plugin loads, the hook injectsadditionalContextlisting the 10 slash commands, the 3 plugin agents, and a token-budget pointer into the LLM's first turn.startupandclearsource values get the full announce;resumeandcompactget a one-line ack to avoid burning context on every resume. Bash 3.2 compatible — runs on macOS stock/bin/bashwith nobrew install bashrequirement.${CLAUDE_PLUGIN_ROOT}quoted for install paths containing spaces. docs/PERFORMANCE.md+.zh-TW.mdsubsection "v3.7.0 Plugin agents and model routing" explainingmodel: inheritsemantics and the current 3-agent scope boundary.docs/ARCHITECTURE.mdEvolution Timeline extended with v3.6.7 / v3.6.8 / v3.7.0 entries.- README + README.zh-TW version badge bumped to v3.7.0; Pipeline section heading bumped to v3.7; CHANGELOG entry added.
Deferred (future release)
- SubagentStop →
run_codex_audit.shcodex audit hook (Phase 2.2 scope reduction). Two compounding reasons: (a) wrong invoker class —run_codex_audit.shlines 4–7 forbid same-session in-LLM invocation (Pattern C3 attack surface), and the original PostToolUse Write|Edit matcher would fire from inside the producing session; (b) contract gap — the SubagentStop hook payload carries no stage/deliverable info, so a wrapper would have to half-infer those required arguments. Real audit-hook integration deferred to a future release when ARS gains a stage/deliverable propagation contract. Seedocs/design/2026-04-30-ars-v3.7.0-plugin-packaging-roadmap.mdUpdate note 2026-05-05 (Phase 2.2 scope reduction).
Changed
academic-pipeline/SKILL.mdfrontmatterversion: "3.7.0"+ H1 + Version Info table.MODE_REGISTRY.mdLast updated bumped tov3.7.0 (2026-05-05)..claude/CLAUDE.mdSkills Overview row + Suite version footer bumped to 3.7.0.scripts/check_spec_consistency.pylint pins (Suite version, README badge, MODE_REGISTRY heading, CHANGELOG section heading) bumped to v3.7.0.
Unchanged
The four skill directories, all 25 modes, agent prompts, schema files,
and lint contracts. Plugin packaging only adds new top-level surface
(commands/, agents/, hooks/, .claude-plugin/, skills/ symlink
dir, three plugin-agent model: inherit frontmatter additions).
Existing 4.3k clone-install users see no breaking change.
Codex review chain
8 inline iterative rounds + 3 fresh PR-level rounds across the three
PRs (#68 / #69 / #70), all converging to 0 P0/P1/P2 findings before
merge. The Phase 2.2 fresh PR review caught one P2 (unquoted
${CLAUDE_PLUGIN_ROOT} breaking install paths with spaces) that the
inline rounds missed — confirms the value of separating implementation
review (inline) from contract / install-time review (fresh).
Reference: feedback_codex_review_vs_resume_audit_scope.md.
[3.6.8] - 2026-05-03
Naming note: this release ships the v3.6.6 generator-evaluator contract spec (
docs/design/2026-04-27-ars-v3.6.6-generator-evaluator-contract-design.md) and its implementation. The v3.6.6 work landed after v3.6.7 due to project sequencing; the design doc retains the v3.6.6 internal naming for the contract gate version (writer_full/evaluator_fullmode, Schema 13.1,pre_commitment_artifacts+disagreement_handlingschema fields), while the suite release is tagged v3.6.8 to keep the CHANGELOG monotonic.
Added
- Schema 13.1 generator-evaluator contract gate for
academic-paper fullmode (shared/sprint_contract.schema.json, design doc §3): two newmodeenum values (writer_full+evaluator_full); two new optional top-level fields (pre_commitment_artifactswriter-only withacceptance_criteria_paraphrase.minimum_dimensions;disagreement_handlingevaluator-only withparaphrase_minimum_dimensions+scoring_plan+pre_commitment_check_protocol+disagreement_resolution); 12allOfbranches enforcing reviewer- / writer- / evaluator-conditional gates (existing 2 + 10 new per design doc §3.5 table). - Two new shipped contract templates:
shared/contracts/writer/full.json(writer dimensions D1 section_completeness / D2 citation_density / D3 argument_blueprint_fidelity / D4 total_word_count / D5 per_section_word_count / D6 acknowledged_limitations / D7 register_consistency; F-conditions F1/F4/F2/F3/F0; noscoring_plan) andshared/contracts/evaluator/full.json(evaluator dimensions D1 originality / D2 methodological_rigor / D3 evidence_sufficiency / D4 argument_coherence / D5 writing_quality; F-conditions F1/F2/F3/F6/F4/F5/F0; fullscoring_plan+disagreement_handling). Templates already shipped on the spec branch as design-time artefacts since 2026-04-28; this release promotes them to live status atomically with the Schema 13.1 upgrade. - Two-phase orchestration inside
academic-paper fullmode (design doc §5): Phase 4 splits into Phase 4a paper-blind writer pre-commitment + Phase 4b paper-visible drafting + self-scoring. Phase 6 splits into Phase 6a paper-blind evaluator pre-commitment + Phase 6b paper-visible scoring + decision. Phase-numbered<phase4a_output>/<phase6a_output>data delimiters mirror the v3.6.2 reviewer pattern. Lint counts: writer 3+4 / evaluator 5+5 / reviewer 5+6 (reviewer surfaces remain zero-touch per §3.6).[GENERATOR-PHASE-ABORTED]abort tag with 5% / three-month operational monitor. academic-paper/SKILL.md## v3.6.6 Generator-Evaluator Contract Protocolorchestration block (101 lines): four-call structure with system-vs-user content discipline, schema-vs-runtime emission distinction, per-phase lint, abort handling, two valid Stage 3 entry paths (standard F0/F4 + exceptional F5), cross-session resume scope. Plus a new## Known limitationssection carrying the graceful-degradation forward note (v3.6.7 candidate) + the cross-session resumepre_commitment_history[]forward note (v3.6.7+ candidate) + in-pair Phase 6 evaluator vs externalacademic-paper-reviewertech debt.academic-paper/agents/draft_writer_agent.md+peer_reviewer_agent.mdeach gain a verbatim## v3.6.6 Generator-Evaluator Contract Protocolsection with the system-prompt sub-sections for Phase 4a/4b (writer) and Phase 6a/6b (evaluator). The orchestrator includes the relevant sub-section verbatim in the system prompt for the corresponding call; user content carries contract JSON, paper metadata, delimiter blocks, and upstream artefacts per the SKILL.md discipline.scripts/check_sprint_contract.pySC- mode-gating audit* (per §7.1 implementation requirement): SC-5 (measurement_procedure canonical outputs) and SC-11 (panel_size sanity) now mode-gated tomode.startswith("reviewer_")so they do not noise on clean writer / evaluator templates. SC-9 (paraphrase_minimum_dimensions exceeds dim count) extended across all three mode families: reviewer readsmp.paraphrase_minimum_dimensions, writer readspre_commitment_artifacts.acceptance_criteria_paraphrase.minimum_dimensions, evaluator readsdisagreement_handling.paraphrase_minimum_dimensions. Mode-agnostic warnings (SC-1 baseline lag, SC-2 single dimension, SC-3 no mandatory, SC-4 orphan dim ref, SC-7 conflicting actions, SC-10 unreferenced mandatory/high) unchanged.- 17 new validator tests (54 → 71 total): 4 writer/evaluator template
positive tests; 5 schema-branch negative tests covering branches 11 / 12 /
4 / 5 / 6 hard-fail (cross-mode field leakage intentionally NOT a v3.6.6
hard-fail per §7.1 R1 settled — v3.7.x
not-clause hardening is the long-term fix); 2 §3.6 reviewer regression tests (test_existing_reviewer_contracts_still_valid_under_13_1+test_byte_equivalent_validation_for_reviewer_contracts); 6 SC-5/SC-9/SC-11 mode-gating tests. scripts/check_v3_6_6_ab_manifest.py(new) implements the §7.5 manifest CI lint: schema-shape checks per §6.2 (top-level required fields with declared types; per-paper required fields; paper_id uniqueness; aggregate role counts 6+1; paper-A paper_type families 3 × 2; paper-A required judge_output_baseline; paper-C must-have known_failure_mode + failure_evidence; paper-C must-not-have judge / metrics fields); path-existence checks (mode-conditional + populated-optional); reverse-scan against fixture-orphans; exit-1-on-malformed-YAML mirrorscheck_sprint_contract.pyconvention..github/workflows/spec-consistency.ymlextends the "Validate sprint contract templates" step to iterate writer + evaluator template directories alongside the existing reviewer loop, and adds a new "Validate v3.6.6 A/B fixture manifest" step running the new manifest CI lint script as an additional step inside the existingspec-consistencyjob.tests/fixtures/v3.6.6-ab/A/B evidence fixture stub (30 files): manifest.yaml + README.md + 6 paper-A inputs/baseline + 1 paper-C inputs/baseline + Stage 3 reviewer excerpt + 6 codex-judge baseline placeholders.manifest_lint_mode: spec_branch,fixture_version: 0.1.0. Each placeholder explains the expected populated content; real fixture data (existing deep-research synthesis reports for paper-A; v3.6.5 session log- Stage 3 reviewer excerpt for paper-C; codex gpt-5.5 + xhigh judge runs against paper-A baseline) populates in follow-up commits before the v3.6.6 implementation work fully completes.
academic-paper-reviewer/references/sprint_contract_protocol.mdcross-reference noting Schema 13.1 since v3.6.6 + pointing readers atacademic-paper/SKILL.md+ design doc §5 for the parallel generator-evaluator protocol. The reviewer protocol itself is byte-equivalent across v3.6.2 → v3.6.8 (zero-touch promise per §3.6).
Changed
- Suite version: v3.6.7 → v3.6.8 (per the naming note above; design doc retains v3.6.6 for the contract gate version).
academic-pipelineskill version bumped from v3.6.7 to v3.6.8 in the.claude/CLAUDE.mdSkills Overview table.
Deferred
- Real fixture data populate for
tests/fixtures/v3.6.6-ab/(30 placeholders → real paper-A inputs + baseline + paper-C session log + codex judge runs) lands in follow-up commits. - Treatment runs (writer Phase 4a/4b + evaluator Phase 6a/6b on the seven
fixtures), codex judge against treatment, and **metrics computation
- summary.md** require actual
academic-paper fullinvocations + Semantic Scholar API + codex CLI runs; deferred to follow-up commits before the fixture-completeness work concludes.
- summary.md** require actual
- manifest_lint_mode flip from
spec_branchtoimplementation_prco-lands with the treatment population in the same atomic merge state per §6.5 invariant 3. - ROADMAP §3.6.4 description correction per design doc §9.3 ("Extend
v3.6.2 sprint contract pattern to the existing
academic-paperwriter/evaluator pair via contract-gated phase splits and Schema 13.1 conditional gates. No new agent files; existingdraft_writer_agentandpeer_reviewer_agentgain per-phase sub-section instructions") lands in the private ROADMAP.md (gitignored, maintained outside this public repo), not in this repo PR.
[3.6.7] - 2026-04-30
Added
- Downstream-agent pattern protection layer (
docs/design/2026-04-29-ars-v3.6.7-downstream-agent-pattern-protection-spec.md). Hardens three downstream agents against 17 hallucination/drift patterns documented in the spec:synthesis_agent(A1–A5 narrative-side), the survey-designer mode ofresearch_architect_agent(B1–B5 instrument-side), and the abstract-only mode ofreport_compiler_agent(C1–C3 publication- side), plus four cross-cutting patterns (D1–D4). Patterns observed in production output across multiple chapter-length runs. - Four reference files in
shared/references/carrying the operational contracts that protection clauses cite:irb_terminology_glossary.md— anonymity vs confidentiality vs de-identification vs pseudonymization (B1).psychometric_terminology_glossary.md— true reverse-coded vs contrast item, with construct-equivalence rule (B2).protected_hedging_phrases.md— five-rule contract for upstream-marked hedge protocol (conservative inclusion, anchor every entry, no duplicates, verbatim preservation, conflict reporting) (C1).word_count_conventions.md— whitespace-split standard (body.split()), 3–5% buffer below hard cap, publisher conventions (C1).
- Cross-model audit prompt template at
shared/templates/codex_audit_multifile_template.md— seven audit dimensions (cross-ref, hallucination, primary-source integrity, internal coherence, instrument quality, Round-N framing, COI adequacy) plus a mandatory three-part Section 4(f) check forreport_compiler_agentbundles (whitespace-split cap-minus-buffer, protected-hedge verbatim, abstract no less hedged than body — failure of any sub-check is P1). - Static lint at
scripts/check_v3_6_7_pattern_protection.pyenforcing protection-clause presence and obligation-phrase shape across the reference files, audit template, and three downstream agent prompts. Per-regexallow_prohibitionflag scopes the prohibition exemption so prohibition-style obligations (DO NOT simulate,must not claim audit-passed state,does not paraphrase) do not leak the exemption to assertion-style obligations on the same Check. Span-restricted exemption rejects a second prohibition elsewhere in the bullet. Modal/advisory weakener coverage:may,should,can,will,would,ought to,ideally,preferably,We recommend that,is/are recommended,is/are allowed,is/are permitted, plus exception qualifiers (except,unless,save when). - Mutation test suite at
scripts/test_check_v3_6_7_pattern_protection.pywith 29 tests preserving codex review evidence (R2–R6). Future checker regressions surface in CI rather than only in ad-hoc mutation runs. - CI wiring in
.github/workflows/spec-consistency.ymlruns both the static lint and the mutation suite on every push and pull request.
Changed
deep-research/agents/synthesis_agent.mdcarries aPATTERN PROTECTION (v3.6.7)block with five clauses covering effect-inventory cross-section consistency self-check, pending-verification hedge wrap, one-line anchor justification, verbatim phrase boundary on quotes, and the prohibition on declarative claims about un-provided documents (with conditional-language fallback).deep-research/agents/research_architect_agent.mdsurvey-designer mode carries aPATTERN PROTECTION (v3.6.7)block with five clauses covering IRB terminology pass-through, reverse-coded construct- equivalence justification, event-anchored retrospective default (calendar-anchored only when sample shares a common event date), neutral-balanced item phrasing with chapter argument vocabulary forbidden, and primary-source list enumerate-fully (no subsetting, no over-setting, no scope cross-contamination).deep-research/agents/report_compiler_agent.mdabstract-only mode carries aPATTERN PROTECTION (v3.6.7)block with three clauses covering whitespace-split word budget plus 3–5% buffer with budget- protected hedges, explicit-temporal-bounds reflexivity disclosure (year range / past-tense disambiguating verb / "former" prefix; deictic phrases forbidden), and the anti-fake-audit guard (DO NOT simulate any audit step; DO NOT claim to have run codex/external review; output metadata must not claim audit-passed state).
Notes
- v3.6.7 ships in two stages. Step 1 + Step 2 (this entry) include the four reference files, the audit template, the static lint, the mutation test suite, the CI wiring, and the three agent-prompt protection blocks. Step 6 (orchestrator hooks for automatic per-agent audit and anti-fake-audit guard wiring) and Step 8 (synthetic evaluation case demonstrating all 17 patterns triggered + protected) ship in a follow-up PR. Step 6 is cross-agent runtime work that warrants its own design discussion and is intentionally decoupled from this prompt-and-lint PR.
- Codex review history: seven rounds of
gpt-5.5+xhighcross-model review reached SHIP-OK with zero P1 + P2 findings. R1 closed ten Step-1 findings; R2 closed four cascade gaps plus the per-Checkallow_prohibitionleak; R3 closed three P2 findings (span-restricted exemption, token→regex with imperative anchoring,except/unless/ save whenweakeners); R4 closed three P2 findings (modal verb scope expansion, §6 sub-clause coverage, lint→CI wiring); R5 closed one P2 plus one P3 (should/can/permittedmodals and the mutation test suite); R6 closed one P2 (will/would/ought to/ideally/preferably/ We-recommend-thatweakeners) and explicitly deferred orchestrator runtime hooks to the Step 6 follow-up PR. R7 surfaced only one P3 add-counter signal (try to / generally / where relevantweakeners), which is non-blocking polish. - ARS pipeline ship-quality target updates from "each agent produces a clean v1" to "end-to-end deliverable set passes independent xhigh cross-model audit at 0 P1 + P2 finding within three rounds" (per spec §10).
[3.6.5.2] - 2026-04-27
Changed
docs/SETUP.mdMethod 4 (claude.ai) recommendation revised. Method 4b (Project + GitHub integration) is now presented first as the recommended claude.ai path, since it brings the repository into Project knowledge for reading and citation without losing fidelity. Method 4a (Custom Skill upload) is now explicitly marked as not recommended for this suite, with a rationale paragraph covering two compounding reasons:- ARS depends on Claude Code-only orchestration features. Each skill drives
12-13 specialised agents through Claude Code's Task / subagent tooling
and Material Passport file handoffs that resume across sessions.
claude.ai Custom Skills do support multi-file packages with
scripts/and code execution per Anthropic's documentation, but the Anthropic- documented scope of the claude.ai Custom Skill runtime does not include Claude Code's Task / subagent control surface or cross-session Material Passport handoffs. The recommendation is forward-looking based on those documented assumptions; we have not run a live upload to characterise the actual surfacing in claude.ai. - Trimming the four
descriptionfields below claude.ai's 200-character cap would weaken Claude Code and Cowork routing on the platforms the suite was actually built for. The Agent Skills specification and Claude Code Skills documentation both allow up to 1,024 characters; only claude.ai's upload UI enforces 200. Trading Claude Code and Cowork routing precision for partial functionality on the limited claude.ai path was judged not worth it.
- ARS depends on Claude Code-only orchestration features. Each skill drives
12-13 specialised agents through Claude Code's Task / subagent tooling
and Material Passport file handoffs that resume across sessions.
claude.ai Custom Skills do support multi-file packages with
- Method 4a install commands kept in place for users who decide to try it anyway, framed as "if you want to try this path despite the limitations" rather than as a recommended flow. The upload UI's expected rejection on description-too-long is documented as deliberate, not an oversight to fix later.
docs/SETUP.zh-TW.mdmirrors the English changes end-to-end.
Notes
- Doc-only patch. No
SKILL.md(frontmatter or body), no agent file, no schema, no script, no test, no workflow, and no version bump in any skill changed in this patch. The four currentdescriptionfields stay at their Claude Code-native lengths (440-842 characters) so routing on Claude Code and Cowork remains intact. - This patch is a scope change from the v3.6.5.2 originally forecast in the v3.6.5.1 SETUP doc. The earlier plan was a description trim; on review, the trim direction was abandoned because it would have damaged Claude Code and Cowork routing to unblock a path that delivers an untested partial fit anyway. The v3.6.5.1 SETUP text's forward-promise of a description trim is removed here.
- Issue #44 receives a single consolidated reply on this PR's merge, summarising both v3.6.5.1 (SETUP doc rewrite) and v3.6.5.2 (Method 4a recommendation), and closes there.
[3.6.5.1] - 2026-04-27
Fixed
docs/SETUP.mdMethod 3 install paths — Option A (symlink) and Option B (copy) now install each of the four skill folders separately into~/.claude/skills/<skill-name>/, matching the<install-root>/<skill-name>/SKILL.mddiscovery convention. The previous text installed the whole repo under~/.claude/skills/academic-research-skills/, which buried the fourSKILL.mdfiles one level too deep for Cowork / Claude Code discovery.docs/SETUP.mdMethod 4 (claude.ai) restructured — split into Method 4a (Custom Skill upload via Settings → Capabilities → Skills, the standard claude.ai Skill install path) and Method 4b (Project + GitHub integration, fallback knowledge mode and not a Skill install). The previous text framed GitHub integration as a Skill install path, which conflated content retrieval with skill execution. Method 4a documents the current 200-characterdescriptioncap blocker (this entry originally forecast a description trim in v3.6.5.2; see the v3.6.5.2 entry above for the actual decision — Method 4a is documented as not recommended for this suite, and descriptions remain at their Claude Code-native lengths).- Method 3 prerequisites — expanded from one sentence to a full prerequisites subsection covering Claude Desktop version, internet connectivity, Cowork process model, folder permissions, paid plan, and Team/Enterprise org-admin controls.
- Method 4 prerequisites — split per sub-method. 4a documents zip structure + description cap surfacing as upload-time errors; 4b documents GitHub authentication via the Anthropic connector, private-repo App authorization, and Team/Enterprise owner-level connector enablement.
- Cowork UI terminology — replaced "Cowork tab" / "working directory" with current Cowork UI labels: mode selector (Chat / Cowork), Tasks view, "Use an existing folder" in the left navigation panel, and Cowork Project as the canonical term.
- Skill invocation framing — clarified that Claude uses each skill's
descriptionfor relevance routing rather than literal trigger-phrase matching, and documented the Cowork/command palette and+capability picker as explicit invocation surfaces. - Method 4 directory table — added the
scripts/row (required for Material Passportliterature_corpus[]adapters and schema validators) and refreshed the project-capacity guidance against current Anthropic Project file limits (per-file 30 MB; file count is not artificially capped at 200). docs/SETUP.zh-TW.md— mirrored the English rewrite end-to-end so Traditional Chinese readers see the same structure and content for Methods 1-4.QUICKSTART.mdStep 1 — install commands aligned with the new Method 3 four-symlink approach.
Notes
- Doc-only patch. No skill content (
SKILL.md), no agent file, no schema, no script, and no test changed in this patch. - Issue #44 (philpav)
reports SETUP problems on Cowork and claude.ai. v3.6.5.1 fixes the SETUP doc;
this entry originally forecast a
SKILL.mddescription-length fix in v3.6.5.2, but v3.6.5.2 instead documents Method 4a as not recommended for this suite (see the v3.6.5.2 entry above for the actual decision). Issue #44 receives a single consolidated reply and closes on v3.6.5.2 ship.
[3.6.5] - 2026-04-27
Added
- Material Passport
literature_corpus[]consumer integration in Phase 1 (deep-research/bibliography_agent + academic-paper/literature_strategist_agent). Corpus-first, search-fills-gap flow with PRE-SCREENED reproducibility block. Reproducibility for systematic-review use is preserved through Iron Rule 1 same-criteria parity plus Step 2 case C (standard external search runs even when corpus fully covers RQ subtopics). academic-pipeline/references/literature_corpus_consumers.md— consumer protocol reference with four Iron Rules (Same criteria / No silent skip / No corpus mutation / Graceful fallback on parse failure) and per-consumer reading instructions.scripts/check_corpus_consumer_protocol.py— CI lint enforcing nine protocol invariants with manifest-driven consumer list and stub-block opt-out.scripts/corpus_consumer_manifest.json— supported-consumer manifest.
Changed
shared/handoff_schemas.mdSchema 9 — retired the v3.6.4 "Consumer-side integration deferred to v3.6.5+" caveat; replaced with backpointer to the consumer protocol.deep-research/SKILL.md2.9.1 → 2.9.2 — bibliography_agent corpus-first flow (also syncs Version Info footer that lagged at 2.9.0).academic-paper/SKILL.md3.1.0 → 3.1.1 — literature_strategist_agent corpus-first flow.academic-pipeline/SKILL.md3.6.4 → 3.6.5 — suite version invariant..claude/CLAUDE.md,MODE_REGISTRY.md,README.md,README.zh-TW.md,scripts/check_spec_consistency.pyupdated for the version bump (suite version, badge, tag, changelog heading).
Notes
- Consumer integration is presence-based: auto-engages when passport carries a
non-empty
literature_corpus[]and parses cleanly. Parse failures fall back to external-DB-only flow with a[CORPUS PARSE FAILURE]surface. No new env flag introduced. - Schema is unchanged from v3.6.4. Existing user adapters work without modification.
citation_compliance_agentcorpus integration deferred to v3.6.6+.source_pointeris not dereferenced by consumers; URI resolution remains a futuresource_verification_agentconcern.
[3.6.4] - 2026-04-25
Added
- Material Passport
literature_corpus[]input port. Schema 9 gains an optionalliterature_corpus[]field defined byshared/contracts/passport/literature_corpus_entry.schema.json. Each entry carriescitation_key, CSL-JSONauthors,year,title, and asource_pointerback to the user's own KB.abstractanduser_notesare private optional fields with copyright caveats. - Adapter contract (
academic-pipeline/references/adapters/overview.md): language-neutral specification for producing literature_corpus entries from user-owned corpus sources. Covers fail-soft entry-level error handling, mandatoryrejection_log.yamloutput, deterministic ordering (sort bycitation_key/source), and extension points for user-written adapters. - Three reference Python adapters (
scripts/adapters/):folder_scan.py(filesystem of PDFs),zotero.py(Better BibTeX JSON export),obsidian.py(vault frontmatter, BibTeX-style or literature-note convention). Each ships with pytest tests, fixtures, and golden expected outputs. - Rejection log contract (
shared/contracts/passport/rejection_log.schema.json). Always emitted; empty when no rejections; closed enum of categorical reason values. - CI lint + pytest job:
scripts/check_literature_corpus_schema.py(schema + adapter example validation),scripts/sync_adapter_docs.py --check(schema→docs drift detector with auto-regen mode), and a new.github/workflows/pytest.ymlrunningscripts/adapters/tests/on path-filtered triggers. _common.ensure_unique_citekey(key, existing)helper for adapters whose source already supplies a citekey (zotero, obsidian frontmatter), with sanitization to satisfy the schema pattern and a/b/...zz alpha-suffix collision disambiguation._common.path_to_file_uri(path)helper that delegates toPath.as_uri()so spaces and reserved characters in filenames are properly percent-encoded.
Changed
academic-pipeline/references/passport_as_reset_boundary.md: "deferred to v3.6.4, PR-B" placeholders replaced with forward references toadapters/overview.mdandliterature_corpus_entry.schema.json.shared/handoff_schemas.md: Schema 9 optional fields table addsliterature_corpus; new "Literature Corpus Input Port (v3.6.4)" subsection appended after Reset Boundary Extension.academic-pipeline/SKILL.mdbumped 3.6.3 → 3.6.4 (suite version invariant). Other skills retain independent semver..claude/CLAUDE.md,MODE_REGISTRY.md,README.md,README.zh-TW.md,scripts/check_spec_consistency.pyupdated for the version bump (suite version, badge, tag, changelog heading).
Not changed (explicit non-goals)
- No ARS agent consumes
literature_corpus[]yet. Consumer-side integration is deferred to v3.6.5+. v3.6.4 defines the input port only. - No PDF parsing, no text extraction, no live API clients, no authenticated library crawling. The reference adapters read filenames or local export files and never make network calls.
[3.6.3] - 2026-04-23
Added
- Opt-in passport reset boundary via
ARS_PASSPORT_RESET=1. Every FULL checkpoint becomes a context-reset boundary when the flag is set.systematic-reviewmode with the flag ON makes reset mandatory; other modes treat reset as the flag-gated default. resume_from_passport=<hash>mode inacademic-pipeline. Lets users resume a pipeline run in a fresh Claude Code session from the Material Passport ledger alone.- Schema 9
reset_boundary[]optional append-only field with two entry kinds (boundary,resume). Entry shape inshared/contracts/passport/reset_ledger_entry.schema.json(oneOf split withkinddiscriminator). Hash computed via JSON Canonical Form + SHA-256 with"000000000000"placeholder for self-reference safety. Optionalpending_decisionfield handles MANDATORY branch choices (Stage 3 reject/restructure/abort, Stage 5 finalization) that survive the reset boundary. - Protocol doc:
academic-pipeline/references/passport_as_reset_boundary.md(authoritative; every file mentioningARS_PASSPORT_RESETmust co-locate a reference). - CI lint:
scripts/check_passport_reset_contract.py+ unittest suite. Wired into.github/workflows/spec-consistency.yml. docs/PERFORMANCE.md+docs/PERFORMANCE.zh-TW.mdlong-running-session subsection documenting when reset beats continuation, passport file-location convention, and empirical-measurement disclaimer.
Changed
academic-pipeline/agents/pipeline_orchestrator_agent.mdadds §"Passport Reset Boundary (v3.6.3+)" and §"Resume Mode:resume_from_passport". FULL Checkpoint Template includes conditional reset-handoff tag slot.academic-pipeline/references/pipeline_state_machine.mddocumentsawaiting_resumetransitions derived from the ledger (no out-of-band state).academic-pipeline/SKILL.mdaddsresume_from_passportto the mode table and bumps version 3.6.2 → 3.6.3.shared/handoff_schemas.mdSchema 9 gainsreset_boundaryrow + "Reset Boundary Extension (v3.6.3)" subsection with full YAML example showing both kinds.
Changed (post-P1 fixes)
pending_decision.options[]now carries per-branch routing ({value, next_stage, next_mode});valueuniqueness within one options array is enforced by CI lint (scripts/check_passport_reset_contract.py). The matched option'snext_stagesupersedes the boundary entry's advisorynextfield.nextMAY benullwhen all branches terminate or no sensible default exists.- Exclusive advisory lock (POSIX
fcntl.flock LOCK_EX, bounded timeout not exceeding 60 s, 30 s recommended) is required for the resume read-check-append sequence. Non-POSIX implementations MUST refuse to resume rather than degrade silently.
Notes
- Flag OFF is the default. Pre-v3.6.3 behavior is preserved byte-for-byte when
ARS_PASSPORT_RESETis unset or=0. - Out of scope (deferred to v3.6.4):
examples/adapters/{folder_scan, zotero, obsidian}/reference adapters and theliterature_corpusentry shape on Schema 9. - No breaking changes. No existing mode behavior changes when the flag is OFF.
[3.6.2] - 2026-04-23
Added
- Sprint Contract (Schema 13) — reviewer hard gate.
shared/sprint_contract.schema.jsondefines machine-checkable acceptance criteria (panel_size,acceptance_dimensions,failure_conditionswithseverity+cross_reviewer_quantifier,measurement_procedure, optionaloverride_ladder, boundedagent_amendments). Validatorscripts/check_sprint_contract.py(schema validation +check_structural_invariants()hard check + nine soft warnings SC-1..SC-11 with SC-6 documented as dead path and SC-8 promoted to hard check). Two templates ship:shared/contracts/reviewer/full.json(panel 5) andshared/contracts/reviewer/methodology_focus.json(panel 2). Reviewer orchestration reshaped into paper-content-blind Phase 1 + paper-visible Phase 2 hard gate. Synthesizer runs three-step mechanical protocol (build matrix → evaluate with quantifier → resolve precedence). Seedocs/design/2026-04-23-ars-v3.6.2-sprint-contract-design.md. - Token cost note. Reviewer total calls under sprint contract =
2 × panel_size. Forreviewer_full: 5 → 10 calls. Phase 1 input is metadata-only and output short, so real token bound is well below 2x.
Changed
academic-paper-reviewerv1.8.1 → v1.9.0. Five reviewer agent markdown files (EIC + methodology + domain + perspective + DA) gain Phase 1/2 protocol sections;editorial_synthesizer_agent.mdgains the three-step synthesizer protocol + forbidden-operations list.- Harness retirement notes folded in. The prior
[Unreleased]harness-retirement pass (Task A perproject_ars_v3.6_execution_order.md) ships with this release — 7 negative-framing blocks rewritten to positive / split form across 7 files, no behaviour change:academic-paper/agents/socratic_mentor_agent.md— Core Principles items 1, 6 (F-001)deep-research/agents/socratic_mentor_agent.md— Quality Standards items 2, 3, 4 (F-002)academic-paper/agents/draft_writer_agent.md— quick style check, paragraph variation, colloquialisms, transition-word usage (F-003, 4 spots)academic-pipeline/agents/pipeline_orchestrator_agent.md— split "Prohibited Actions" (9 items, all negative) into "Scope (delegate, don't perform)" (items 1-6, positive delegation) + "Hard boundaries (never violate)" (items 7-9, kept negative as intentional safety directives for silent-failure modes: fabrication, skipped checkpoints, skipped integrity gates) (F-004)academic-pipeline/agents/collaboration_depth_agent.md— Agent-specific boundaries 4 bullets (F-005)academic-pipeline/SKILL.md— single-line UX guidance (F-006)academic-paper/references/academic_writing_style.md— §4 Formality 3 items (F-007, discovered during apply)
Notes
reviewer_re_review,reviewer_calibration,reviewer_guidedare reserved in the Schema 13modeenum but ship without contract templates in v3.6.2. Those modes continue pre-v3.6.2 behaviour until a follow-up patch adds their templates.reviewer_quickis intentionally excluded from the Schema 13modeenum (Q3-A' boundary).- CI gate:
validate-sprint-contractsstep in.github/workflows/spec-consistency.ymlruns the full unit test suite and validates every template undershared/contracts/reviewer/*.jsonagainst the current ARS version. - Kept-as-debt from harness retirement: ~50 anti-hallucination references across
deep-research/,academic-paper/references/anti_leakage_protocol.md,academic-pipeline/references/ai_research_failure_modes.md,shared/agents/compliance_agent.md,shared/compliance_checkpoint_protocol.md— load-bearing integrity architecture (Lu 2026 7-mode; S2 API Tier-0;[MATERIAL GAP]taxonomy). Not retired under the iron rule clause for silent-failure domains.
[3.5.1] - 2026-04-22
Added
- Opt-in Socratic reading-check probe. When
ARS_SOCRATIC_READING_PROBE=1is set, the Socratic Mentor fires a one-time honesty probe during goal-oriented sessions where the user has cited a specific paper. The probe asks the user to paraphrase one passage. Decline is logged without penalty. Outcome is recorded in the Research Plan Summary and flows into the Stage 6 AI Self-Reflection Report when the pipeline continues. Default OFF. Roadmap slot: v3.7.3. Seedeep-research/agents/socratic_mentor_agent.md§"Optional Reading Probe Layer".
Changed
deep-research/SKILL.md,deep-research/references/socratic_mode_protocol.md,academic-pipeline/references/process_summary_protocol.md— aligned text updates for the new probe section. No behaviour change when the env var is unset.
Version
- Suite: 3.5.0 → 3.5.1 (patch; opt-in, default OFF, no breaking change)
deep-researchskill: 2.9.0 → 2.9.1academic-pipelineskill: 3.5.0 → 3.5.1 (tracks suite version percheck_version_consistency.pyinvariant)
[3.5.0] - 2026-04-21
Added
shared/collaboration_depth_rubric.mdv1.0 — canonical 4-dimension rubric (Delegation Intensity, Cognitive Vigilance, Cognitive Reallocation, Zone Classification). Based on Wang, S., & Zhang, H. (2026). "Pedagogical partnerships with generative AI in higher education: how dual cognitive pathways paradoxically enable transformative learning." International Journal of Educational Technology in Higher Education, 23:11. DOI 10.1186/s41239-026-00585-x. Licensed CC-BY-NC 4.0.academic-pipeline/agents/collaboration_depth_agent.md— observer agent (Agent Team grows 3 → 4). Invoked at every FULL/SLIM checkpoint and at pipeline completion; scores user-AI collaboration pattern against the canonical rubric. Advisory only — never blocks progression. Frontmatter declaresblocking: false,measures: collaboration_depth,rubric_ref: shared/collaboration_depth_rubric.md.scripts/check_collaboration_depth_rubric.py+scripts/test_check_collaboration_depth_rubric.py— new lint enforces: (1) rubric file exists; (2) rubric cites Wang & Zhang 2026 with DOI; (3)rubric_versionfrontmatter field; (4) four canonical dimension headings; (5)/(6) any agent claimingmeasures: collaboration_depthreferences the canonical rubric path and declaresblocking: false; (7)/(8) orchestrator and SKILL.md mention observer with non-blocking semantics. 10 unit tests, all green.academic-pipeline/references/changelog.mdrow v2.8.academic-pipeline/references/reinforcement_content.mdrow for FULL/SLIM checkpoint — IRON RULE: observer is advisory only, never blocks, never a leaderboard.
Changed
academic-pipeline/SKILL.md— version bump3.3.0 → 3.4.0. Agent Team table grows to 4 rows. New "Collaboration Depth Observer" section with explicit non-blocking guarantees and distinction from integrity verification and Stage 6 self-reflection. Reference Files table adds rubric entry.academic-pipeline/agents/pipeline_orchestrator_agent.md— checkpoint Steps flow amended: afterstate_trackerupdate the orchestrator invokescollaboration_depth_agenton the just-completed stage's dialogue range (FULL/SLIM only; MANDATORY integrity gates explicitly skip) and injects its output into checkpoint templates as a named "Collaboration Depth" section. FULL checkpoint template expanded with the observer block; SLIM template gains a one-line compact observer summary; MANDATORY template unchanged (integrity gates never dilute). New "Collaboration Depth Observer" subsection under §3 Checkpoint Management covers invocation, cross-model behaviour, short-stage guard, and non-blocking IRON RULE.academic-pipeline/agents/state_tracker_agent.md— Write Access Control addscollaboration_depth_agent(append-onlycollaboration_depth_history[]). Newdialogue_log_refturn-range pointer per stage; newcollaboration_depth_history[]root-level array; newappend_observer_report()function (only function that writes the history; preconditions block any attempt to turn observer output into a blocking condition).scripts/_skill_lint.py— new sharedsplit_frontmatter(text) -> (dict|None, str)lenient helper, reused by the new lint.- Suite version bumped to
3.5.0acrossREADME.md,README.zh-TW.md,MODE_REGISTRY.md,.claude/CLAUDE.md; new### v3.5.0 (2026-04-21)section in both READMEs; new## v3.5 Key Additionsblock in.claude/CLAUDE.md. scripts/check_spec_consistency.py— README version expectations bumped tov3.5.0;MODE_REGISTRY.mdlast-updated expectation updated;.claude/CLAUDE.mdsuite version expectation updated. New embedded-changelog regression checks for### v3.5.0 (2026-04-21)entries.
Notes
- MANDATORY integrity checkpoints (Stages 2.5, 4.5) are not instrumented by the observer. The observer never appears in the "Flagged" line of any checkpoint.
blocked_by: collaboration_depth_agentis never a legal state. The orchestrator's numbered Step 3 explicitly branches on checkpoint_type. - Cross-model behaviour (
ARS_CROSS_MODEL): observer runs on both models; dimension disagreement > 2 points is flagged explicitly, never silently averaged.ARS_CROSS_MODEL_SAMPLE_INTERVALescape hatch documented. - Short-stage guard: if the completed stage has fewer than 5 user turns, a static
insufficient_evidenceblock is injected and the full-model observer call is skipped. - Credit: Wang & Zhang (2026) introduced the dual-pathway SEM and three-zone (Zone 1 / Zone 2 / Zone 3) framework that anchors the rubric's dimension operationalisation and synthesis rule.
[3.4.0] - 2026-04-20
Added
shared/agents/compliance_agent.md— single mode-aware agent for PRISMA-trAIce + RAISE compliance. Dispatches oncompliance_mode ∈ {systematic_review, primary_research, other_evidence_synthesis}. See design specdocs/design/2026-04-20-v3.4-prisma-trAIce-raise-readcheck-design.md.shared/prisma_trAIce_protocol.md— verbatim 17-item snapshot fromcqh4046/PRISMA-trAIce(2025-12-10) + per-item ARS check procedure + 4-tier behaviour table. Citation: Holst et al. 2025, JMIR AI, doi:10.2196/80247.shared/raise_framework.md— 4 principles (human oversight / transparency / reproducibility / fit-for-purpose) + 8-role matrix + mandatory scope disclaimer. Citation: Thomas et al. 2025, NIHR ESG Best Practice Working Group, 17 July 2025.shared/compliance_checkpoint_protocol.md— Stage 2.5 / 4.5 dual-gate behaviour spec, decision precedence, override ladder, fail-loop integration, boundary behaviour for non-pipeline invocation.shared/compliance_report.schema.json— Schema 12 validator (Draft 2020-12).examples/compliance/fixture_sr_full_compliant.yaml,fixture_sr_missing_M4.yaml,fixture_primary_raise_weak.yaml— regression fixtures + user reference templates.scripts/check_compliance_report.py+ tests — Schema 12 CLI validator.scripts/validate_compliance_fixtures.py+ tests — YAML→JSON fixture loop used by CI.scripts/check_prisma_trAIce_freshness.py+ tests — non-blocking upstream-drift warning (180-day threshold)..github/workflows/freshness-check.yml— weekly cron (Monday 09:00 UTC) + path-filtered push trigger for freshness check.docs/PERFORMANCE.md+.zh-TW.md: new "Long-running session management" section + v3.4.0 token-cost deltas.
Changed
shared/handoff_schemas.md: Schema 12 pointer + Material Passportcompliance_history[](append-only audit trail).academic-pipeline/SKILL.md(v3.2.2 → v3.3.0): Stage 2.5 / 4.5 extended with compliance payload; checkpoint dashboard gains compliance row.deep-research/SKILL.md(v2.8.1 → v2.9.0):systematic-reviewmode now triggerscompliance_agentat both gates.academic-paper/SKILL.md(v3.0.2 → v3.1.0):fullmode adds pre-finalize RAISE principles-only check (warn-only).disclosuremode unchanged and complementary..github/workflows/spec-consistency.yml: added compliance validator + unit test runner steps.scripts/check_spec_consistency.py: version pins bumped.README.md,README.zh-TW.md,.claude/CLAUDE.md,MODE_REGISTRY.md: suite version → 3.4.0.
Notes
- Calibration philosophy: compliance_agent ships with transparent reporting, no hard FNR/FPR threshold. This is self-consistent with ARS's v3.3.2
task_type: open-endedtruth-in-advertising annotation — publishing a hard gate would contradict the "not a benchmark task" declaration. - Compliance Mandatory failures in SR mode are blocking, but the 3-round override ladder preserves human-in-the-loop authority. Overrides auto-inject
disclosure_addenduminto the final manuscript — no detection evasion. - The v3.2 Failure Mode Checklist and the v3.4.0 compliance agent run in parallel at the same gates. Their scopes are non-overlapping: failure-mode checks research validity; compliance checks reporting transparency.
- Internal numbering: compliance_report is Schema 12 (not 10). Schema 10 is Style Profile (v2.7+); Schema 11 is R&R Traceability Matrix. The plan's initial Schema 10 assignment was corrected mid-branch before Task 9.
[3.3.6] - 2026-04-15
Added
docs/ARCHITECTURE.md— single source of truth for pipeline structure (flow, stage × dimension matrix, data-access flow, skill dependency graph, quality gates, modes). Merged into main via PR #18.docs/SETUP.md+docs/SETUP.zh-TW.md— prerequisites, API keys, Pandoc / tectonic setup, cross-model verification (ARS_CROSS_MODEL), and four installation methods.docs/PERFORMANCE.md+docs/PERFORMANCE.zh-TW.md— per-mode token budgets, full-pipeline cost estimate, and recommended Claude Code settings (Agent Team, Ralph Loop, Skip Permissions).
Changed
README.mdandREADME.zh-TW.mdstreamlined: removed the ASCII pipeline diagram and the 16-point key-feature list (superseded bydocs/ARCHITECTURE.md). Setup, performance, and installation sections relocated todocs/. Skill Details now anchors version numbers and routes readers to ARCHITECTURE.md §3 for per-agent rosters.scripts/check_spec_consistency.py— bumped README version expectations tov3.3.6; DOCX contract expectations (both EN and zh-TW) moved from READMEs to the newdocs/SETUP.*docs; addedcheck_setup_docs()step.- Suite version bumped to
3.3.6acrossREADME.md,README.zh-TW.md,.claude/CLAUDE.md, andMODE_REGISTRY.md.
Notes
- No functional change to any skill. Pure documentation reorganization.
[3.3.5] - 2026-04-15
Added
shared/benchmark_report.schema.json— JSON Schema (draft-2020-12) defining required fields for ARS benchmark reports. Catches the "n=2 author-conducted baseline" failure mode from Anthropic's automated-w2s-researcher paper.shared/benchmark_report_pattern.md— narrative hub doc explaining the schema.scripts/check_benchmark_report.py+ tests — validator with self-scored and small-sample warnings.examples/benchmark_report_template.json— fillable template.repro_lockoptional sub-block added to Material Passport (Schema 9 inshared/handoff_schemas.md). Configuration lockfile; NOT a deterministic replay guarantee.shared/artifact_reproducibility_pattern.md— hub doc with mandatory "not a replay guarantee" disclaimer section and requiredstochasticity_declarationfield.scripts/check_repro_lock.py+ tests — passport validator.examples/passport_with_repro_lock.yaml— example.requirements-dev.txt— formal Python dev dep manifest (pyyaml + jsonschema).
Changed
.github/workflows/spec-consistency.ymlinstalls viapip install -r requirements-dev.txtinstead of ad-hocpip install.academic-pipeline/references/reproducibility_audit.mdcross-links to new artifact-reproducibility pattern.
[3.3.4] - 2026-04-15
Fixed
- Embedded changelog sections in
README.mdandREADME.zh-TW.mdnow include the missingv3.3.3andv3.3.2summaries, so the README history matches the published releases. scripts/check_spec_consistency.pynow verifies that the README changelog summaries include the latest release entries, so future drift fails CI.
Changed
- Suite version bumped to
3.3.4across release-facing docs after the README changelog sync patch release.
[3.3.3] - 2026-04-15
Fixed
scripts/_skill_lint.pynow rejects SKILL frontmatter that is missing a closing---fence instead of silently treating the rest of the file as YAML.scripts/_skill_lint.pynow reports a readable error when frontmatter parses as valid YAML but not as a mapping object, instead of crashing withAttributeError.- Broken showcase link for the post-publication audit report corrected in both
README.mdandREADME.zh-TW.md. scripts/check_spec_consistency.pynow validates README relative Markdown links so future dead links fail CI.
Changed
- DOCX generation contract aligned across README,
academic-paper/SKILL.md,academic-paper/agents/formatter_agent.md,academic-pipeline/SKILL.md, andacademic-pipeline/agents/pipeline_orchestrator_agent.md: direct.docxoutput is Pandoc-dependent, with Markdown + conversion instructions as the fallback. - Added regression tests covering missing closing fences and non-mapping YAML frontmatter in both lint test suites.
- Suite version bumped to
3.3.3across release-facing docs;academic-paperpatch-bumped to3.0.2andacademic-pipelinepatch-bumped to3.2.2.
[3.3.2] - 2026-04-15
Added
metadata.data_access_levelfield on every top-level SKILL.md. Three-tier vocabulary (raw|redacted|verified_only) declaring what kind of data each skill may consume. Inspired by the three-tier isolation pattern in Anthropic's automated-w2s-researcher (2026).deep-research=rawacademic-paper=redactedacademic-paper-reviewer=verified_onlyacademic-pipeline=verified_only
scripts/check_data_access_level.pylint script with unit tests; wired into.github/workflows/spec-consistency.yml.- Pointer section in
shared/handoff_schemas.mddocumenting the vocabulary for future skill authors. metadata.task_typefield on every top-level SKILL.md. Two-value vocabulary (open-ended|outcome-gradable) declaring whether the task has a scalar ground-truth metric. All current ARS skills areopen-ended— the field is a truth-in-advertising signal that ARS targets domain-judgment work, not benchmark tasks.scripts/check_task_type.pylint script with 4 unit tests; wired into the same CI workflow.- Pointer section in
shared/handoff_schemas.mdfor thetask_typevocabulary. shared/ground_truth_isolation_pattern.md— narrative pattern doc explaining the three-layer model behinddata_access_levelandtask_type. Cross-references existing protocols (S2 verification, anti-leakage, integrity gates, calibration mode). Linked fromhandoff_schemas.mdandCONTRIBUTING.md.
Changed
- Per-skill
metadata.versionpatch-bumped on all 4 SKILL.md files;last_updatedrefreshed to 2026-04-15. - Suite version bumped to 3.3.2 across
README.md,README.zh-TW.md, and.claude/CLAUDE.md.
[3.3.1] - 2026-04-14
Fixed
- Public contract drift across
README.md,README.zh-TW.md,.claude/CLAUDE.md,MODE_REGISTRY.md, and the affectedSKILL.mdfiles - Cross-model wording now matches the implemented scope: integrity sample verification and independent DA critique are shipped; sixth-reviewer peer review remains planned
academic-pipelinecheckpoint docs now state that SLIM checkpoints still wait for explicit user confirmationacademic-pipelineintegrity gate docs now consistently state that Stage 2.5 and Stage 4.5 cannot be skippedacademic-paper/SKILL.mdmode-count heading andacademic-paper-reviewer/SKILL.mdVersion Info block
Added
scripts/check_spec_consistency.pyto catch mode-count, version-block, and forbidden-claim drift.github/workflows/spec-consistency.ymlto run the consistency check on pushes and pull requests
[3.3] - 2026-04-09
Added — PaperOrchestra-inspired enhancements
Integrates techniques from Song et al. (2026, arXiv:2604.05018) "PaperOrchestra: A Multi-Agent Framework for Automated AI Research Paper Writing."
- Semantic Scholar API Verification (deep-research, academic-pipeline): Tier 0 programmatic reference verification via S2 API. Title search with Levenshtein >= 0.70 matching. DOI mismatch detection for Compound Deception Pattern #5. Bibliography deduplication via S2 IDs. Graceful degradation if API unavailable.
- New file:
deep-research/references/semantic_scholar_api_protocol.md - Modified:
source_verification_agent,bibliography_agent,integrity_verification_agent
- New file:
- Anti-Leakage Protocol (academic-paper, deep-research): Knowledge Isolation Directive prioritizes session materials over LLM parametric memory for factual content. Flags
[MATERIAL GAP]for missing content instead of silently filling from memory. Reduces Mode 5/6 failure risk.- New file:
academic-paper/references/anti_leakage_protocol.md - Modified:
draft_writer_agent,report_compiler_agent
- New file:
- VLM Figure Verification (academic-paper): Optional closed-loop verification of rendered figures using vision-capable LLM. 10-point checklist covering data accuracy, APA 7.0 compliance, and visual quality. Max 2 refinement iterations.
- New file:
academic-paper/references/vlm_figure_verification.md - Modified:
visualization_agent
- New file:
- Score Trajectory Protocol (academic-pipeline): Per-dimension rubric score delta tracking across revision rounds. Detects regressions (delta < -3) and triggers mandatory checkpoint. Extends v3.2 early-stopping with dimension-level granularity.
- New file:
academic-pipeline/references/score_trajectory_protocol.md - Modified:
integrity_review_protocol.md,handoff_schemas.md(Schema 5)
- New file:
- Stage 2 Parallelization Directive (academic-pipeline): Visualization and argument building can run in parallel after outline completion.
- Handoff Schema Updates (shared):
semantic_scholar_idfield added to Bibliography source object.score_trajectorystructure added to Integrity Report schema.
Version bumps: deep-research v2.8, academic-paper v3.0, academic-pipeline v3.2
[3.2] - 2026-04-09
Added — Lu 2026 integration
Integrates insights from Lu et al. (2026, Nature 651:914-919) — the first end-to-end autonomous AI research system to pass blind peer review.
- AI Research Failure Mode Checklist (academic-pipeline): 7-mode taxonomy extending the existing 5-type citation hallucination taxonomy. Covers implementation-bug blindness, hallucinated experimental results, shortcut reliance, bug-as-insight, methodology fabrication, and pipeline-level frame-lock. Runs at Stage 2.5 and 4.5 with mandatory blocking behaviour. Reported at Stage 6 in the Failure Mode Audit Log subsection of the AI Self-Reflection Report.
- New file:
academic-pipeline/references/ai_research_failure_modes.md
- New file:
- Reviewer Calibration Mode (academic-paper-reviewer v1.8): opt-in mode that measures FNR / FPR / balanced accuracy / AUC against a user-supplied gold-standard set of 5-20 papers. Uses 5x ensembling with fresh context per run. Cross-model verification default-on. Session-scoped confidence disclosure.
- New file:
academic-paper-reviewer/references/calibration_mode_protocol.md
- New file:
- Disclosure Mode (academic-paper v2.9): venue-specific AI-usage disclosure statement generator. v1 database covers ICLR, NeurIPS, Nature, Science, ACL, EMNLP. Unknown venues halt and prompt user to paste policy.
- New files:
academic-paper/references/disclosure_mode_protocol.md,academic-paper/references/venue_disclosure_policies.md
- New files:
- Fidelity-Originality Mode Spectrum (all skills): classifies all modes on a fidelity–originality axis per Lu 2026 Fig 1c. Quick Mode Selection Guides updated with Spectrum column.
- New file:
shared/mode_spectrum.md
- New file:
- Early-Stopping Criterion (academic-pipeline v3.1): convergence check (delta < 3 points + no P0) suggests stopping revision loop. Budget transparency estimate at pipeline start.
- README Positioning Update: "Why human-in-the-loop, not full automation?" section citing Lu 2026 as external evidence for ARS's design thesis. Both EN and zh-TW updated.
Changed
.claude/CLAUDE.md: synced all skill versions and mode lists to reality (deep-research v2.7, academic-paper v2.9, academic-paper-reviewer v1.8, academic-pipeline v3.1)quality_rubrics.md: added "Known error profile" preamble explaining rubric scores are ordinally but not cardinally interpretable without calibration
Version bumps: academic-paper v2.9, academic-paper-reviewer v1.8, academic-pipeline v3.1
[3.1.1] - 2026-04-09
Added
- Information Systems — Senior Scholars' Basket of 11 (extending the Basket of 8 added in v2.9): Decision Support Systems, Information & Management, Information and Organization — completing the AIS College of Senior Scholars' official list of premier IS journals
- Section heading updated from "Information Systems (Basket of 8)" to "Information Systems (Senior Scholars' Basket of 11)" in
academic-paper-reviewer/references/top_journals_by_field.md - Original IS Basket of 8 proposed and drafted by @mchesbro1 — Issue #5. Extended to Basket of 11 by @cloudenochcsis — Issue #7, PR #8. Source: AIS Senior Scholars' List of Premier Journals
[2.9.1] - 2026-04-03
Added
statusandrelated_skillsmetadata to all 4 SKILL.md frontmatters- Enables skill discovery tools and cross-skill navigation for users with multiple skills installed
deep-research↔academic-paper↔academic-paper-reviewer↔academic-pipeline
[2.9] - 2026-03-27
Added
- Style Calibration — learn the author's writing voice from past papers (optional, intake Step 10)
- Writing Quality Check — checklist catching overused AI-typical patterns (renamed from AI Writing Lint)
- Information Systems Basket of 8 journals added to academic-paper reference list
- Copilot philosophy tagline to README EN + zh-TW
- Substack guide articles to both READMEs
Fixed
- Skill Details section version numbers and agent descriptions updated
- /simplify review — stale refs, lint sweep efficiency, schema fields
- Removed last v4.0 reference in CHANGELOG
[2.8] - 2026-03-22
Added
- SCR Loop Phase 1 — State-Challenge-Reflect mechanism integrated into Socratic Mentor Agent
- Commitment gates at layer/chapter transitions (collect user predictions before presenting evidence)
- Certainty-triggered contradiction (probes high-confidence statements with counterpoints)
- Adaptive intensity (tracks commitment accuracy, adjusts challenge frequency)
- Self-calibration signal (S5) for convergence detection
- SCR Switch — users can disable/re-enable predictions mid-dialogue
deep-research/agents/socratic_mentor_agent.md— SCR Protocol section with commitment gates, divergence reveal, and adaptive intensitydeep-research/references/socratic_questioning_framework.md— SCR Overlay Protocol mapping SCR phases to Socratic functionsacademic-paper/agents/socratic_mentor_agent.md— Chapter-level SCR Protocol with per-chapter commitment questions and cross-chapter pattern tracking
[2.7.3] - 2026-03-10
Fixed
- Version badge corrected in both EN and zh-TW READMEs
[2.7.2] - 2026-03-10
Added
- Version, license, and sponsor badges to README
- zh-TW README badges
[2.7.1] - 2026-03-10
Fixed
- Buy Me a Coffee username corrected
[2.7] - 2026-03-09
Added
- Integrity Verification v2.0: Anti-Hallucination Overhaul
- Full academic research skills suite (4 skills, 116 files)
- Deep Research v2.3 — 13-agent research team with 7 modes
- Academic Paper v2.4 — 12-agent paper writing with LaTeX hardening
- Academic Paper Reviewer v1.4 — Multi-perspective peer review with quality rubrics
- Academic Pipeline v2.6 — 10-stage orchestrator with integrity verification