Status (2026-05-05 banner): DESIGN-ONLY, NOT YET IMPLEMENTED. This spec occupies ROADMAP §3.6.8 ("Trust provenance & drift transparency"). Filename and roadmap slot retain v3.6.8.
Release semver:
v3.7.1(settled 2026-05-07 round-1 codex F-006 amend; supersedes initial Q7v3.6.9). Reason: main shipped v3.7.0 (plugin packaging) on 2026-05-05; tagging v3.6.9 after v3.7.0 would mean a maintenance-line backport, not a forward release. Trust-chain protections D1–D5 are forward features on the v3.7 line, so v3.7.1 is the correct semver. Roadmap slot staysv3.6.8and filename unchanged per the roadmap-slot-vs-release-semver decoupling pattern (precedent:2026-04-22-ars-v3.7.3-reading-check-probe-design.mdfilename retains roadmap slot v3.7.3 while actual release tag is v3.5.1).The substance of this spec — five upstream-trust-chain protections (D1–D5) — is unchanged from the 2026-04-30 draft. Sequencing per ROADMAP §3.6.8: v3.6.7 follow-up shipped 2026-05-05 (Step 6 + Step 8 = Phase 6.8 complete on main
a3b95fd, PR #67), so v3.6.8 sequencing constraint is now CLEARED.
Brainstorm Decision Record (2026-05-07, 8-round Q&A — supersedes §9 Open Questions where overlapping):
# Decision Spec sections affected Cascade note Q1 Ship D1–D5 as one release (no D1+D2 first / no per-pattern split) §4 (all 6 steps stay in one release), §5 acceptance criteria None Q2 human_read_source: trueset via explicit user command (e.g./ars-mark-read <refcode>); no inference, no session-activity heuristic. Round-1 codex F-005 amend (2026-05-07): command writes to a peer file<session>_human_read_log.yaml(or session-stable user-owned location), NOT intoliterature_corpus[]schema (which is adapter-owned withadditionalProperties: falseand forbids consumer-agent mutation peracademic-pipeline/references/literature_corpus_consumers.md). Orchestrator merges peer file at frontmatter-read time.§3.1 frontmatter (clarify human_read_sourceis derived, not a literal field on the entry), §3.3 LOW WARN row, NEW §3.6 Human-Read Signal ProtocolAdds new subsection; closes §9 Q4 Q3 Drift Log = peer file <session>_drift_log.md. Schema 9 untouched in v3.6.9§3.4 + §9 Q1 settled (no Schema 9 amendment in this release) None Q4 Superseded by Q8 below (originally "every emission" — replaced by per-stage hybrid) §3.3 control flow, §9 Q3 See Q8 Q5 D2 Scope Report = additive append to v3.6.7 codex_audit_multifile_template.md, not rewrite. Preserves v3.6.7's 18-codex-round invariants.§3.2 + §4 Step 2 Acceptance must include regression: v3.6.7 check_v3_6_7_pattern_protection.pylint + 25 INV mutation tests still PASS on modified template. Add §3.7 cross-reference table covering v3.6.7 §3.7 invariant family A-F overlap auditQ6 D5 retrieval fallback chain = default ON, no env flag. Backward compat via early-exit on first content-match success (Risk 4 mitigation already specified). Round-1 codex F-007 amend (2026-05-07): default-ON applies to mechanical fallback steps only (publisher_direct, wayback_cdx_search, author_personal_website, scholar_all_versions, oa_aggregators). researchgate_requestis human-handoff (not in the auto-chain): on chain exhaustion before RG, retrieval emits apending_user_action: researchgate_request_<refcode>log entry; user manually requests via author RG profile and resumes.§3.5 (split chain into 5 auto + 1 human-handoff), §8 Risk 4 (add explicit "no opt-out flag" rationale + RG human-handoff exception), §9 Q2 settled None Q7 Release tag = v3.7.1(round-1 codex F-006 amend). Roadmap slot staysv3.6.8.This banner + filename unchanged. Cross-doc sweep: any reference to "v3.6.8" as ship tag → "v3.7.1 (roadmap slot v3.6.8)" All ## v3.6.xheaders in CHANGELOG/README/.claude/CLAUDE.mdfor the trust-chain release become## v3.7.1. Plugin manifest version bumps to 3.7.1Q8 D3 Cite-Time Provenance Check = hybrid: (a) downstream agents ( synthesis_agent,draft_writer_agent,report_compiler_agent) emit author-year citation followed by hidden refcode commentSmith (2024) <!--ref:smith2024-->at every citation emission without reading frontmatter (agents stay in-prompt-context, never reference orchestrator/stage gate, never resolve markers — strict partial-inversion discipline per round-1 codex F-004); (b) finalizer reads frontmatter, applies §3.3 4-cell matrix, replaces hidden comment-marker with one of:<!--ref:slug ok-->(no warning),<!--ref:slug LOW-WARN-->(adds to per-section pre-finalization checklist),[UNVERIFIED CITATION — AI HAS NOT CROSS-CHECKED]<!--ref:slug-->(MED WARN),[UNVERIFIED CITATION — NO ORIGINAL]<!--ref:slug-->(HIGH WARN); (c)formatter_agenthard-gates on any unresolved<!--ref:comment whose status is notokor LOW-WARN-acknowledged, AND on any[UNVERIFIED CITATIONmarker. Round-1 codex F-001 amend (2026-05-07): finalizer location is mode-dependent —academic-pipelinemode usespipeline_orchestrator_agentat stage transition (architecturally mirrors v3.6.7 Step 6 audit_artifact gate); standaloneacademic-paperanddeep-researchmodes use inline finalize-on-deliverable (a per-skillcite_provenance_finalizer_agentinvoked at deliverable emission boundary, not on every cite emission). Both paths converge on the same matrix output; the standalone path is documented in §3.3 with mode-dependence table. Round-1 codex F-002 amend (2026-05-07): target agent isformatter_agent(the actual repo path), notformat_converter_agent(which does not exist).§3.3 control flow rewrite (3 sub-flows: pipeline mode, standalone academic-paper, standalone deep-research) + §4 Step 3 split into 3a agent placeholder spec / 3b orchestrator finalize spec (pipeline) / 3c standalone finalize spec / 3d formatter hard-gate spec Q4 ("every emission") superseded — placeholder is per-emission, finalization is per-stage (pipeline) or per-deliverable-emission (standalone). UX trade-off: user does not see resolved markers in-session until finalize fires; mitigated by finalize firing at every checkpoint (pipeline) or at deliverable boundary (standalone) Where to next: This banner records contract-level decisions. The spec body (§3-§9) has not yet been edited to match. The intended workflow is to run codex review round-1 against this banner + unedited body and let the cross-model audit surface every drift, cascade, and §9 Open Question that the 8-round Q&A has now closed or shifted. Memory:
feedback_codex_iterative_spec_review_to_zero.mddiscipline (run rounds until 0 finding before impl).
Round-1 Codex Amend (2026-05-07, gpt-5.5 xhigh, 7 P1 + 11 P2 + 3 P3):
Round-1 was run against the brainstorm-amended banner + unedited body. Three architectural P1 findings (F-001 / F-003 / F-006) re-opened brainstorm; their decisions are folded into the Q-table above (Q2 / Q6 / Q7 / Q8 amend cells). The remaining 4 P1 findings are surgical:
F-id Finding Resolution (folded into surgical edits, not re-brainstormed) F-001 Q8 hybrid breaks in standalone modes (no pipeline_orchestrator_agent)Q8 amend: pipeline mode uses orchestrator-at-stage-gate; standalone modes use inline finalize-on-deliverable via per-skill cite_provenance_finalizer_agentF-002 Hard-gate target format_converter_agentdoes not exist; actual repo path isformatter_agentSpec body sweep: replace format_converter_agent→formatter_agenteverywhere (§3.3, §4 Step 3, §5 acceptance criteria)F-003 Spec assumes [refcode]emissions butdraft_writer_agentemitsSmith (2024)author-yearQ8 amend: agent emits two-layer citation Smith (2024) <!--ref:slug-->(visible author-year + hidden refcode HTML comment); finalizer scans hidden comment, formatter strips on renderF-004 Q8 risks re-opening v3.6.7 fake-audit prompt leak (agents knowing about orchestrator finalize) Q8 amend: agents must "emit placeholder, never read frontmatter, never mention orchestrator/stage gate, never resolve/remove markers." v3.6.9-specific lint manifest (NOT widening v3.6.7 frozen _v3_6_7_inversion_manifest.jsonscope perfeedback_codex_iterative_spec_review_to_zero.mdlessons learned). Add newscripts/check_v3_6_8_pattern_protection.pyF-005 human_read_sourcehas no safe write location (literature_corpus[]isadditionalProperties: falseadapter-owned)Q2 amend: /ars-mark-read <refcode>writes to peer file<session>_human_read_log.yaml; orchestrator merges at frontmatter-read time.human_read_sourceis derived not stored on entryF-006 v3.6.9 tag is maintenance-line backport given main is at v3.7.0 Q7 amend: release tag = v3.7.1(forward release on v3.7 line); filename / roadmap slot stays v3.6.8F-007 ResearchGate Request Full-Text in default-ON auto-chain creates unsafe automated outreach Q6 amend: 5 auto-chain steps + RG as human-handoff (pending_user_action log) P2 (11) and P3 (3) findings: documented in PR #74 round-1 codex review output (full transcript retained in maintainer's local session log; key cascades summarized below). All are cascade or doc-sweep findings addressed by surgical edits to §3.1 / §3.2 / §3.3 / §3.5 / new §3.6 / new §3.7 / §4 Step 0–6 / §5 acceptance / §6 / §7 / §8 / §9 in this same PR (not deferred). Round-2 codex review will run after surgical edits to confirm 0-finding convergence before implementation.
Status: Design — spec input from real-world chapter-writing session, brainstorm-record-amended 2026-05-07, round-1 codex amend 2026-05-07 Date: 2026-04-30 (substance) / 2026-05-07 (brainstorm + round-1 codex decisions) Target release: v3.7.1 (settled 2026-05-07 round-1 codex F-006 amend; roadmap slot v3.6.8) Scope: Pipeline-wide hallucination defense at the trust-claim layer (frontmatter granularity, audit scope disclosure) and session transparency layer (drift logging, cite-time verification check, retrieval fallback). Complements v3.6.7 downstream-agent pattern protection by addressing the upstream trust-chain between user, source artifacts, and AI-produced descriptions.
A 2026-04-30 production session (Phase 1+2+3 outputs already at V2_CLEAN per v3.6.5/v3.6.6 standard) revealed a class of silent over-claim that the downstream-agent pattern protection layer does not catch.
The user, an experienced QA/AI-writing practitioner, repeatedly left the pipeline phase to manually verify entries against original PDFs. This was not user-flow noise; it was rational defensive behavior in response to LLM hallucination risk. Each manual return surfaced new findings:
bibliography_v2.md itself contained a fabricated quote attributed to D14 (OECD/UNESCO 2005) that does not appear in the retrieved PDFThree observations follow:
Observation 1 — Derivative pollution. The bibliography file (a derivative artifact AI produced earlier) was treated as ground truth in downstream entries. Each entry's frontmatter carried a verified: <date> field that conflated three distinct trust strengths: (a) AI viewed the entry .md, (b) AI cross-checked entry .md against the derivative bibliography, (c) AI cross-checked entry .md against the original publication PDF. To the user, verified: 2026-04-30 reads as "this source is safe to cite." The conflation is a silent over-claim that scales hallucination risk into chapter prose.
Observation 2 — Audit scope blind spot. Three rounds of cross-model codex audit reported "ADDRESSED" status on the entry corpus. Coverage analysis after the fact showed the audit could only verify against original PDF for ~22 of ~53 entries (those with retrieved sources). The remaining ~31 entries were "verified" only against the derivative bibliography — i.e., the audit confirmed internal consistency of two same-source artifacts, not correctness against original publications. The audit pipeline did not disclose this scope coverage gap, and the user almost moved into chapter prose under the false confidence that the corpus was uniformly verified.
Observation 3 — Phase drift as healthy signal. The user's repeated phase departures (chapter drafting → retrieval → metadata audit → intake) were not pipeline-discipline failures. They were the only mechanism that surfaced the v2 fabrications, the wrong case lists, and the missing statutory anchor. A pipeline design that nudges the user to "stay on track" would have suppressed the very behavior that caught the hallucinations. The intervention surface should be transparency (let the user reconstruct their drift trajectory after the session) rather than enforcement (interrupting drift mid-session).
The lesson is that ARS pipeline ship-quality is bounded not only by the weakest downstream agent but also by the granularity of trust claims (frontmatter), the honesty of audit reports (scope disclosure), and the user's ability to audit their own session trajectory (drift transparency). v3.6.7 hardened agents against producing new hallucinations during their own work; v3.6.8 hardens the trust-chain layer that decides whether prior content is safe to consume downstream.
Codify five upstream-trust-chain protections into ARS spec, with prompt-level integration into bibliography_agent and orchestrator layers, plus tooling for retrieval pipeline robustness. Target outcome: end-of-pipeline deliverables carry an auditable trust chain from final claim back to either (a) original-source verification or (b) explicit unverified-flag, with no verified claim stronger than the underlying evidence supports.
A bibliography entry carries verified: <date> (or equivalent single field) that does not distinguish between (a) entry-self-consistency, (b) entry-vs-derivative-bibliography consistency, (c) entry-vs-original-source verification. Downstream agents and human readers treat all three as equivalent confidence.
Mechanism: bibliography_agent and downstream entry writers emit a single verified field after performing whichever check is available. When original source is not retrieved, the field is still emitted, indicating only (a) or (b) but reading as (c).
Protection: Frontmatter schema must split into orthogonal fields. Two storage layers: (a) the existing literature_corpus_entry.schema.json carries the seven adapter-owned / AI-owned fields below; (b) the new peer file <session>_human_read_log.yaml (per §3.6) carries user-owned human_read_source markers as a session-scoped derivative that the finalizer joins at frontmatter-read time. This split honors the v3.6.5 Iron Rule "no consumer-side corpus mutation" (literature_corpus[] stays adapter-owned with additionalProperties: false) while still surfacing the human_read_source: true | false semantic at finalizer / audit / cite-time matrix lookup.
# Stored on the entry (literature_corpus_entry.schema.json — adapter or AI writes):
source_acquired: true | false # Original PDF/HTML in repo
source_acquisition_date: <ISO 8601> # When acquired
source_acquisition_path: <relative path># Where it lives
source_verified_against_original: true | false # AI cross-checked against original
source_verification_method: codex_audit | manual_grep | vision_check | none
description_source: original_pdf | bibliography_v<n> | secondary_summary
description_last_audit: <round_id> | none
# Derived at read-time from peer file <session>_human_read_log.yaml (§3.6):
human_read_source: true | false # USER explicitly ran /ars-mark-read <slug>
human_read_at: <ISO 8601> | null # When the mark-read command fired
Three firm rules (per round-1 codex F-005 amend, three rules total):
- source_verified_against_original: true REQUIRES source_acquired: true AND source_verification_method ∈ {codex_audit, manual_grep, vision_check} (round-2 R2-007 amend: none is enumerated but is forbidden in conjunction with source_verified_against_original: true; the rule "non-empty" was insufficient because none is a non-empty string. Lint must enforce the affirmative-method enumeration, not just non-emptiness.)
- source_acquired: false REQUIRES description_last_audit: none (no original means audit cannot be substantive). Lint must enforce.
- human_read_source is NEVER stored on the entry — adapters and consumer agents MUST NOT emit it as part of the entry. The field is computed at read-time by joining <session>_human_read_log.yaml against the entry's citation_key. Lint must reject entries that ship a literal human_read_source field. (Rationale: literature_corpus_entry.schema.json is additionalProperties: false and adapter-owned per academic-pipeline/references/literature_corpus_consumers.md; mutating it from a consumer would break the v3.6.5 corpus-consumer protocol.)
bibliography_agent prompt must specify these fields with examples, plus a refusal rule: when source is not retrieved, agent MUST NOT emit source_verified_against_original: true regardless of internal-consistency checks performed. The agent must also NOT emit human_read_source — that field is set only by the user-issued /ars-mark-read command (see §3.6).
A cross-model audit (codex round) reports "PASSED" or "ADDRESSED" without disclosing how many corpus entries were independently verifiable (had retrieved source) versus how many were only self-consistency-checked (no retrieved source, audit fell back to derivative comparison).
Mechanism: Audit reports are written as global pass/fail summaries. Coverage breakdown is computable from frontmatter (count entries with source_acquired: true vs false) but is not surfaced in the audit report header.
Protection: Audit report template (in shared/templates/codex_audit_multifile_template.md and any subsequent codex audit prompt) must require a Section 0 Scope Report block. Per round-1 codex Q5 amend (round-2 R2-008 polish), the Scope Report is added as an additive prepend to the v3.6.7 template, NOT a rewrite: it precedes the existing Sections 1–7 as a new Section 0 without renumbering them. The 7 existing v3.6.7 audit dimensions and the §3 "Patterns surfaced" Section 4(f) check stay byte-equivalent in title, ordinal label, and order. See §3.7 for the v3.6.7 invariant overlap audit confirming this preserves INV-1 / INV-2 / INV-3 (check_v3_6_7_pattern_protection.py) plus families A–F (check_audit_artifact_consistency.py).
## Codex Audit Round N — Scope Report
**Total entries audited:** <N_total>
**Entries with retrieved original source:** <N_with_source> (verified against original publication)
**Entries description-only (no retrieved source):** <N_without_source> (verified only against derivative bibliography or self-consistency)
**Audit scope warning:** <N_without_source> entries cannot be independently verified by this audit round. Their `verified` status reflects internal consistency between entry .md and the derivative bibliography source, NOT correctness against the original publication. These entries should be treated as **unverifiable until original sources are retrieved**.
**Affected refcodes (description-only):** <comma-separated list>
Two firm rules:
- The Scope Report block must appear before any pass/fail summary
- Aggregate "PASSED" status MUST be split into:
- verified-against-source: PASS | FAIL (over the retrieved-source subset)
- description-internally-consistent: PASS | FAIL (over the non-retrieved subset)
- unaudited-due-to-missing-source: <count> (always reported, never hidden)
The combined-aggregate "PASSED" verb is forbidden in audit summary.
When narrative-construction agents (synthesis_agent, draft_writer_agent, report_compiler_agent) emit a citation, no automated provenance check fires against the cited entry's frontmatter. The agent assumes the entry exists and is citable, and produces declarative claims about its content.
Mechanism: Agent prompts treat the bibliography as an opaque context. When a citation is emitted, no downstream layer intercepts to verify trust-chain status of the underlying entry.
Protection (3-step pipeline; mode-dependent finalizer location):
Step 1 — Agent emits two-layer citation (uniform across all modes). Downstream agents emit citations as <author-year visible form> immediately followed by a hidden HTML comment <!--ref:<slug>-->, where <slug> is the citation_key from the frontmatter (e.g. Smith (2024) <!--ref:smith2024--> or (Smith, 2024)<!--ref:smith2024-->). Agents emit this two-layer form at every citation emission without reading the frontmatter — they only know the slug from the corpus context already in their prompt. Agents must NOT mention the finalizer, the orchestrator, or stage gates (strict partial-inversion discipline per round-1 codex F-004; v3.6.7 Pattern C3 attack-surface stays closed). The hidden comment is invisible in markdown rendering but mechanically extractable.
Step 2 — Finalizer applies the 4-cell matrix and resolves the hidden comment (mode-dependent location):
| Mode | Finalizer agent | Trigger boundary | Hard-gate agent |
|---|---|---|---|
academic-pipeline |
pipeline_orchestrator_agent |
Stage transition (mirrors v3.6.7 Step 6 audit_artifact gate) | formatter_agent (academic-paper terminal) |
academic-paper standalone |
cite_provenance_finalizer_agent (new, per-skill) |
After draft_writer_agent deliverable emission, before formatter_agent |
formatter_agent |
deep-research standalone |
cite_provenance_finalizer_agent (new, per-skill) |
After report_compiler_agent deliverable emission, before terminal hard-gate (round-2 R2-001 amend: deep-research has no formatter_agent; report_compiler_agent is its compilation step, so finalizer must run AFTER compilation, not before) |
cite_provenance_terminal_gate_agent (new, deep-research per-skill; round-3 R3-001 amend): a separate single-purpose agent that runs AFTER cite_provenance_finalizer_agent, reads the resolved draft, and refuses to emit the final report if any [UNVERIFIED CITATION ...] or unresolved <!--ref:slug--> (status neither ok nor LOW-WARN-acknowledged) remains. NOT a re-invocation of report_compiler_agent (that would require two-pass dispatch and conflict with the v3.6.7 partial-inversion discipline; report_compiler_agent must NEVER read frontmatter or resolve markers, including a hypothetical second pass). The terminal-gate agent never compiles; it only inspects + emits/refuses. |
The finalizer reads <!--ref:<slug>--> comments, dereferences <slug> against frontmatter (entry from literature_corpus[] joined with peer file <session>_human_read_log.yaml from §3.6 — see Q2 amend), and applies this matrix to mutate the comment in place:
source_acquired |
source_verified_against_original |
human_read_source |
Resolution |
|---|---|---|---|
| false | — | — | HIGH WARN: cite has no original source on file. Replace <!--ref:slug--> with [UNVERIFIED CITATION — NO ORIGINAL]<!--ref:slug--> |
| true | false | — | MED WARN: PDF in repo but AI has not cross-checked (regardless of human_read_source; AI verification is the gating condition for MED — Step 3b R3 codex P1 closure made the matrix total over the 8 input combinations). Replace with [UNVERIFIED CITATION — AI HAS NOT CROSS-CHECKED]<!--ref:slug--> |
| true | true | false | LOW WARN: AI cross-checked, user has not. Replace with <!--ref:slug LOW-WARN-->; also append slug to per-section pre-finalization checklist artifact |
| true | true | true | OK, replace with <!--ref:slug ok--> |
Each finalizer pass is idempotent on its own resolved markers (re-running on already-resolved <!--ref:slug ok--> is a no-op). On revision loops (Stage 4 → reviewer → Stage 4 revise; or academic-paper Phase 6 → Phase 4 loops), the finalizer re-runs against the current draft and re-resolves any newly-emitted bare <!--ref:slug--> comments; resolved markers do not invalidate.
Step 3 — Terminal hard-gate at output boundary (mode-dependent agent; per round-2 R2-001 + round-3 R3-001 amend):
academic-pipeline and standalone academic-paper: formatter_agent is the hard-gate. It MUST refuse to convert any draft that contains either (a) a literal [UNVERIFIED CITATION ...] marker, or (b) any unresolved <!--ref:slug--> HTML comment whose status is neither ok nor LOW-WARN-acknowledged.deep-research: cite_provenance_terminal_gate_agent (round-3 R3-001 amend) is the hard-gate. It runs AFTER cite_provenance_finalizer_agent and is the deep-research terminal artifact emitter. It refuses to emit the final report if any [UNVERIFIED CITATION ...] or unresolved <!--ref:slug--> (status neither ok nor LOW-WARN-acknowledged) remains. report_compiler_agent does NOT carry the hard-gate — it only compiles (per Step 3a strict-partial-inversion: never read frontmatter, never resolve markers). The dispatch order is report_compiler_agent (compile, emit two-layer placeholders) → cite_provenance_finalizer_agent (read frontmatter, resolve placeholders) → cite_provenance_terminal_gate_agent (inspect resolved draft, emit final report or refuse). Single-pass, no re-invocation of report_compiler_agent.In all modes, the user must either acquire/verify the source (which removes the upstream HIGH/MED WARN cell condition) or rewrite to remove the citation, before final output. LOW-WARN-acknowledged means the user has explicitly run /ars-mark-read <refcode> (see §3.6) for that slug, which on the next finalizer pass promotes <!--ref:slug LOW-WARN--> to <!--ref:slug ok-->.
Markers are draft-only — they render as invisible HTML comments (or [UNVERIFIED CITATION ...] visible warnings for HIGH/MED WARN) in draft prose but are stripped from final output by the terminal hard-gate agent only after the hard-gate passes (i.e. all comments are ok or LOW-WARN-acknowledged).
Consistent with v3.5 Collaboration Depth Observer precedent: warn, surface, but never silently block user choice. The conversion-time hard gate is the single non-negotiable point.
User leaves expected pipeline phase (e.g., from academic-paper:plan mode to manual retrieval to ad-hoc tool building back to academic-paper:plan). Session ends. User has no way to reconstruct what work was done in which phase vs what was ad-hoc tooling vs what was backflow verification. Future sessions and audit replay treat all session output as equivalent phase deliverable.
Mechanism: ARS session does not emit a structured phase-trajectory log. Phase information lives implicitly in agent invocation order; ad-hoc work (user manually editing files, writing scratch artifacts) is invisible to the pipeline orchestrator.
Protection: Add Session Drift Log as an end-of-session orchestrator artifact. Orchestrator emits <session>_drift_log.md containing:
## Session <session_id> drift log
**Session start phase:** <phase>
**Session end phase:** <phase>
**Pipeline mode:** <mode>
### Phase trajectory
- HH:MM <action> (in-phase | backflow-to-<phase> | crossphase-jump-to-<phase> | ad-hoc)
- HH:MM ...
### Session deliverables
**Phase-standard artifacts produced:**
- <filename> (phase: <phase>, agent: <agent>)
**Ad-hoc artifacts produced (NOT phase-standard):**
- <filename> (rationale recorded by user: <text> | inferred: <text>)
**Sources newly verified-against-original:**
- <refcode list>
**Sources still unverified-against-original:**
- <refcode list>
### Phase status diff (from session start to session end)
- <artifact>: <before> → <after>
### Recommended next-session work (dependency-ordered)
- <bullet list>
The Drift Log is not enforcement. It is a transparency artifact: the user reads it after the session to reconstruct what happened, decide whether ad-hoc artifacts should be promoted to phase-standard, and plan the next session.
Two firm rules:
- Backflow events are NOT failures. The Drift Log uses neutral language (backflow-to-<phase>), never deviation / out-of-spec / error.
- Ad-hoc artifacts must be enumerated, not hidden. If bibliography_agent did not emit a particular file, the file is ad-hoc; this is fine but must be visible to the user.
retrieval_agent (or upstream bibliography_agent retrieval stage) attempts a single retrieval channel (publisher direct via DOI). On failure (paywall, WAF, 403, redirect to login) the entry is marked retrieval_failed and the corpus moves on. Legitimate alternative channels — Wayback Machine CDX API, author personal websites, Google Scholar all-versions, OA aggregators (Unpaywall, OpenAlex, CORE) — are not attempted.
Mechanism: Retrieval logic is implemented as a single attempt rather than a fallback chain. False negatives are common: the 2026-04-30 session recovered F3 (Yale Daily News, Vercel-WAF-blocked) via Wayback CDX search and E1 (Harvey & Stensaker 2008, Wiley paywall) via author's institutional repository — both legitimate, both invisible to single-channel retrieval.
A second failure mode in single-channel retrieval: success-criterion is too weak. A 200 OK with Content-Type: application/pdf does NOT prove the retrieved file is the requested paper. The 2026-04-30 session retrieved an 8 MB PDF from peraturan.bpk.go.id with valid PDF header; first page revealed it was a Banjarmasin city tax regulation, not the requested Permendikbudristek 53/2023. The wrong-record case must be caught by content-grep at retrieval time.
Protection: retrieval_agent (or equivalent retrieval stage) must implement a fallback chain with content verification:
The fallback chain has 5 mechanical auto-steps + 1 human-handoff terminal step (per round-1 codex F-007 amend; ResearchGate is human-handoff so default-ON does not mean automated outreach).
def retrieve(refcode, citation):
# 5 mechanical auto-steps; default ON, no env flag (per round-1 Q6).
auto_chain = [
publisher_direct, # DOI → publisher PDF
wayback_cdx_search, # CDX API → archived snapshot
author_personal_website, # site:<author_lastname>.<inst-tld> grep
scholar_all_versions, # Scholar's "All N versions" → [PDF]-marked
oa_aggregators, # Unpaywall / OpenAlex / CORE
]
for method in auto_chain:
result = method(citation)
if not result.acquired:
log_failure(refcode, method.name, result.failure_reason)
continue
if not verify_content_match(result.pdf, citation):
log_failure(refcode, method.name, "content_match_failed")
continue
return result
# All auto-steps exhausted. Emit human-handoff event for ResearchGate
# Request Full-Text. RG is NOT in the auto-chain because asking strangers
# for files on the user's behalf without consent is unsafe automated outreach.
log_pending_user_action(refcode, "researchgate_request", citation)
return RetrievalFailed(
refcode,
attempts=auto_chain_log,
pending_user_action="researchgate_request",
)
verify_content_match() MUST do at minimum:
- Read first page of retrieved PDF
- Grep for citation title (or significant title fragment)
- Grep for at least one citation author surname
- Reject if either fails
Three firm rules:
- Each fallback step's failure reason must be logged (paywall_403, waf_blocked, not_indexed, content_match_failed, etc.). No silent skips.
- Content-Type: application/pdf + HTTP 200 is NEVER sufficient for retrieval success. Content match is mandatory.
- researchgate_request is human-handoff, not in the auto-chain. On auto-chain exhaustion, retrieval emits a pending_user_action: researchgate_request_<refcode> log entry; the user manually requests via the author's RG profile and resumes when the file arrives. Default-ON applies to the 5 mechanical auto-steps, never to RG outreach.
The §3.3 4-cell matrix and the §3.1 frontmatter schema both reference human_read_source: true | false. This subsection defines the user-facing affordance that sets the value, and the storage / merge contract that keeps literature_corpus[] schema unmutated.
Affordance: explicit user command (no inference, no session-activity heuristic).
/ars-mark-read <citation_key> # mark a single entry as human-read
/ars-mark-read <citation_key>... # batch form (space-separated)
/ars-unmark-read <citation_key> # rescind (in case of erroneous mark)
/ars-list-read # print the current session's mark-read log
Storage: session-scoped peer file at <passport-path-parent>/<passport-stem>_human_read_log.yaml (round-5 R5-003 amend; the <session>_human_read_log.yaml shorthand used elsewhere in this spec is the passport-derived path computed per §3.6 cross-session block). NOT the entry's frontmatter. Schema:
# <session>_human_read_log.yaml
session_id: <session_id>
created_at: <ISO 8601>
human_read:
- citation_key: smith2024
marked_at: <ISO 8601>
- citation_key: oecd_unesco_2005
marked_at: <ISO 8601>
The peer file lives next to other session artifacts (Material Passport, Drift Log) and is user-owned. The orchestrator / finalizer reads it at frontmatter-read time and joins on citation_key to derive human_read_source: true.
Three firm rules:
literature_corpus[] to add human_read_source or human_read_at — those keys are derived, not stored. Lint must reject any entry that ships them literally (per §3.1 firm rule #3).<citation_key> is a hard error, not a silent miss. /ars-mark-read MUST validate that the slug exists in the active corpus before writing the log entry; on miss, emit [ARS-MARK-READ ERROR: citation_key '<slug>' not in literature_corpus[]] and refuse to write.<session>_human_read_log.yaml is append-only across the session; rescind operations write a rescinded_at field instead of deleting the original entry, so audit replay can reconstruct the user's signal trajectory.Cross-session semantics (round-2 R2-003 + round-3 R3-002 amend): v3.6.3's ARS_PASSPORT_RESET is a context-reset boundary, NOT an evidence-reset; per academic-pipeline/references/passport_as_reset_boundary.md, append-only ledger entries (audit_artifact ledger, reset_boundary ledger) survive across reset. Human-read signals are user-issued evidence and follow the same survival semantics. Concrete handoff mechanism (round-3 R3-002 amend; Schema 9 untouched per Q3):
passport_as_reset_boundary.md); it does NOT define a session artifact directory. v3.7.1 derives the read-log location from the passport path rather than introducing a new convention or env var:<passport-path-parent>/<passport-stem>_human_read_log.yaml, where <passport-stem> is the passport filename without extension. Example: passport /work/proj/.ars/passport_abc123.json → read-log /work/proj/.ars/passport_abc123_human_read_log.yaml./ars-mark-read MUST hard-fail (NOT silently create) on any of these conditions:[ARS-MARK-READ ERROR: no active passport path; run a session with passport handoff first or pass --passport-path explicitly] and refuse to write.[ARS-MARK-READ ERROR: passport file not found at <path>] and refuse.R_OK permission): emit [ARS-MARK-READ ERROR: passport parent directory <path> unreadable] and refuse.W_OK on parent): emit [ARS-MARK-READ ERROR: cannot write read-log at <path>; parent directory not writable] and refuse.realpath to follow symlinks once and detect link cycles) for *_human_read_log.yaml, enumerates all matching files including the active one, and merges them. The active session's read-log is the only APPEND target; all other matches are read-only at merge time.$ARS_SESSION_ARTIFACT_DIR is NOT introduced (round-4 R4-003 amend; preserves v3.6.3 minimal-contract discipline). The user-supplied passport path remains the single discovery anchor.Smith_human_read_log.yaml vs smith_human_read_log.yaml), the orchestrator MUST fail-fast with [ARS-MARK-READ ERROR: casefold-colliding read-log filenames at <path>] and refuse to merge. Symlinks within the parent dir are followed exactly once via realpath; circular symlinks are detected and rejected with [ARS-MARK-READ ERROR: symlink cycle in read-log discovery].citation_key appears in multiple session logs (e.g. user marked a refcode in session A, rescinded in session B), the most recent entry by marked_at / rescinded_at ISO 8601 timestamp wins. Append-only is preserved (no log file is mutated; precedence is computed at merge time by the orchestrator).ARS_PASSPORT_RESET=1 and ARS_PASSPORT_RESET=0, prior human_read entries carry forward via this filesystem discovery — reset does NOT erase them, since reset only clears in-memory context, never user-owned filesystem evidence./ars-clear-read-log — out-of-scope for v3.7.1; future enhancement) would be required to actively rescind prior signals across sessions; no automatic erasure occurs at any reset boundary.Per Q5 amend, D2's Scope Report block is added as an additive prepend (new Section 0 / Scope Report ahead of the existing Sections 1–7, without renumbering them) to shared/templates/codex_audit_multifile_template.md rather than a rewrite (round-2 R2-008 amend). The template's existing 7-section inventory stays byte-equivalent in section title, ordinal labels, and order; Scope Report is prefixed before Section 1 with its own Section-0 header.
Two distinct v3.6.7 lint surfaces apply (round-2 R2-005 amend):
scripts/check_v3_6_7_pattern_protection.py enforces line-position-sensitive prompt-side invariants on 3 manifest files (synthesis_agent.md, research_architect_agent.md, report_compiler_agent.md): INV-1 / INV-2 / INV-3.scripts/check_audit_artifact_consistency.py enforces 6 artifact-side invariant families (A–F per v3.6.7 Step 6 spec §3.7) over audit_artifact[] ledger entries and their JSONL / sidecar / verdict counterparts.These are different scripts checking different artefacts. The original §3.7 prose conflated them. The corrected mapping:
| Invariant surface | What it checks | Effect of additive Scope Report on the audit template | Preserved? |
|---|---|---|---|
INV-1 (check_v3_6_7_pattern_protection.py) |
Canonical Clause 1 line "Sub-agents have been observed..." appears exactly once in each of 3 manifest files (synthesis_agent.md, research_architect_agent.md, report_compiler_agent.md) |
Scope Report block lives in shared/templates/codex_audit_multifile_template.md, NOT in any of the 3 manifest files. Manifest scope unchanged. |
✅ |
INV-2 (check_v3_6_7_pattern_protection.py) |
No Clause 2 disclosure phrases (a)–(d) (e.g. "the orchestrator runs codex audit afterward") inside PATTERN PROTECTION blocks of the 3 manifest files | Same: Scope Report is in audit template, not in any manifest file. INV-2 scope lint scans manifest blocks only. | ✅ |
INV-3 (check_v3_6_7_pattern_protection.py) |
Canonical Clause 1 line confined to the 3 manifest files (frozen scope manifest at scripts/v3_6_7_inversion_manifest.json) |
Audit template not in manifest scope; Scope Report addition does not introduce Clause 1 text. | ✅ |
Family A (per-artifact cross-field rules A1–A6, check_audit_artifact_consistency.py) |
Constraints on audit_artifact[] Schema 9 entries, not the audit template body |
Scope Report is template prose, not Schema 9 data. | ✅ |
Family B (cross-file / Layer 3 rules B1–B10, check_audit_artifact_consistency.py) |
Constraints on audit_jsonl / audit_sidecar / audit_verdict schemas |
Scope Report does not modify these JSON schemas. | ✅ |
Family C (mirror rules C1–C4, check_audit_artifact_consistency.py) |
C3 in particular forbids "the orchestrator runs audit afterward" leaking from agent prompts. Scope Report mentions that the audit was run by the orchestrator — but it is in the audit report template, not in the agent prompt. | Reading the audit report after the fact does not give a downstream agent prompt knowledge of orchestrator behavior at draft time. | ✅ |
Family D (ordering rules D1–D4, check_audit_artifact_consistency.py) |
D1 strict-monotonic verified_at_ms etc. are Schema 9 ledger semantics |
Scope Report does not write audit_artifact[]. |
✅ |
Family E (lifecycle ownership E1–E9, check_audit_artifact_consistency.py) |
Ownership of audit JSONL/sidecar/verdict file emission | Scope Report is text inside the verdict file body; ownership unchanged. | ✅ |
Family F (naming convention F1–F4, check_audit_artifact_consistency.py) |
Run-ID format and similar | Scope Report adds no new identifiers. | ✅ |
Acceptance (round-2 R2-005 amend; corrected regression gate): Step 2 (§4) MUST include regression tests against BOTH lint surfaces:
- python scripts/check_v3_6_7_pattern_protection.py PASSES (12/12 invariants) on the modified template branch
- pytest scripts/test_check_v3_6_7_pattern_protection.py -q PASSES 58/58 mutation tests
- python scripts/check_audit_artifact_consistency.py PASSES on a synthesized audit_artifact ledger that includes a verdict file body containing the new Section 0 Scope Report block (verifies family A–F invariants are not perturbed by template prose changes)
If any of the three fails, the additive-prepend claim is broken and Q5 must be re-discussed.
Per round-1 codex F-004, v3.7.1 introduces new prompt-level invariants on the three downstream agents (synthesis_agent / draft_writer_agent / report_compiler_agent) for the two-layer citation emission discipline. Widening the v3.6.7 frozen scripts/v3_6_7_inversion_manifest.json scope would re-open the 18-codex-round v3.6.7 convergence, so v3.7.1 ships its own manifest and lint instead.
Files to create:
- scripts/v3_6_8_inversion_manifest.json — frozen scope manifest covering v3.7.1 prompt invariants. Initial entries: synthesis_agent / draft_writer_agent / report_compiler_agent for the "Two-Layer Citation Emission" prompt block invariants. Schema mirrors v3.6.7 manifest (file list + per-file invariant assertions) but is a separate JSON file and a separate EXPECTED_MANIFEST_FILES tuple in the v3.7.1 lint.
- scripts/check_v3_6_8_pattern_protection.py — see Step 3a. References v3_6_8_inversion_manifest.json, NOT the v3.6.7 manifest.
Acceptance (round-2 R2-006 + round-3 R3-003 amend; corrected boundary statement + canonical range definition):
- v3.6.7 lint regression: python scripts/check_v3_6_7_pattern_protection.py PASSES 12/12; pytest scripts/test_check_v3_6_7_pattern_protection.py -q PASSES 58/58. The boundary rule is: v3.7.1 work does NOT modify the v3.6.7-tagged PATTERN PROTECTION blocks in synthesis_agent.md / research_architect_agent.md / report_compiler_agent.md (those blocks stay byte-equivalent under v3.6.7 INV-1/2/3 enforcement). v3.7.1 MAY add new prompt sections (e.g. "Two-Layer Citation Emission") to the same files, OUTSIDE the v3.6.7 PATTERN PROTECTION fenced block.
- Canonical range definition (round-3 R3-003 + round-4 R4-001 + round-5 R5-004 amend; uses existing heading-to-next-heading extraction, no marker mutation): each protected block is extracted by the same heading-based extractor that v3.6.7 lint already uses (per scripts/check_v3_6_7_pattern_protection.py block isolation logic at line ~256) — start at the line containing ## PATTERN PROTECTION (v3.6.7) heading; end at the line before the next H1 / H2 / H3 (#, ##, or ###) heading or EOF, whichever comes first (round-5 R5-004 amend: H1/H2/H3 matches the existing extractor's actual termination set; previous wording said only H2 which would have produced a different byte range than v3.6.7 lint). The byte range is from the start of the heading line through the end of the last block line (excluding the next-heading line itself). No new BEGIN/END markers are introduced (introducing markers would mutate v3.6.7 protected blocks, contradicting the boundary rule).
- SHA check normalization rules: bytes are read raw (no LF→CRLF conversion); the file's BOM (if any) is excluded; trailing whitespace of the last block line is preserved; SHA-256 is computed over the resulting byte sequence. The extractor is the byte-equivalent function shared between v3.6.7 lint and v3.7.1 lint to guarantee identical results.
- Single source of truth (round-4 R4-002 + round-5 R5-001 + round-6 R6-002 amend; no dual stored truth, base commit derived not stored, shallow-clone safe): the v3.6.7 frozen manifest at scripts/v3_6_7_inversion_manifest.json is the single source of truth for the protected file list; the v3.6.7 content is whatever the file shows at the manifest's most recent modifying commit (derived via git log -1 --format=%H scripts/v3_6_7_inversion_manifest.json). v3.7.1 lint computes the SHA on demand at runtime by (a) reading the v3.6.7 manifest's file list, (b) extracting each file's heading-based block range at PR HEAD, (c) hashing the bytes, (d) extracting + hashing the same files at the v3.6.7 manifest's most recent modifying commit (the "v3.6.7 base commit"), (e) asserting (c) == (d). v3.7.1 manifest does NOT store expected_v3_6_7_block_sha256 values (avoiding concurrent / out-of-order PR drift). v3.6.7 manifest does NOT need a new base_commit field — git log -1 derivation makes that drift-proof.
- Shallow-clone safety (round-6 R6-002 + round-7 R7-001 amend): actions/checkout@v4 defaults to fetch-depth: 1 in CI, which would make git log -1 return only HEAD (rendering the comparison vacuous). v3.7.1 lint MUST first call git rev-parse --is-shallow-repository; if true, lint MUST either (a) call git fetch --unshallow origin <default-branch> to deepen the clone, OR (b) hard-fail with [ARS-V3.7.1 LINT ERROR: shallow clone detected; set fetch-depth: 0 in checkout step before running v3.7.1 byte-equivalence check]. Default-branch lookup order (round-7 R7-001 amend): (1) git symbolic-ref --quiet --short refs/remotes/origin/HEAD (strip the origin/ prefix); (2) GitHub Actions fallback github.event.repository.default_branch env var; (3) hard-fail with [ARS-V3.7.1 LINT ERROR: default branch unresolvable; clone must include origin/HEAD or set GITHUB_DEFAULT_BRANCH env]. Step 0 acceptance MUST also amend .github/workflows/spec-consistency.yml to add fetch-depth: 0 on the actions/checkout@v4 step that runs the v3.7.1 lint, so production CI does not rely on the unshallow fallback. The "same-run hashes against itself" fallback from round-4 prose is removed (round-5 R5-001 amend: it was vacuous for drift detection).
- v3.7.1 lint scope: confined to v3.7.1-tagged sections only; does NOT scan or mutate v3.6.7-tagged blocks. The SHA check is the only v3.7.1 lint that reads v3.6.7-tagged bytes (read-only, byte-equivalence assertion).
Files to modify:
- shared/contracts/passport/literature_corpus_entry.schema.json — add the seven entry-stored fields per §3.1 (round-1 codex F-005 amend: human_read_source and human_read_at are derived, NOT entry-stored — they live in §3.6 peer file <session>_human_read_log.yaml). Schema stays additionalProperties: false; no widening to admit user-mutable fields.
- deep-research/agents/bibliography_agent.md — add prompt section "Trust-Chain Frontmatter Discipline" specifying field semantics + the three firm rules (the third being "NEVER emit human_read_source literally; that field is user-set via §3.6 command") + refusal-on-uncertain rule
- academic-paper/agents/literature_strategist_agent.md — same prompt addition for downstream consumer
New CI lint:
- scripts/check_v3_6_8_frontmatter_trust_schema.py — validates the three firm rules across all entry .md files in a sample corpus, including the "no literal human_read_source on entry" rule
Wire into .github/workflows/spec-consistency.yml.
Acceptance:
- bibliography_agent and literature_strategist_agent carry the new prompt block with all three firm rules
- Lint enforces all three firm rules on test fixtures
- Mutation test: an entry that ships a literal human_read_source: true field is rejected by both schema validation (additionalProperties: false) AND the v3.7.1 lint (defense in depth)
- Mutation tests confirm refusal-on-uncertain behavior
Files to modify:
- shared/templates/codex_audit_multifile_template.md — add Scope Report block as opening section, before any pass/fail summary
- Per-mode codex audit prompts that reference the template
New CI lint:
- scripts/check_v3_6_8_audit_scope_block.py — checks audit report templates for required Scope Report header + forbidden combined-aggregate "PASSED" verb
Acceptance:
- All audit templates carry the Scope Report block
- Lint enforces template compliance
- Sample audit report run on the 2026-04-30 production session corpus reproduces the correct coverage breakdown (22 verifiable / 31 description-only of 53)
- v3.6.7 invariant regression (per §3.7 + Q5 amend): python scripts/check_v3_6_7_pattern_protection.py PASSES 12/12 invariants on the modified template; pytest scripts/test_check_v3_6_7_pattern_protection.py -q PASSES 58/58 mutation tests. If either fails, additive-prepend is broken; reopen Q5.
Step 3 splits into 4 sub-steps to honor the strict partial-inversion discipline (agents emit, finalizer resolves, formatter gates) and the mode-dependent finalizer location (pipeline-orchestrator vs. per-skill standalone finalizer).
Files to modify:
- deep-research/agents/synthesis_agent.md — add prompt block "Two-Layer Citation Emission" specifying <author-year> <!--ref:slug--> form, the strict partial-inversion rules (NEVER read frontmatter, NEVER mention finalizer / orchestrator / stage gate, NEVER resolve markers)
- academic-paper/agents/draft_writer_agent.md — same prompt block
- deep-research/agents/report_compiler_agent.md — same prompt block (per round-1 codex P2 finding 10: agent coverage)
New CI lint:
- scripts/check_v3_6_8_pattern_protection.py — separate v3.7.1 lint with its own roadmap-slot-namespaced manifest at scripts/v3_6_8_inversion_manifest.json (filename retains v3_6_8 slot per Q7 / spec_location_semver_decoupling). Does NOT modify the v3.6.7 frozen _v3_6_7_inversion_manifest.json scope (per round-1 codex F-004 amend; preserves 18-codex-round v3.6.7 convergence). Lint enforces (i) two-layer form regex on emitted citations in agent test fixtures, (ii) absence of resolver-layer prose inside the new "Two-Layer Citation Emission" prompt blocks (forbidden term set: finalizer / orchestrator / stage gate / terminal gate / resolver, plus plural variants and underscore-bounded substring matches like cite_provenance_finalizer_agent; broader than the spec's narrow three-term list per Step 3a R2/R3 codex closures, intentionally over-inclusive to close attack surface for Step 3c/3d agent names), (iii) absence of any frontmatter-read instruction in those blocks (variants covered: frontmatter / front matter / front-matter / front_matter; verbs read / look up / dereference / consult / open / parse / load / access / query / fetch with identifier-aware boundaries that catch function-style calls like read_frontmatter(); negation must be ≤30 chars before the verb and not separated from the verb by a sentence terminator including markdown bullet boundaries). Step 3a R4 codex review confirms 0 P0+P1 against this enforcement surface.
Acceptance:
- All 3 agents carry the two-layer citation prompt block
- v3.7.1 lint enforces strict partial-inversion (3 invariants above)
- v3.6.7 lint regression: python scripts/check_v3_6_7_pattern_protection.py PASSES (no widening of v3.6.7 manifest; v3.7.1 lint is separate)
Files to modify:
- academic-pipeline/agents/pipeline_orchestrator_agent.md — add subsection "Cite-Time Provenance Finalizer (v3.7.1)" specifying §3.3 4-cell matrix, peer-file <session>_human_read_log.yaml join semantics, idempotency on revision loops
New CI lint:
- scripts/check_v3_6_8_cite_provenance_pipeline.py — checks that pipeline_orchestrator_agent carries the finalizer subsection with all 4 matrix rows and the idempotency clause
Acceptance:
- Orchestrator subsection present
- Mutation test: removing any of the 4 matrix rows fails lint
- Revision-loop test (per round-1 codex P2 finding 11): a draft passed through finalizer twice (Stage 4 → reviewer → Stage 4 revise) produces identical resolved markers; new bare <!--ref:slug--> comments in the revised pass are resolved correctly without invalidating prior ok markers
Files to create:
- academic-paper/agents/cite_provenance_finalizer_agent.md — per-skill finalizer for academic-paper standalone mode, invoked at deliverable emission boundary AFTER draft_writer_agent finishes drafting and BEFORE the draft is handed to formatter_agent. Implements the same §3.3 4-cell matrix as Step 3b.
- deep-research/agents/cite_provenance_finalizer_agent.md — per-skill finalizer for deep-research standalone mode, invoked AFTER report_compiler_agent finishes compiling its draft (round-2 R2-001 + round-3 R3-001 amend). Same §3.3 matrix. The finalizer reads/resolves markers, but does NOT emit final report; that is the next agent's job (Step 3d).
New CI lint:
- scripts/check_v3_6_8_cite_provenance_standalone.py — checks that both standalone finalizer agents exist, carry the §3.3 matrix, and are referenced from the appropriate dispatch points in academic-paper/SKILL.md and deep-research/SKILL.md. For deep-research, also asserts that cite_provenance_finalizer_agent runs AFTER report_compiler_agent (NOT before) per the dispatch order.
Acceptance:
- Both finalizer agents exist
- Academic-paper standalone test: a draft from academic-paper full mode (no pipeline orchestrator) reaches formatter_agent with all <!--ref:slug--> comments resolved (no unresolved bare comments cause format-time hard-block in healthy path)
- Deep-research standalone test (round-2 R2-001 + round-3 R3-001 amend): a report from deep-research full mode passes through report_compiler_agent (compiles, emits two-layer placeholders per Step 3a) → cite_provenance_finalizer_agent (resolves markers) → cite_provenance_terminal_gate_agent (inspects resolved draft, emits final report). Three single-purpose agents, no re-invocation. The terminal report is emitted only if the gate agent passes.
formatter_agent for academic-paper, cite_provenance_terminal_gate_agent for deep-research; round-3 R3-001 amend)Files to modify:
- academic-paper/agents/formatter_agent.md — add hard-gate refusal rule: refuse any draft containing (a) [UNVERIFIED CITATION ...] literal markers, or (b) unresolved <!--ref:slug--> HTML comments whose status is neither ok nor LOW-WARN-acknowledged
Files to create (round-3 R3-001 amend):
- deep-research/agents/cite_provenance_terminal_gate_agent.md — separate single-purpose terminal-gate agent. Reads the resolved draft from cite_provenance_finalizer_agent, applies the same refusal conditions as formatter_agent, emits the final report or refuses. Does NOT compile (that is report_compiler_agent), does NOT resolve markers (that is cite_provenance_finalizer_agent). Pure inspect + emit/refuse. This avoids the two-pass report_compiler_agent dispatch problem and keeps the v3.6.7 strict-partial-inversion discipline intact for report_compiler_agent.
New CI lint:
- scripts/check_v3_6_8_formatter_hard_gate.py — checks that formatter_agent carries both refusal rules
- scripts/check_v3_6_8_terminal_gate_agent.py (round-3 R3-001 amend) — checks that cite_provenance_terminal_gate_agent exists, carries both refusal rules, and is wired into deep-research dispatch AFTER cite_provenance_finalizer_agent. Asserts (a) the agent never reads frontmatter (per partial-inversion discipline of Step 3a), (b) the agent never modifies any <!--ref:slug--> marker (only inspects), (c) report_compiler_agent is NOT modified to carry hard-gate (preserves v3.6.7 PATTERN PROTECTION block byte-equivalence per Step 0)
Acceptance:
- formatter_agent carries both refusal rules
- cite_provenance_terminal_gate_agent exists and carries both refusal rules; report_compiler_agent is unchanged from v3.6.7 (per Step 0 boundary rule + round-3 R3-001 amend)
- Mutation tests: removing either refusal rule from formatter_agent fails lint; removing the terminal-gate agent or its rules fails lint; modifying report_compiler_agent's v3.6.7 PATTERN PROTECTION block fails Step 0 lint
- End-to-end test (academic-paper): a draft with one HIGH WARN marker is hard-refused at formatter_agent; same draft after /ars-mark-read acknowledgement passes hard gate
- End-to-end test (deep-research, round-3 R3-001 amend): a report with one HIGH WARN marker emitted during report_compiler_agent compilation is hard-refused at cite_provenance_terminal_gate_agent; same report after /ars-mark-read acknowledgement + cite_provenance_finalizer_agent re-pass passes the terminal gate
The 3-agent linear dispatch (report_compiler_agent → cite_provenance_finalizer_agent → cite_provenance_terminal_gate_agent) needs a concrete handoff contract. Partial-inversion preserved (round-5 R5-002 amend): report_compiler_agent outputs ONLY standard markdown with two-layer citations per §3a; a thin dispatcher wrapper (single-purpose orchestration code, not an LLM agent) reads that markdown and constructs the handoff payload (draft_path / draft_text / compilation_metadata) for the next agent. The finalizer and terminal-gate agents consume the schema directly because §3a explicitly authorizes them to know about frontmatter / dispatch state.
Files to create:
- deep-research/scripts/cite_provenance_dispatcher.py (round-6 R6-001 + round-7 R7-002 amend) — single-purpose dispatcher wrapper for the deep-research standalone path. Reads report_compiler_agent's markdown output, computes compilation_metadata (word count, section count, citation count, two-layer placeholder count) by static parse, constructs the handoff payload per shared/contracts/cite_provenance/handoff_schema.json, and dispatches to cite_provenance_finalizer_agent. Not an LLM agent — pure orchestration code, mirrors the v3.6.7 wrapper pattern. Convention note (round-7 R7-002 amend): deep-research/scripts/ is a new per-skill subdirectory introduced by v3.7.1 alongside the existing agents/ / examples/ / references/ subdirs (root scripts/ continues to host cross-skill helpers); v3.7.1 explicitly blesses this convention because the dispatcher is skill-local runtime code with no cross-skill use. Plugin packaging via skills/deep-research -> ../deep-research symlink (per v3.7.0 .claude-plugin/ packaging) automatically includes the new subdir. For the academic-pipeline path the equivalent dispatcher logic lives in pipeline_orchestrator_agent.md per Step 3b (no separate file needed; orchestrator already handles handoff state).
- shared/contracts/cite_provenance/handoff_schema.json — JSON Schema for the per-step handoff payload (round-5 R5-002 amend; partial-inversion-preserving allocation of populator roles):
- draft_path (string, absolute or repo-relative): location of the current draft markdown file. Populated by the orchestrator/dispatcher wrapping report_compiler_agent's standard markdown output (NOT by report_compiler_agent itself; partial-inversion preserved per §3a).
- draft_text (string, optional alternative to draft_path): inline draft content for in-memory dispatch. Same population rule.
- compilation_metadata (object, populated by the orchestrator/dispatcher by computing over report_compiler_agent's output): word count, section count, citation count, two-layer placeholder count. report_compiler_agent itself emits ONLY standard markdown with two-layer citations (per §3a strict-partial-inversion); it does NOT populate this field. The orchestrator counts/computes after receiving the markdown.
- citation_resolution_summary (object, populated by cite_provenance_finalizer_agent): per-status counts (ok, LOW-WARN, MED-WARN, HIGH-WARN), unresolved-marker count, refcodes that triggered each status. The finalizer reads frontmatter + emits this summary as part of its job; this is consistent with §3a since the finalizer is explicitly authorized to read frontmatter.
- unresolved_marker_count (integer, populated by finalizer; copied forward by terminal gate without modification): number of <!--ref:slug--> comments that did NOT resolve to ok or LOW-WARN-acknowledged.
- terminal_gate_decision (object, populated by cite_provenance_terminal_gate_agent): decision: "EMITTED" | "REFUSED", reason: <string|null> (refusal reason if REFUSED), final_report_path: <string|null> (set if EMITTED).
- Partial-inversion ratification: report_compiler_agent outputs only markdown; the dispatcher (single-purpose wrapper, not an LLM agent) computes draft_path / draft_text / compilation_metadata from that markdown. This preserves §3a's "agent never knows about finalizer / terminal-gate / orchestrator." Lint at Step 3e MUST verify that no agent prompt file (report_compiler_agent.md) references the handoff schema or its field names.
Files to modify:
- deep-research/SKILL.md — add subsection "Cite-Provenance Pipeline (v3.7.1)" describing the 3-agent dispatch order with reference to the handoff schema. The existing report_compiler_agent → final-report dispatch (per current SKILL.md) is replaced (in standalone mode) with the 3-agent chain. The PATTERN PROTECTION block in report_compiler_agent is unchanged; only the SKILL.md dispatch wiring updates.
- deep-research/agents/cite_provenance_finalizer_agent.md — add explicit prompt instruction "consume handoff payload per shared/contracts/cite_provenance/handoff_schema.json; emit the same payload with citation_resolution_summary and unresolved_marker_count populated."
- deep-research/agents/cite_provenance_terminal_gate_agent.md — add explicit prompt instruction "consume handoff payload; emit same payload with terminal_gate_decision populated; emit final report file if EMITTED."
New CI lint:
- scripts/check_v3_6_8_handoff_schema.py — validates the JSON Schema shape; verifies deep-research/SKILL.md references it; verifies both new agent prompts mention handoff_schema.json and the field names they're responsible for.
Acceptance:
- handoff_schema.json exists and validates
- deep-research/scripts/cite_provenance_dispatcher.py exists, parses markdown, populates the schema's orchestrator-owned fields, and dispatches to cite_provenance_finalizer_agent (round-6 R6-001 amend)
- deep-research/SKILL.md 3-agent dispatch wiring documented, naming the dispatcher script as the standalone-mode invocation surface
- Both new agent prompts reference the schema by path
- report_compiler_agent.md does NOT reference the schema or its field names (round-5 R5-002 lint enforcement)
- Mutation tests confirm: (a) removing any schema reference from finalizer/terminal-gate fails lint; (b) adding any schema reference to report_compiler_agent.md fails lint; (c) renaming or deleting cite_provenance_dispatcher.py fails lint
- Integration test: a synthetic 3-agent dispatch with a draft containing one HIGH WARN marker propagates the correct citation_resolution_summary (1 HIGH-WARN, 0 ok), correct unresolved_marker_count (1), and terminal_gate_decision (REFUSED, reason cites HIGH WARN refcode)
Files to modify:
- academic-pipeline/agents/pipeline_orchestrator_agent.md — add Drift Log artifact spec per §3.4 (peer file <session>_drift_log.md, NOT a Schema 9 amendment per Q3 amend)
- shared/templates/session_drift_log_template.md — new file, canonical template
Acceptance:
- Orchestrator emits <session>_drift_log.md at session end
- Template enforces neutral language (no deviation / out-of-spec / error for backflow)
- Manual run on 2026-04-30 production session reproduces a plausible Drift Log (validation against this paper's authoring session as case study)
Files to modify:
- deep-research/agents/bibliography_agent.md — add retrieval-stage prompt block per §3.5 with the 5 mechanical auto-step chain (publisher_direct → wayback_cdx_search → author_personal_website → scholar_all_versions → oa_aggregators), content-match mandatory rule, AND the pending_user_action: researchgate_request human-handoff terminal step (per round-1 codex F-007 amend)
- New shared reference: shared/references/retrieval_fallback_chain.md — canonical fallback method spec, failure-reason vocabulary, content-match procedure, RG human-handoff protocol (how user resumes after acquiring the file via RG)
Acceptance:
- bibliography_agent retrieval prompt enumerates 5 mechanical auto-fallback methods + 1 human-handoff terminal
- Each auto-method has documented failure-reason vocabulary
- verify_content_match() procedure spec'd in the reference file
- RG human-handoff is documented as terminal (NOT in auto-chain); fallback emits pending_user_action: researchgate_request_<refcode> log entry, never auto-sends RG message
- Sample run: F3 (Wayback recoverable) and E1 (author site recoverable) successfully retrieved by auto-chain; one paywalled-only refcode reaches RG human-handoff terminal and emits the pending_user_action log entry without making any outbound RG request
New synthetic eval cases:
- tests/eval_cases/v3_6_8_trust_chain_violation.md — corpus with mixed source_acquired states; verifies (a) bibliography_agent does not emit unsupported verified claims, (b) audit emits correct Scope Report, (c) cite-time hook injects markers correctly, (d) formatter_agent refuses to convert academic-paper drafts with [UNVERIFIED CITATION ...] markers or unresolved <!--ref:slug--> comments, (e) cite_provenance_terminal_gate_agent refuses deep-research reports with same markers (round-3 R3-001 + round-4 R4-004 amend; gate owner is the separate single-purpose agent, NOT report_compiler_agent)
- tests/eval_cases/v3_6_8_retrieval_fallback.md — corpus with paywall-blocked, Wayback-recoverable, and content-mismatch entries; verifies fallback chain behavior end-to-end
Acceptance: - Both eval cases pass - Wired into the v3.7.1 release CI gate
/ars-mark-read plugin commands (round-2 R2-002 amend)The /ars-mark-read / /ars-unmark-read / /ars-list-read commands defined in §3.6 Human-Read Signal Protocol need a concrete implementation target that fits the existing v3.7.0 plugin commands convention (commands/ars-*.md).
Files to create:
- commands/ars-mark-read.md — slash command frontmatter + body. Validates <citation_key> against active literature_corpus[]; on miss, emit [ARS-MARK-READ ERROR: citation_key '<slug>' not in literature_corpus[]] and refuse to write. On success, append entry to <session>_human_read_log.yaml. Model: sonnet (per feedback_no_haiku.md); routing: per feedback_delegation_mode_ask.md, this is a per-keystroke command, not an agent dispatch.
- commands/ars-unmark-read.md — slash command frontmatter + body. Append rescinded_at: <ISO 8601> to the matching <session>_human_read_log.yaml entry (NOT delete; per §3.6 third firm rule, log is append-only).
- commands/ars-list-read.md — slash command frontmatter + body. Reads <session>_human_read_log.yaml and prints active marks (those without rescinded_at).
- commands/ars-mark-read.zh-TW.md (optional) — zh-TW translation if English-only commands break the consumer surface.
New CI lint:
- scripts/check_v3_6_8_mark_read_commands.py — verifies the 3 commands exist, carry the validation rule (citation_key must be in literature_corpus[]), and write to <session>_human_read_log.yaml (NOT to entry frontmatter, per §3.1 firm rule #3 / round-1 F-005).
Acceptance:
- 3 commands exist with sonnet model routing in frontmatter
- Invalid <citation_key> (not in corpus) produces hard error, no log write
- Valid <citation_key> appends to the passport-derived read-log path (<passport-path-parent>/<passport-stem>_human_read_log.yaml per §3.6 R5-003 amend) with marked_at: <ISO 8601>
- /ars-unmark-read writes rescinded_at, never deletes original entry (append-only firm rule)
- /ars-list-read returns only entries without rescinded_at
- End-to-end with Step 3 finalizer: after /ars-mark-read smith2024, the next finalizer pass promotes <!--ref:smith2024 LOW-WARN--> to <!--ref:smith2024 ok-->
- Fail-fast (round-5 R5-003 amend): all 4 fail-fast modes from §3.6 (no active passport / passport not found / parent unreadable / read-log unwritable) emit the canonical [ARS-MARK-READ ERROR: ...] message and exit non-zero. First-time write (read-log file does not yet exist) is NOT a fail mode — the command creates the file with the YAML schema header before appending.
ARS v3.7.1 (roadmap slot v3.6.8) ships when:
human_read_source is NEVER stored on the entry (per §3.6 + round-1 codex F-005)check_v3_6_7_pattern_protection.py 12/12 + 58 mutation tests; per §3.7 + Q5 amend)pipeline_orchestrator_agent at stage transition for academic-pipeline; per-skill cite_provenance_finalizer_agent at deliverable boundary for standalone academic-paper / deep-research) AND mode-dependent terminal hard-gate is wired (round-3 R3-004 amend): formatter_agent enforces hard gate for academic-pipeline + standalone academic-paper; cite_provenance_terminal_gate_agent enforces hard gate for standalone deep-research. Both refusal targets cover [UNVERIFIED CITATION ...] markers and unresolved <!--ref:slug--> comments.bibliography_agent retrieval prompt references itscripts/check_v3_6_8_*.py family) enforces all the above; v3.7.1 lint manifest is separate from v3.6.7's frozen manifest (per Step 0 + round-1 codex F-004)[UNVERIFIED CITATION ...] markers on the 31 description-only entries before the user reaches chapter prose/ars-mark-read <citation_key> command works end-to-end: writes to <session>_human_read_log.yaml, finalizer joins on next pass, LOW-WARN markers are promoted to ok (per §3.6)academic-paper full mode (no pipeline orchestrator) reaches formatter_agent with all <!--ref:slug--> comments resolved by the per-skill cite_provenance_finalizer_agent; same for deep-research full modeok markers.claude/CLAUDE.md / MODE_REGISTRY.md / .claude-plugin/plugin.json / docs/ARCHITECTURE.md / new /ars-mark-read command docs all reference v3.7.1 (roadmap slot v3.6.8) consistentlypassport.session_history[] integration of Drift Log (per Q3 amend; v3.7.x+ candidate)_AUDIT_FINDINGS.md for the round-3 codex audit cross-check, a PENDING_RETRIEVAL.md for unsourced entries, a per-domain quote-registry for the dominant source family, and a ROADMAP.md tracking chapter-section progressfeedback_ars_user_drift_is_verification_behavior.mdfeedback_ars_verification_provenance_layer.mdfeedback_ars_audit_scope_disclosure.mdfeedback_ars_session_drift_log.mdfeedback_ars_retrieval_fallback_chain.mddocs/design/2026-04-29-ars-v3.6.7-downstream-agent-pattern-protection-spec.md — downstream agent pattern protection (D1–D5 patterns are the upstream complement to v3.6.7's downstream-agent narrative/instrument/publication patterns)literature_corpus_entry.schema.jsonfeedback_ars_bibliography_agent_hallucination_patterns.md (2026-04-29) — five upstream bibliography_agent patterns (JSIE prestigious anchoring, online vs print year, author mashup, phantom source, annotation overclaim). v3.7.1 D1 protection mitigates the downstream consequence of those upstream patterns by making trust-chain transparent.feedback_subagent_tool_hallucination.md (2026-04-29) — sub-agent tool hallucination (claiming to have run codex audit / simulated fixes). v3.7.1 D2 (audit scope disclosure) is the structural protection: audit reports must declare what they actually verified.feedback_codex_iterative_spec_review_to_zero.md — convergence target rationale (run codex review until 0 finding before impl)feedback_codex_review_vs_resume_audit_scope.md — "补 counter 不改 rule → 停" stop signal vs. architectural inflectionfeedback_architectural_inflection_after_repeated_p1.md — when 3+ same-finding partials = architectural smell, escalateRisk 1: Frontmatter schema split breaks existing user adapters that emit only verified: <date>.
Mitigation: Adapters can map legacy verified to the most conservative of the new fields (source_verified_against_original: false unless adapter has affirmative source-acquisition signal). Document migration path in academic-pipeline/references/literature_corpus_consumers.md.
Risk 2: Cite-Time Provenance Check warnings are noisy in early-corpus drafts. Mitigation: Markers are draft-only and removed before final output. The hard gate is at format-conversion only. Users self-pace marker resolution.
Risk 3: Session Drift Log produces "yet another file to read" with low signal-to-noise. Mitigation: Drift Log is end-of-session, not real-time. User reads only when reflecting on long sessions. Template structure surfaces top-3 actionable items first; full trajectory in collapsible section.
Risk 4: Retrieval fallback chain extends retrieval time meaningfully.
Mitigation: Steps are sequential with early-exit on first content-match success. Wayback CDX is fast (<5s); author-website grep is moderate; OA aggregators are batch-able; chain exhaustion takes <30s for typical paper. Acceptable for batch retrieval. Default-ON for the 5 mechanical auto-steps without env-flag opt-out is intentional (per Q6 amend) — under v3.6.5+ ARS the corpus is already user-owned via literature_corpus[] adapters; there is no failure mode where a user wants single-channel retrieval back. RG human-handoff is the explicit safety boundary: default-ON never means automated outreach (per round-1 codex F-007).
Risk 5: Spec is misread as enforcement of user behavior.
Mitigation: §2.2 explicitly states "no keep-on-track enforcement." All warnings are advisory; only formatter_agent enforces a hard gate, and that gate exists to prevent silent shipment of unverified citations to formal output — a different concern from in-session user behavior.
Risk 6 (round-1 codex F-001 amend): Standalone-mode finalizer fails to fire — a draft from academic-paper full mode (no pipeline orchestrator) reaches formatter_agent with unresolved bare <!--ref:slug--> comments and gets hard-blocked, leaving the user no resolver path.
Mitigation: Step 3c ships per-skill cite_provenance_finalizer_agent for both academic-paper and deep-research standalone modes. Standalone-mode test (acceptance criterion #11) explicitly exercises this path. If a future skill adds a new mode that bypasses both the pipeline orchestrator AND the per-skill finalizer, the v3.7.1 lint should reject the dispatch as cite_provenance_finalizer_unwired.
Risk 7 (round-1 codex F-004 amend): v3.7.1 lint widens the v3.6.7 frozen manifest scope and re-opens 18 codex rounds of v3.6.7 convergence work.
Mitigation: Step 0 introduces scripts/v3_6_8_inversion_manifest.json (filename retains roadmap-slot namespace per Q7) and scripts/check_v3_6_8_pattern_protection.py as separate artifacts from the v3.6.7 frozen manifest. Step 0 acceptance includes v3.6.7 lint regression PASS gate. v3.7.1 PRs that touch any file in v3.6.7's frozen manifest list require an explicit "byte-equivalent v3.6.7-tagged block preserved" SHA assertion (per Step 0 canonical range definition / round-3 R3-003 amend).
All four original §9 open questions are now closed by the 2026-05-07 brainstorm + round-1 codex amend cycle:
| # | Original question | Resolution |
|---|---|---|
| 1 | Schema 9 location of Drift Log artifact (passport.session_history[] or peer file?) | Closed by Q3: peer file <session>_drift_log.md; Schema 9 integration deferred to v3.7.x+ (see §6 Out-of-Scope). |
| 2 | ResearchGate Request Full-Text in auto-chain or human-mediated step? | Closed by round-1 codex F-007: human-handoff terminal step (NOT in auto-chain); auto-chain emits pending_user_action: researchgate_request_<refcode>; default-ON does not apply to RG. |
| 3 | Cite-Time hook fires on every emission or only at deliverable-finalization? | Closed by Q4 → Q8 supersede: per-emission placeholder + per-stage (pipeline) or per-deliverable (standalone) finalize. Hybrid architecture mirrors v3.6.7 Step 6 audit_artifact gate. |
| 4 | human_read_source user signal via UI/command or inference? |
Closed by Q2 + round-1 codex F-005: explicit /ars-mark-read <citation_key> command writes to peer file <session>_human_read_log.yaml; orchestrator merges at frontmatter-read time; field is derived, never stored on entry (§3.6 Human-Read Signal Protocol). |
No open questions remain after round-1 codex amend. Round-2 codex review will run to confirm 0-finding convergence on the post-surgical-edit body before implementation begins (per feedback_codex_iterative_spec_review_to_zero.md).
Each pattern in §3 is traceable to a specific event in the 2026-04-30 session:
| Pattern | Generative event | Memory entry |
|---|---|---|
| D1 | verified: 2026-04-30 written on entries that were never cross-checked against original |
feedback_ars_verification_provenance_layer.md |
| D2 | Round 3 codex audit reported "ADDRESSED" without disclosing 22/31 split | feedback_ars_audit_scope_disclosure.md |
| D3 | Risk articulated: user could have written chapter prose citing description-only entries before realizing scope gap | feedback_ars_verification_provenance_layer.md (cite-time provision) |
| D4 | User repeatedly left phase to verify; session-end visibility of trajectory absent | feedback_ars_user_drift_is_verification_behavior.md, feedback_ars_session_drift_log.md |
| D5 | F3 Wayback recovery + E1 Harvey personal-site recovery + PMu wrong-record content match failure | feedback_ars_retrieval_fallback_chain.md |
Patterns are not speculative; each is a real-world hallucination-defense gap that materialized in production use.