psyclaw 0.27.22 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/dist/apps/panel/index.html +2 -2
- package/dist/src/adapters/pi/extension.js +225 -92
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/agents/import.js +44 -32
- package/dist/src/agents/import.js.map +1 -1
- package/dist/src/analysis/hooks.d.ts +1 -0
- package/dist/src/analysis/hooks.js +20 -1
- package/dist/src/analysis/hooks.js.map +1 -1
- package/dist/src/ars/bridge.d.ts +20 -0
- package/dist/src/ars/bridge.js +132 -0
- package/dist/src/ars/bridge.js.map +1 -0
- package/dist/src/ars/contracts.d.ts +55 -0
- package/dist/src/ars/contracts.js +2 -0
- package/dist/src/ars/contracts.js.map +1 -0
- package/dist/src/ars/panel-plan.d.ts +4 -0
- package/dist/src/ars/panel-plan.js +11 -0
- package/dist/src/ars/panel-plan.js.map +1 -0
- package/dist/src/ars/pi-panel-executor.d.ts +30 -0
- package/dist/src/ars/pi-panel-executor.js +140 -0
- package/dist/src/ars/pi-panel-executor.js.map +1 -0
- package/dist/src/ars/profile.d.ts +36 -0
- package/dist/src/ars/profile.js +119 -0
- package/dist/src/ars/profile.js.map +1 -0
- package/dist/src/ars/re-review.d.ts +22 -0
- package/dist/src/ars/re-review.js +172 -0
- package/dist/src/ars/re-review.js.map +1 -0
- package/dist/src/branding.d.ts +2 -4
- package/dist/src/branding.js +3 -5
- package/dist/src/branding.js.map +1 -1
- package/dist/src/bundled-tools.d.ts +3 -0
- package/dist/src/bundled-tools.js +21 -0
- package/dist/src/bundled-tools.js.map +1 -0
- package/dist/src/chat.js +6 -5
- package/dist/src/chat.js.map +1 -1
- package/dist/src/creation/contracts.d.ts +38 -0
- package/dist/src/creation/contracts.js +2 -0
- package/dist/src/creation/contracts.js.map +1 -0
- package/dist/src/creation/service.d.ts +7 -0
- package/dist/src/creation/service.js +196 -0
- package/dist/src/creation/service.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/installer.js +32 -10
- package/dist/src/install/installer.js.map +1 -1
- package/dist/src/orchestration/personas.d.ts +15 -0
- package/dist/src/orchestration/personas.js +51 -0
- package/dist/src/orchestration/personas.js.map +1 -0
- package/dist/src/orchestration/pi-executor.d.ts +1 -0
- package/dist/src/orchestration/pi-executor.js +1 -1
- package/dist/src/orchestration/pi-executor.js.map +1 -1
- package/dist/src/panel/extension.js +29 -8
- package/dist/src/panel/extension.js.map +1 -1
- package/dist/src/panel/server.js +14 -5
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/project/paths.d.ts +3 -0
- package/dist/src/project/paths.js +7 -0
- package/dist/src/project/paths.js.map +1 -1
- package/dist/src/rules/user-rules.d.ts +8 -0
- package/dist/src/rules/user-rules.js +36 -0
- package/dist/src/rules/user-rules.js.map +1 -0
- package/dist/src/skills/contracts.d.ts +4 -4
- package/dist/src/skills/recommended.js +1 -1
- package/dist/src/skills/registry.js +49 -28
- package/dist/src/skills/registry.js.map +1 -1
- package/dist/src/style/cli-ui.js +0 -1
- package/dist/src/style/cli-ui.js.map +1 -1
- package/package.json +14 -3
- package/scripts/rebrand-pi.mjs +6 -0
- package/skills/recommended/catalog.json +2 -11
- package/vendor/ars/.claude/CLAUDE.md +371 -0
- package/vendor/ars/.command-invariants.toml +24 -0
- package/vendor/ars/CITATION.cff +35 -0
- package/vendor/ars/LICENSE +417 -0
- package/vendor/ars/MODE_REGISTRY.md +76 -0
- package/vendor/ars/NOTICE.md +26 -0
- package/vendor/ars/POSITIONING.md +99 -0
- package/vendor/ars/PSYCLAW_SOURCE.json +10 -0
- package/vendor/ars/README.md +751 -0
- package/vendor/ars/SECURITY.md +52 -0
- package/vendor/ars/THIRD_PARTY.md +70 -0
- package/vendor/ars/academic-paper/SKILL.md +542 -0
- package/vendor/ars/academic-paper/agents/abstract_bilingual_agent.md +171 -0
- package/vendor/ars/academic-paper/agents/argument_builder_agent.md +276 -0
- package/vendor/ars/academic-paper/agents/citation_compliance_agent.md +422 -0
- package/vendor/ars/academic-paper/agents/draft_writer_agent.md +656 -0
- package/vendor/ars/academic-paper/agents/formatter_agent.md +999 -0
- package/vendor/ars/academic-paper/agents/intake_agent.md +393 -0
- package/vendor/ars/academic-paper/agents/literature_strategist_agent.md +626 -0
- package/vendor/ars/academic-paper/agents/peer_reviewer_agent.md +516 -0
- package/vendor/ars/academic-paper/agents/revision_coach_agent.md +334 -0
- package/vendor/ars/academic-paper/agents/socratic_mentor_agent.md +527 -0
- package/vendor/ars/academic-paper/agents/structure_architect_agent.md +401 -0
- package/vendor/ars/academic-paper/agents/visualization_agent.md +441 -0
- package/vendor/ars/academic-paper/examples/chinese_paper_example.md +278 -0
- package/vendor/ars/academic-paper/examples/clinical_citation_verification_checklist.md +95 -0
- package/vendor/ars/academic-paper/examples/clinical_epistemic_status_example.md +100 -0
- package/vendor/ars/academic-paper/examples/commitment_ledger_example.md +147 -0
- package/vendor/ars/academic-paper/examples/imrad_hei_example.md +234 -0
- package/vendor/ars/academic-paper/examples/literature_review_example.md +260 -0
- package/vendor/ars/academic-paper/examples/plan_mode_guided_writing.md +600 -0
- package/vendor/ars/academic-paper/examples/revision_mode_example.md +344 -0
- package/vendor/ars/academic-paper/examples/revision_recovery_example.md +506 -0
- package/vendor/ars/academic-paper/examples/version_family_reconciliation_example.md +89 -0
- package/vendor/ars/academic-paper/references/abstract_writing_guide.md +169 -0
- package/vendor/ars/academic-paper/references/academic_writing_style.md +188 -0
- package/vendor/ars/academic-paper/references/anti_leakage_protocol.md +83 -0
- package/vendor/ars/academic-paper/references/apa7_chinese_citation_guide.md +364 -0
- package/vendor/ars/academic-paper/references/apa7_extended_guide.md +198 -0
- package/vendor/ars/academic-paper/references/changelog.md +11 -0
- package/vendor/ars/academic-paper/references/citation_format_switcher.md +228 -0
- package/vendor/ars/academic-paper/references/committee_correspondence_protocol.md +158 -0
- package/vendor/ars/academic-paper/references/credit_authorship_guide.md +308 -0
- package/vendor/ars/academic-paper/references/disclosure_mode_protocol.md +478 -0
- package/vendor/ars/academic-paper/references/domain_evidence_profiles.md +38 -0
- package/vendor/ars/academic-paper/references/failure_paths.md +349 -0
- package/vendor/ars/academic-paper/references/funding_statement_guide.md +319 -0
- package/vendor/ars/academic-paper/references/hei_domain_glossary.md +169 -0
- package/vendor/ars/academic-paper/references/intro_title_rhetoric_guide.md +114 -0
- package/vendor/ars/academic-paper/references/journal_submission_guide.md +249 -0
- package/vendor/ars/academic-paper/references/latex_template_reference.md +378 -0
- package/vendor/ars/academic-paper/references/mode_selection_guide.md +378 -0
- package/vendor/ars/academic-paper/references/paper_structure_patterns.md +330 -0
- package/vendor/ars/academic-paper/references/plan_mode_protocol.md +112 -0
- package/vendor/ars/academic-paper/references/policy_anchor_disclosure_protocol.md +200 -0
- package/vendor/ars/academic-paper/references/policy_anchor_table.md +157 -0
- package/vendor/ars/academic-paper/references/revision_patch_protocol.md +173 -0
- package/vendor/ars/academic-paper/references/statistical_visualization_standards.md +750 -0
- package/vendor/ars/academic-paper/references/venue_disclosure_policies.md +259 -0
- package/vendor/ars/academic-paper/references/vlm_figure_verification.md +126 -0
- package/vendor/ars/academic-paper/references/workflow_phase_details.md +135 -0
- package/vendor/ars/academic-paper/references/writing_judgment_framework.md +59 -0
- package/vendor/ars/academic-paper/references/writing_quality_check.md +173 -0
- package/vendor/ars/academic-paper/templates/bilingual_abstract_template.md +78 -0
- package/vendor/ars/academic-paper/templates/case_study_template.md +129 -0
- package/vendor/ars/academic-paper/templates/conference_paper_template.md +108 -0
- package/vendor/ars/academic-paper/templates/credit_statement_template.md +132 -0
- package/vendor/ars/academic-paper/templates/funding_statement_template.md +290 -0
- package/vendor/ars/academic-paper/templates/imrad_template.md +183 -0
- package/vendor/ars/academic-paper/templates/latex_article_template.tex +199 -0
- package/vendor/ars/academic-paper/templates/literature_review_template.md +135 -0
- package/vendor/ars/academic-paper/templates/policy_brief_template.md +139 -0
- package/vendor/ars/academic-paper/templates/revision_tracking_template.md +199 -0
- package/vendor/ars/academic-paper/templates/theoretical_paper_template.md +119 -0
- package/vendor/ars/academic-paper-reviewer/SKILL.md +491 -0
- package/vendor/ars/academic-paper-reviewer/agents/devils_advocate_reviewer_agent.md +443 -0
- package/vendor/ars/academic-paper-reviewer/agents/domain_reviewer_agent.md +412 -0
- package/vendor/ars/academic-paper-reviewer/agents/editorial_synthesizer_agent.md +478 -0
- package/vendor/ars/academic-paper-reviewer/agents/eic_agent.md +339 -0
- package/vendor/ars/academic-paper-reviewer/agents/field_analyst_agent.md +221 -0
- package/vendor/ars/academic-paper-reviewer/agents/methodology_reviewer_agent.md +449 -0
- package/vendor/ars/academic-paper-reviewer/agents/perspective_reviewer_agent.md +427 -0
- package/vendor/ars/academic-paper-reviewer/examples/hei_paper_review_example.md +391 -0
- package/vendor/ars/academic-paper-reviewer/examples/interdisciplinary_review_example.md +299 -0
- package/vendor/ars/academic-paper-reviewer/examples/subclaim_decomposition_example.md +80 -0
- package/vendor/ars/academic-paper-reviewer/references/calibration_mode_protocol.md +256 -0
- package/vendor/ars/academic-paper-reviewer/references/changelog.md +10 -0
- package/vendor/ars/academic-paper-reviewer/references/editorial_decision_standards.md +236 -0
- package/vendor/ars/academic-paper-reviewer/references/guided_mode_protocol.md +34 -0
- package/vendor/ars/academic-paper-reviewer/references/integration_guide.md +15 -0
- package/vendor/ars/academic-paper-reviewer/references/quality_rubrics.md +84 -0
- package/vendor/ars/academic-paper-reviewer/references/re_review_mode_protocol.md +340 -0
- package/vendor/ars/academic-paper-reviewer/references/review_criteria_framework.md +98 -0
- package/vendor/ars/academic-paper-reviewer/references/review_panel_provenance_protocol.md +197 -0
- package/vendor/ars/academic-paper-reviewer/references/review_quality_thinking.md +58 -0
- package/vendor/ars/academic-paper-reviewer/references/reviewer_sprint_prompt_source.md +324 -0
- package/vendor/ars/academic-paper-reviewer/references/sprint_contract_protocol.md +296 -0
- package/vendor/ars/academic-paper-reviewer/references/statistical_reporting_standards.md +505 -0
- package/vendor/ars/academic-paper-reviewer/references/top_journals_by_field.md +206 -0
- package/vendor/ars/academic-paper-reviewer/templates/editorial_decision_template.md +235 -0
- package/vendor/ars/academic-paper-reviewer/templates/peer_review_report_template.md +305 -0
- package/vendor/ars/academic-paper-reviewer/templates/revision_response_template.md +248 -0
- package/vendor/ars/academic-pipeline/SKILL.md +736 -0
- package/vendor/ars/academic-pipeline/agents/claim_ref_alignment_audit_agent.md +382 -0
- package/vendor/ars/academic-pipeline/agents/collaboration_depth_agent.md +164 -0
- package/vendor/ars/academic-pipeline/agents/integrity_verification_agent.md +870 -0
- package/vendor/ars/academic-pipeline/agents/pipeline_orchestrator_agent.md +1379 -0
- package/vendor/ars/academic-pipeline/agents/state_tracker_agent.md +622 -0
- package/vendor/ars/academic-pipeline/examples/full_pipeline_example.md +482 -0
- package/vendor/ars/academic-pipeline/examples/integrity_failure_recovery.md +389 -0
- package/vendor/ars/academic-pipeline/examples/mid_entry_example.md +414 -0
- package/vendor/ars/academic-pipeline/references/adapters/.gitkeep +0 -0
- package/vendor/ars/academic-pipeline/references/adapters/overview.md +153 -0
- package/vendor/ars/academic-pipeline/references/ai_research_failure_modes.md +185 -0
- package/vendor/ars/academic-pipeline/references/changelog.md +14 -0
- package/vendor/ars/academic-pipeline/references/claim_audit_calibration_protocol.md +175 -0
- package/vendor/ars/academic-pipeline/references/claim_verification_protocol.md +282 -0
- package/vendor/ars/academic-pipeline/references/external_review_protocol.md +131 -0
- package/vendor/ars/academic-pipeline/references/integrity_review_protocol.md +110 -0
- package/vendor/ars/academic-pipeline/references/literature_corpus_consumers.md +193 -0
- package/vendor/ars/academic-pipeline/references/mode_advisor.md +135 -0
- package/vendor/ars/academic-pipeline/references/passport_as_reset_boundary.md +132 -0
- package/vendor/ars/academic-pipeline/references/pipeline_state_machine.md +405 -0
- package/vendor/ars/academic-pipeline/references/plagiarism_detection_protocol.md +239 -0
- package/vendor/ars/academic-pipeline/references/process_summary_protocol.md +209 -0
- package/vendor/ars/academic-pipeline/references/progress_dashboard_template.md +38 -0
- package/vendor/ars/academic-pipeline/references/reinforcement_content.md +15 -0
- package/vendor/ars/academic-pipeline/references/reproducibility_audit.md +55 -0
- package/vendor/ars/academic-pipeline/references/score_trajectory_protocol.md +78 -0
- package/vendor/ars/academic-pipeline/references/team_collaboration_protocol.md +261 -0
- package/vendor/ars/academic-pipeline/references/two_stage_review_protocol.md +27 -0
- package/vendor/ars/academic-pipeline/templates/pipeline_status_template.md +146 -0
- package/vendor/ars/agents/report_compiler_agent.md +341 -0
- package/vendor/ars/agents/research_architect_agent.md +298 -0
- package/vendor/ars/agents/synthesis_agent.md +356 -0
- package/vendor/ars/commands/ars-3w.md +10 -0
- package/vendor/ars/commands/ars-abstract.md +10 -0
- package/vendor/ars/commands/ars-cache-invalidate.md +20 -0
- package/vendor/ars/commands/ars-citation-check.md +10 -0
- package/vendor/ars/commands/ars-disclosure.md +10 -0
- package/vendor/ars/commands/ars-format-convert.md +10 -0
- package/vendor/ars/commands/ars-full.md +9 -0
- package/vendor/ars/commands/ars-lit-review.md +12 -0
- package/vendor/ars/commands/ars-mark-read.md +18 -0
- package/vendor/ars/commands/ars-outline.md +10 -0
- package/vendor/ars/commands/ars-plan.md +10 -0
- package/vendor/ars/commands/ars-rebuttal-audit.md +12 -0
- package/vendor/ars/commands/ars-reviewer.md +9 -0
- package/vendor/ars/commands/ars-revision-coach.md +9 -0
- package/vendor/ars/commands/ars-revision.md +10 -0
- package/vendor/ars/commands/ars-unmark-read.md +16 -0
- package/vendor/ars/deep-research/SKILL.md +600 -0
- package/vendor/ars/deep-research/agents/bibliography_agent.md +473 -0
- package/vendor/ars/deep-research/agents/devils_advocate_agent.md +192 -0
- package/vendor/ars/deep-research/agents/editor_in_chief_agent.md +167 -0
- package/vendor/ars/deep-research/agents/ethics_review_agent.md +267 -0
- package/vendor/ars/deep-research/agents/meta_analysis_agent.md +325 -0
- package/vendor/ars/deep-research/agents/monitoring_agent.md +209 -0
- package/vendor/ars/deep-research/agents/report_compiler_agent.md +341 -0
- package/vendor/ars/deep-research/agents/research_architect_agent.md +298 -0
- package/vendor/ars/deep-research/agents/research_question_agent.md +216 -0
- package/vendor/ars/deep-research/agents/risk_of_bias_agent.md +231 -0
- package/vendor/ars/deep-research/agents/socratic_mentor_agent.md +764 -0
- package/vendor/ars/deep-research/agents/source_verification_agent.md +219 -0
- package/vendor/ars/deep-research/agents/synthesis_agent.md +356 -0
- package/vendor/ars/deep-research/agents/timeline_extraction_agent.md +99 -0
- package/vendor/ars/deep-research/examples/exploratory_research.md +157 -0
- package/vendor/ars/deep-research/examples/fact_check_mode.md +173 -0
- package/vendor/ars/deep-research/examples/handoff_to_paper.md +318 -0
- package/vendor/ars/deep-research/examples/idea_diversity_coverage_gap_advisory.md +64 -0
- package/vendor/ars/deep-research/examples/policy_analysis.md +161 -0
- package/vendor/ars/deep-research/examples/review_mode.md +253 -0
- package/vendor/ars/deep-research/examples/socratic_guided_research.md +331 -0
- package/vendor/ars/deep-research/examples/systematic_review.md +133 -0
- package/vendor/ars/deep-research/references/apa7_style_guide.md +162 -0
- package/vendor/ars/deep-research/references/argumentation_reasoning_framework.md +68 -0
- package/vendor/ars/deep-research/references/arxiv_api_protocol.md +76 -0
- package/vendor/ars/deep-research/references/changelog.md +22 -0
- package/vendor/ars/deep-research/references/chinese_literature_api_protocol.md +317 -0
- package/vendor/ars/deep-research/references/cross_agent_quality_definitions.md +14 -0
- package/vendor/ars/deep-research/references/crossref_api_protocol.md +84 -0
- package/vendor/ars/deep-research/references/equator_reporting_guidelines.md +482 -0
- package/vendor/ars/deep-research/references/ethics_checklist.md +282 -0
- package/vendor/ars/deep-research/references/failure_paths.md +355 -0
- package/vendor/ars/deep-research/references/interdisciplinary_bridges.md +292 -0
- package/vendor/ars/deep-research/references/irb_decision_tree.md +315 -0
- package/vendor/ars/deep-research/references/literature_monitoring_strategies.md +263 -0
- package/vendor/ars/deep-research/references/logical_fallacies.md +192 -0
- package/vendor/ars/deep-research/references/methodology_patterns.md +462 -0
- package/vendor/ars/deep-research/references/mode_selection_guide.md +331 -0
- package/vendor/ars/deep-research/references/openalex_api_protocol.md +82 -0
- package/vendor/ars/deep-research/references/preregistration_guide.md +324 -0
- package/vendor/ars/deep-research/references/semantic_scholar_api_protocol.md +107 -0
- package/vendor/ars/deep-research/references/socratic_mode_protocol.md +99 -0
- package/vendor/ars/deep-research/references/socratic_questioning_framework.md +232 -0
- package/vendor/ars/deep-research/references/source_quality_hierarchy.md +188 -0
- package/vendor/ars/deep-research/references/systematic_review_protocol.md +95 -0
- package/vendor/ars/deep-research/references/systematic_review_toolkit.md +353 -0
- package/vendor/ars/deep-research/templates/evidence_assessment_template.md +127 -0
- package/vendor/ars/deep-research/templates/literature_matrix_template.md +85 -0
- package/vendor/ars/deep-research/templates/preregistration_template.md +318 -0
- package/vendor/ars/deep-research/templates/prisma_protocol_template.md +248 -0
- package/vendor/ars/deep-research/templates/prisma_report_template.md +415 -0
- package/vendor/ars/deep-research/templates/research_brief_template.md +93 -0
- package/vendor/ars/package.json +24 -0
- package/vendor/ars/pi/README.md +161 -0
- package/vendor/ars/pi/package.json +26 -0
- package/vendor/ars/pi/wrapper.js +193 -0
- package/vendor/ars/pi/wrapper.test.mjs +201 -0
- package/vendor/ars/pyproject.toml +2 -0
- package/vendor/ars/requirements-pdf-content-classifier.txt +5 -0
- package/vendor/ars/scripts/_block_parser.py +396 -0
- package/vendor/ars/scripts/_ci_pytest_manifest.toml +661 -0
- package/vendor/ars/scripts/_claim_audit_constants.py +268 -0
- package/vendor/ars/scripts/_e4_evidence.py +110 -0
- package/vendor/ars/scripts/_eval_threshold_gate.py +73 -0
- package/vendor/ars/scripts/_markdown_lint_util.py +224 -0
- package/vendor/ars/scripts/_next_verified_at_ms.py +176 -0
- package/vendor/ars/scripts/_passport_yaml.py +53 -0
- package/vendor/ars/scripts/_skill_lint.py +254 -0
- package/vendor/ars/scripts/_text_similarity.py +141 -0
- package/vendor/ars/scripts/adapters/README.md +89 -0
- package/vendor/ars/scripts/adapters/_common.py +209 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/expected_passport.yaml +25 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/expected_rejection_log.yaml +18 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Chen2024_AIAssessment.pdf +0 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Wang_2023_formative_feedback.pdf +0 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/paper1.pdf +0 -0
- package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture//344/270/255/346/226/207/346/252/224/345/220/215_2024.pdf +0 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/expected_passport.yaml +40 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/expected_rejection_log.yaml +13 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.gitkeep +0 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.obsidian/app.json +1 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/_templates/tmpl.md +7 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/chen2024ai.md +13 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/invalid.md +1 -0
- package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/wang2023formative.md +10 -0
- package/vendor/ars/scripts/adapters/examples/zotero/expected_passport.yaml +34 -0
- package/vendor/ars/scripts/adapters/examples/zotero/expected_rejection_log.yaml +34 -0
- package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/.gitkeep +0 -0
- package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/export.json +43 -0
- package/vendor/ars/scripts/adapters/folder_scan.py +214 -0
- package/vendor/ars/scripts/adapters/obsidian.py +336 -0
- package/vendor/ars/scripts/adapters/tests/.gitkeep +0 -0
- package/vendor/ars/scripts/adapters/tests/conftest.py +77 -0
- package/vendor/ars/scripts/adapters/tests/test_check_corpus_consumer_protocol.py +632 -0
- package/vendor/ars/scripts/adapters/tests/test_check_literature_corpus_schema.py +631 -0
- package/vendor/ars/scripts/adapters/tests/test_common.py +365 -0
- package/vendor/ars/scripts/adapters/tests/test_conftest.py +96 -0
- package/vendor/ars/scripts/adapters/tests/test_folder_scan.py +260 -0
- package/vendor/ars/scripts/adapters/tests/test_literature_corpus_entry_schema.py +745 -0
- package/vendor/ars/scripts/adapters/tests/test_obsidian.py +357 -0
- package/vendor/ars/scripts/adapters/tests/test_rejection_log_schema.py +271 -0
- package/vendor/ars/scripts/adapters/tests/test_sync_adapter_docs.py +88 -0
- package/vendor/ars/scripts/adapters/tests/test_zotero.py +454 -0
- package/vendor/ars/scripts/adapters/zotero.py +318 -0
- package/vendor/ars/scripts/adjudication_activity.py +1592 -0
- package/vendor/ars/scripts/announce-ars-loaded.sh +144 -0
- package/vendor/ars/scripts/ars_anchorize_draft.py +170 -0
- package/vendor/ars/scripts/ars_apply_revision_patch.py +912 -0
- package/vendor/ars/scripts/ars_cache_invalidate.py +40 -0
- package/vendor/ars/scripts/ars_mark_read.py +521 -0
- package/vendor/ars/scripts/ars_phase_scope_manifest.json +33 -0
- package/vendor/ars/scripts/ars_update_check.sh +215 -0
- package/vendor/ars/scripts/ars_write_scope_guard.py +506 -0
- package/vendor/ars/scripts/arxiv_client.py +222 -0
- package/vendor/ars/scripts/audit_snapshot.py +572 -0
- package/vendor/ars/scripts/bibliographic_integrity_signals.py +800 -0
- package/vendor/ars/scripts/bootstrap_timeline_yaml.py +146 -0
- package/vendor/ars/scripts/build_claim_standing_candidate_ledger.py +1238 -0
- package/vendor/ars/scripts/build_claim_standing_query_plan.py +643 -0
- package/vendor/ars/scripts/build_content_coverage_advisory.py +1205 -0
- package/vendor/ars/scripts/build_cross_document_consistency_advisory.py +2332 -0
- package/vendor/ars/scripts/build_review_pathway_rule_trace.py +830 -0
- package/vendor/ars/scripts/build_submission_packet_manifest.py +2310 -0
- package/vendor/ars/scripts/check_215_field_norm.py +173 -0
- package/vendor/ars/scripts/check_216_surface_form.py +250 -0
- package/vendor/ars/scripts/check_268_nested_commitment_ledger.py +180 -0
- package/vendor/ars/scripts/check_390_revision_patch_discipline.py +296 -0
- package/vendor/ars/scripts/check_392_citation_verification_intake.py +126 -0
- package/vendor/ars/scripts/check_394_submission_policy.py +178 -0
- package/vendor/ars/scripts/check_439_format_profile.py +307 -0
- package/vendor/ars/scripts/check_619_disclosure_closeout.py +237 -0
- package/vendor/ars/scripts/check_630_codex_subscription_transport.py +456 -0
- package/vendor/ars/scripts/check_669_review_pathway_rule_trace.py +662 -0
- package/vendor/ars/scripts/check_670_revision_roadmap_integration.py +518 -0
- package/vendor/ars/scripts/check_673_adjudication_activity.py +684 -0
- package/vendor/ars/scripts/check_684_review_criteria_binding.py +557 -0
- package/vendor/ars/scripts/check_agents_mirror_sync.py +115 -0
- package/vendor/ars/scripts/check_audit_artifact_consistency.py +2313 -0
- package/vendor/ars/scripts/check_benchmark_report.py +79 -0
- package/vendor/ars/scripts/check_bibliographic_integrity_signals.py +831 -0
- package/vendor/ars/scripts/check_calibration_tiers.py +235 -0
- package/vendor/ars/scripts/check_changelog_covers_merges.py +289 -0
- package/vendor/ars/scripts/check_ci_pytest_manifest.py +204 -0
- package/vendor/ars/scripts/check_claim_audit_consistency.py +1664 -0
- package/vendor/ars/scripts/check_claim_standing_candidate_ledger_integration.py +500 -0
- package/vendor/ars/scripts/check_claim_standing_freshness.py +253 -0
- package/vendor/ars/scripts/check_claim_standing_transmissions.py +449 -0
- package/vendor/ars/scripts/check_collaboration_depth_rubric.py +180 -0
- package/vendor/ars/scripts/check_command_frontmatter_name.py +116 -0
- package/vendor/ars/scripts/check_committee_correspondence.py +333 -0
- package/vendor/ars/scripts/check_compliance_report.py +108 -0
- package/vendor/ars/scripts/check_content_coverage_advisory_integration.py +796 -0
- package/vendor/ars/scripts/check_control_availability.py +172 -0
- package/vendor/ars/scripts/check_corpus_consumer_protocol.py +404 -0
- package/vendor/ars/scripts/check_cross_document_consistency_advisory_integration.py +1191 -0
- package/vendor/ars/scripts/check_cross_model_handoff_contract.py +234 -0
- package/vendor/ars/scripts/check_cross_model_verification_sync.py +261 -0
- package/vendor/ars/scripts/check_data_access_level.py +131 -0
- package/vendor/ars/scripts/check_data_flows.py +252 -0
- package/vendor/ars/scripts/check_decision_contract.py +464 -0
- package/vendor/ars/scripts/check_degradation_registry.py +326 -0
- package/vendor/ars/scripts/check_distribution_surface_claims.py +226 -0
- package/vendor/ars/scripts/check_domain_evidence_profile.py +538 -0
- package/vendor/ars/scripts/check_e4_promotion.py +195 -0
- package/vendor/ars/scripts/check_evals_gold_set.py +279 -0
- package/vendor/ars/scripts/check_evidence_row_integration.py +396 -0
- package/vendor/ars/scripts/check_experiment_provenance.py +117 -0
- package/vendor/ars/scripts/check_field_norm_severity.py +144 -0
- package/vendor/ars/scripts/check_firm_rules_sync.py +375 -0
- package/vendor/ars/scripts/check_heldout_measurement_report.py +1179 -0
- package/vendor/ars/scripts/check_human_subjects_output_contract.py +139 -0
- package/vendor/ars/scripts/check_human_subjects_reference_migration.py +844 -0
- package/vendor/ars/scripts/check_indirect_prompt_injection_no_call.py +328 -0
- package/vendor/ars/scripts/check_instruction_data_boundary.py +236 -0
- package/vendor/ars/scripts/check_judge_prompt_version.py +125 -0
- package/vendor/ars/scripts/check_literature_corpus_schema.py +402 -0
- package/vendor/ars/scripts/check_model_tiering.py +223 -0
- package/vendor/ars/scripts/check_panel_synthesis.py +1331 -0
- package/vendor/ars/scripts/check_passport_reset_contract.py +214 -0
- package/vendor/ars/scripts/check_pattern_eval_manifest.py +422 -0
- package/vendor/ars/scripts/check_persuasion_invariance_fixtures.py +637 -0
- package/vendor/ars/scripts/check_phase_conformance.py +2180 -0
- package/vendor/ars/scripts/check_pipeline_boundary_semantics.py +600 -0
- package/vendor/ars/scripts/check_pipeline_integrity.py +340 -0
- package/vendor/ars/scripts/check_policy_anchor_protocol.py +159 -0
- package/vendor/ars/scripts/check_policy_anchor_table.py +286 -0
- package/vendor/ars/scripts/check_preprint_venues_consistency.py +123 -0
- package/vendor/ars/scripts/check_prisma_trAIce_freshness.py +84 -0
- package/vendor/ars/scripts/check_promotion_bakeoff_preregistration.py +1303 -0
- package/vendor/ars/scripts/check_ranking_lift.py +323 -0
- package/vendor/ars/scripts/check_re_review_synthesis.py +2719 -0
- package/vendor/ars/scripts/check_receipt_enum_sync.py +203 -0
- package/vendor/ars/scripts/check_repro_lock.py +85 -0
- package/vendor/ars/scripts/check_review_pathway_output.py +276 -0
- package/vendor/ars/scripts/check_reviewer_data_fences.py +224 -0
- package/vendor/ars/scripts/check_reviewer_finding_contract.py +783 -0
- package/vendor/ars/scripts/check_reviewer_role_label.py +447 -0
- package/vendor/ars/scripts/check_reviewer_scoring_honesty.py +292 -0
- package/vendor/ars/scripts/check_reviewer_sprint_prompt_sync.py +409 -0
- package/vendor/ars/scripts/check_revision_claim_drift_suite_v2.py +1622 -0
- package/vendor/ars/scripts/check_revision_token_conservation.py +263 -0
- package/vendor/ars/scripts/check_risk_register.py +280 -0
- package/vendor/ars/scripts/check_role_scoped_contract.py +702 -0
- package/vendor/ars/scripts/check_rq_framing_patterns.py +184 -0
- package/vendor/ars/scripts/check_rubric_weight_consistency.py +32 -0
- package/vendor/ars/scripts/check_seeded_defect_fixtures.py +425 -0
- package/vendor/ars/scripts/check_setup_cross_model_parity.py +139 -0
- package/vendor/ars/scripts/check_spec_consistency.py +1249 -0
- package/vendor/ars/scripts/check_sprint_contract.py +371 -0
- package/vendor/ars/scripts/check_stage_capability_matrix.py +784 -0
- package/vendor/ars/scripts/check_submission_packet_manifest_integration.py +358 -0
- package/vendor/ars/scripts/check_surface_form_parity.py +445 -0
- package/vendor/ars/scripts/check_task_type.py +22 -0
- package/vendor/ars/scripts/check_tools_allowlist.py +538 -0
- package/vendor/ars/scripts/check_tortured_phrase_screening_integration.py +1974 -0
- package/vendor/ars/scripts/check_v3_10_134_write_scope.py +286 -0
- package/vendor/ars/scripts/check_v3_10_policy.py +656 -0
- package/vendor/ars/scripts/check_v3_6_6_ab_manifest.py +364 -0
- package/vendor/ars/scripts/check_v3_6_7_pattern_protection.py +1366 -0
- package/vendor/ars/scripts/check_v3_6_8_audit_scope_block.py +464 -0
- package/vendor/ars/scripts/check_v3_6_8_cite_provenance_pipeline.py +231 -0
- package/vendor/ars/scripts/check_v3_6_8_frontmatter_trust_schema.py +248 -0
- package/vendor/ars/scripts/check_v3_6_8_mark_read_commands.py +79 -0
- package/vendor/ars/scripts/check_v3_6_8_pattern_protection.py +941 -0
- package/vendor/ars/scripts/check_v3_7_3_three_layer_citation.py +318 -0
- package/vendor/ars/scripts/check_v3_8_annotation_literal_sync.py +228 -0
- package/vendor/ars/scripts/check_v3_9_0_triangulation.py +366 -0
- package/vendor/ars/scripts/check_v3_9_2_phase_boundary.py +270 -0
- package/vendor/ars/scripts/check_v3_9_4_temporal_verification.py +139 -0
- package/vendor/ars/scripts/check_venue_disclosure_policies.py +63 -0
- package/vendor/ars/scripts/check_version_consistency.py +826 -0
- package/vendor/ars/scripts/check_workflow_classification.py +223 -0
- package/vendor/ars/scripts/chinese_literature_client.py +1938 -0
- package/vendor/ars/scripts/citation_verification_summary.py +85 -0
- package/vendor/ars/scripts/claim_audit_calibration.py +517 -0
- package/vendor/ars/scripts/claim_audit_finalizer.py +456 -0
- package/vendor/ars/scripts/claim_audit_pipeline.py +1594 -0
- package/vendor/ars/scripts/claim_registry_coverage.py +493 -0
- package/vendor/ars/scripts/claim_standing_discovery.py +784 -0
- package/vendor/ars/scripts/claim_standing_stance_runner.py +758 -0
- package/vendor/ars/scripts/claim_standing_stance_scorer.py +239 -0
- package/vendor/ars/scripts/claim_strength_drift_disposition.py +666 -0
- package/vendor/ars/scripts/contamination_signals.py +689 -0
- package/vendor/ars/scripts/corpus_consumer_manifest.json +19 -0
- package/vendor/ars/scripts/cross_model_codex_transport.py +1374 -0
- package/vendor/ars/scripts/cross_model_codex_verify.sh +6 -0
- package/vendor/ars/scripts/cross_model_handoff.py +359 -0
- package/vendor/ars/scripts/cross_model_smoke_test.sh +183 -0
- package/vendor/ars/scripts/cross_model_smoke_test_codex.sh +35 -0
- package/vendor/ars/scripts/cross_model_verification/gemini_is_grounded.jq +45 -0
- package/vendor/ars/scripts/cross_model_verification/gemini_sources.jq +45 -0
- package/vendor/ars/scripts/cross_model_verification/normalize_compat_verdict.py +57 -0
- package/vendor/ars/scripts/cross_model_verification/openai_has_completed_web_search.jq +15 -0
- package/vendor/ars/scripts/cross_model_verification/openai_sources.jq +17 -0
- package/vendor/ars/scripts/cross_model_verification/openai_text.jq +11 -0
- package/vendor/ars/scripts/crossref_client.py +225 -0
- package/vendor/ars/scripts/dispatch_e4_panel.py +2731 -0
- package/vendor/ars/scripts/evidence_rows.py +2043 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3.json +32 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_input.json +27 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime.json +32 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime_input.json +27 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override.json +50 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override_action.json +14 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_pass.json +44 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/explicit_user_request_log.json +26 -0
- package/vendor/ars/scripts/fixtures/adjudication_activity/mandatory_checkpoint_log.json +116 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/README.md +56 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +26 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
- package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.verdict.yaml +12 -0
- package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction.json +46 -0
- package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction_check_attestation.json +44 -0
- package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase.json +44 -0
- package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_abstract_missing.json +107 -0
- package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_detected.json +124 -0
- package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/expected_outcomes.json +77 -0
- package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/manifest.yaml +37 -0
- package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/001-valid-doi-test.json +20 -0
- package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/002-valid-arxiv-test.json +19 -0
- package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/003-fabricated-test.json +20 -0
- package/vendor/ars/scripts/fixtures/claim_audit_calibration/gold_set.json +344 -0
- package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/query_plan.json +108 -0
- package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/retrieval_input.json +179 -0
- package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/concern_tracker.json +129 -0
- package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/response_skeleton.md +19 -0
- package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/source_letter.txt +9 -0
- package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_draft.json +48 -0
- package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_inventory.json +38 -0
- package/vendor/ars/scripts/fixtures/content_coverage_advisory/packet/consent.txt +1 -0
- package/vendor/ars/scripts/fixtures/content_coverage_advisory/session_sources.json +3 -0
- package/vendor/ars/scripts/fixtures/cross_document_consistency/README.md +6 -0
- package/vendor/ars/scripts/fixtures/cross_document_consistency/accepted_draft.md +35 -0
- package/vendor/ars/scripts/fixtures/cross_document_consistency/cases.json +75 -0
- package/vendor/ars/scripts/fixtures/cross_document_consistency/preregistration.md +5 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/forbidden_event.jsonl +4 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/grounded_verified.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/malformed.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/missing_search.jsonl +2 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/multiple_finals.jsonl +4 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/not_found.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/unbound_source.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/cross_model_codex_transport/wrong_search_shape.jsonl +3 -0
- package/vendor/ars/scripts/fixtures/evidence_rows/phase_e_inputs.json +114 -0
- package/vendor/ars/scripts/fixtures/evidence_rows/session_sources.json +5 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/cross-border-us-tw-gdpr.json +71 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/gdpr-member-state-unresolved.json +62 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/missing-data-axis.json +45 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/no-profile.json +33 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/tw-gdpr-two-axis.json +74 -0
- package/vendor/ars/scripts/fixtures/human_subjects_authority/us-gdpr-two-axis.json +74 -0
- package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/lint-near-misses.json +72 -0
- package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/no-profile-request.json +18 -0
- package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/tw-candidates-request.json +63 -0
- package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/us-candidates-request.json +77 -0
- package/vendor/ars/scripts/fixtures/review_target_context/exact-declaration.json +24 -0
- package/vendor/ars/scripts/fixtures/review_target_context/field-general-declaration.json +24 -0
- package/vendor/ars/scripts/fixtures/review_target_context/msr-2027-technical-full-declaration.json +24 -0
- package/vendor/ars/scripts/fixtures/review_target_context/synthetic-registry.json +92 -0
- package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/README.md +21 -0
- package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_attested_only.json +55 -0
- package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_machine_supported.json +49 -0
- package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_not_isolated.json +56 -0
- package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_unknown.json +49 -0
- package/vendor/ars/scripts/fixtures/submission_package/clean/paper.md +16 -0
- package/vendor/ars/scripts/fixtures/submission_package/clean/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/paper.md +11 -0
- package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/paper.tex +9 -0
- package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_package/marker_no_join/paper.md +7 -0
- package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/paper.md +8 -0
- package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/references.bib +6 -0
- package/vendor/ars/scripts/fixtures/submission_package/passports/corpus_only.yaml +9 -0
- package/vendor/ars/scripts/fixtures/submission_package/passports/summary_join.yaml +16 -0
- package/vendor/ars/scripts/fixtures/submission_package/profiles/full.yaml +15 -0
- package/vendor/ars/scripts/fixtures/submission_package/profiles/tight.yaml +15 -0
- package/vendor/ars/scripts/fixtures/submission_package/summary_join/paper.md +7 -0
- package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/paper.md +9 -0
- package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_package/venue_clean/paper.md +26 -0
- package/vendor/ars/scripts/fixtures/submission_package/venue_clean/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_package/venue_violations/paper.md +20 -0
- package/vendor/ars/scripts/fixtures/submission_package/venue_violations/references.bib +13 -0
- package/vendor/ars/scripts/fixtures/submission_packet_manifest/base_inventory.json +101 -0
- package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/consent-materials.txt +3 -0
- package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/training-certificate.txt +2 -0
- package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/tw-consent-materials.txt +3 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/corpus_input.yaml +30 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.md +36 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.tex +21 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/seed_expectations.json +218 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot.json +164 -0
- package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot_manifest.json +29 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/README.md +55 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/empty_feed.xml +10 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/error_5xx.html +7 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/id_hit.xml +26 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/README.md +69 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/cnki_landing_page.html +13 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/error_5xx.html +2 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_ambiguous.json +16 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_hit.json +16 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_zero.json +14 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_hit.json +15 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_zero.json +13 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_hit.json +43 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_issn_mismatch.json +31 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_no_doi.json +31 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_unknown_ra.json +34 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_year_mismatch.json +31 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_absent.json +4 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_exists.json +16 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_internal_error.json +4 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_hit.json +25 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_other_title.json +18 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_cnki.json +6 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_crossref.json +6 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_istic.json +6 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_unknown_prefix.json +6 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/crossref/doi_hit.json +24 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/crossref/error_5xx.html +7 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/crossref/title_search_miss.json +15 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/openalex/doi_hit.json +29 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/openalex/error_5xx.json +4 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/openalex/title_search_miss.json +11 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/doi_hit.json +14 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/error_5xx.json +3 -0
- package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/title_search_miss.json +5 -0
- package/vendor/ars/scripts/human_read_attestation_resolver.py +467 -0
- package/vendor/ars/scripts/ideation_diversity_assignment_gate.py +430 -0
- package/vendor/ars/scripts/inquiry_branch_ledger.py +2540 -0
- package/vendor/ars/scripts/legacy/ars_apply_revision_patch_v1_0.py +715 -0
- package/vendor/ars/scripts/legacy/check_re_review_synthesis_v1_0.py +2215 -0
- package/vendor/ars/scripts/migrate_literature_corpus_to_v3_10.py +223 -0
- package/vendor/ars/scripts/migrate_literature_corpus_to_v3_7_3.py +277 -0
- package/vendor/ars/scripts/migrate_literature_corpus_to_v3_9_0.py +304 -0
- package/vendor/ars/scripts/model_tiering_manifest.json +46 -0
- package/vendor/ars/scripts/openalex_client.py +232 -0
- package/vendor/ars/scripts/parse_audit_verdict.py +802 -0
- package/vendor/ars/scripts/pdf_content_classifier_worker.py +176 -0
- package/vendor/ars/scripts/pdf_read_preflight.py +1455 -0
- package/vendor/ars/scripts/policy_anchor_disclosure_referee.py +354 -0
- package/vendor/ars/scripts/recompute_receipts.py +1414 -0
- package/vendor/ars/scripts/render_claim_standing_view.py +374 -0
- package/vendor/ars/scripts/render_eval_comment.py +130 -0
- package/vendor/ars/scripts/render_harness_retirement_issue.py +167 -0
- package/vendor/ars/scripts/repro_lock_validation.py +90 -0
- package/vendor/ars/scripts/research_workflow_profile.py +1079 -0
- package/vendor/ars/scripts/resolve_human_subjects_authority.py +1158 -0
- package/vendor/ars/scripts/resolve_review_target_context.py +683 -0
- package/vendor/ars/scripts/resume_e4_record.py +509 -0
- package/vendor/ars/scripts/retraction_status.py +484 -0
- package/vendor/ars/scripts/review_criteria_binding.py +889 -0
- package/vendor/ars/scripts/review_panel_provenance.py +744 -0
- package/vendor/ars/scripts/revision_roadmap.py +1967 -0
- package/vendor/ars/scripts/run_ci_pytest_manifest.py +116 -0
- package/vendor/ars/scripts/run_codex_audit.sh +1191 -0
- package/vendor/ars/scripts/run_evals.py +513 -0
- package/vendor/ars/scripts/run_ideation_diversity_no_call.py +3505 -0
- package/vendor/ars/scripts/run_indirect_prompt_injection_no_call.py +3330 -0
- package/vendor/ars/scripts/run_indirect_prompt_injection_probe.py +399 -0
- package/vendor/ars/scripts/run_review_criteria_constructive_value.py +1895 -0
- package/vendor/ars/scripts/run_role_topology_utility_dry_run.py +606 -0
- package/vendor/ars/scripts/score_review_criteria_constructive_value.py +617 -0
- package/vendor/ars/scripts/semantic_scholar_client.py +291 -0
- package/vendor/ars/scripts/slr_lineage.py +59 -0
- package/vendor/ars/scripts/sync_adapter_docs.py +118 -0
- package/vendor/ars/scripts/temporal_integrity_audit.py +840 -0
- package/vendor/ars/scripts/test_431_exact_or_bust.py +253 -0
- package/vendor/ars/scripts/test__eval_threshold_gate.py +125 -0
- package/vendor/ars/scripts/test__markdown_lint_util.py +129 -0
- package/vendor/ars/scripts/test__next_verified_at_ms.py +256 -0
- package/vendor/ars/scripts/test_adjacent_framing_probe_lint.py +171 -0
- package/vendor/ars/scripts/test_adjudication_activity.py +1515 -0
- package/vendor/ars/scripts/test_ars_anchorize_draft.py +178 -0
- package/vendor/ars/scripts/test_ars_apply_revision_patch.py +1335 -0
- package/vendor/ars/scripts/test_ars_cache_invalidate.py +51 -0
- package/vendor/ars/scripts/test_ars_mark_read.py +910 -0
- package/vendor/ars/scripts/test_ars_update_check.py +816 -0
- package/vendor/ars/scripts/test_ars_write_scope_guard.py +790 -0
- package/vendor/ars/scripts/test_arxiv_client.py +374 -0
- package/vendor/ars/scripts/test_audit_schemas.py +560 -0
- package/vendor/ars/scripts/test_audit_snapshot_render_section_0.py +105 -0
- package/vendor/ars/scripts/test_block_parser.py +259 -0
- package/vendor/ars/scripts/test_bootstrap_timeline_yaml.py +148 -0
- package/vendor/ars/scripts/test_build_claim_standing_candidate_ledger.py +1203 -0
- package/vendor/ars/scripts/test_build_claim_standing_query_plan.py +791 -0
- package/vendor/ars/scripts/test_build_submission_packet_manifest.py +2644 -0
- package/vendor/ars/scripts/test_check_215_field_norm.py +238 -0
- package/vendor/ars/scripts/test_check_216_surface_form.py +341 -0
- package/vendor/ars/scripts/test_check_268_nested_commitment_ledger.py +194 -0
- package/vendor/ars/scripts/test_check_390_revision_patch_discipline.py +279 -0
- package/vendor/ars/scripts/test_check_392_citation_verification_intake.py +144 -0
- package/vendor/ars/scripts/test_check_394_submission_policy.py +200 -0
- package/vendor/ars/scripts/test_check_439_format_profile.py +252 -0
- package/vendor/ars/scripts/test_check_619_disclosure_closeout.py +158 -0
- package/vendor/ars/scripts/test_check_630_codex_subscription_transport.py +245 -0
- package/vendor/ars/scripts/test_check_669_review_pathway_rule_trace.py +304 -0
- package/vendor/ars/scripts/test_check_670_revision_roadmap_integration.py +260 -0
- package/vendor/ars/scripts/test_check_673_adjudication_activity.py +367 -0
- package/vendor/ars/scripts/test_check_684_review_criteria_binding.py +274 -0
- package/vendor/ars/scripts/test_check_agents_mirror_sync.py +137 -0
- package/vendor/ars/scripts/test_check_audit_artifact_consistency.py +2133 -0
- package/vendor/ars/scripts/test_check_benchmark_report.py +117 -0
- package/vendor/ars/scripts/test_check_bibliographic_integrity_signals.py +609 -0
- package/vendor/ars/scripts/test_check_calibration_tiers.py +376 -0
- package/vendor/ars/scripts/test_check_changelog_covers_merges.py +509 -0
- package/vendor/ars/scripts/test_check_ci_pytest_manifest.py +425 -0
- package/vendor/ars/scripts/test_check_claim_standing_candidate_ledger_integration.py +256 -0
- package/vendor/ars/scripts/test_check_claim_standing_freshness.py +277 -0
- package/vendor/ars/scripts/test_check_collaboration_depth_rubric.py +239 -0
- package/vendor/ars/scripts/test_check_command_frontmatter_name.py +178 -0
- package/vendor/ars/scripts/test_check_committee_correspondence.py +298 -0
- package/vendor/ars/scripts/test_check_compliance_report.py +381 -0
- package/vendor/ars/scripts/test_check_content_coverage_advisory_integration.py +308 -0
- package/vendor/ars/scripts/test_check_control_availability.py +361 -0
- package/vendor/ars/scripts/test_check_cross_document_consistency_advisory_integration.py +698 -0
- package/vendor/ars/scripts/test_check_cross_model_handoff_contract.py +301 -0
- package/vendor/ars/scripts/test_check_cross_model_verification_sync.py +203 -0
- package/vendor/ars/scripts/test_check_data_access_level.py +227 -0
- package/vendor/ars/scripts/test_check_data_flows.py +382 -0
- package/vendor/ars/scripts/test_check_decision_contract.py +387 -0
- package/vendor/ars/scripts/test_check_degradation_registry.py +270 -0
- package/vendor/ars/scripts/test_check_distribution_surface_claims.py +225 -0
- package/vendor/ars/scripts/test_check_domain_evidence_profile.py +439 -0
- package/vendor/ars/scripts/test_check_e4_promotion.py +163 -0
- package/vendor/ars/scripts/test_check_evals_gold_set.py +312 -0
- package/vendor/ars/scripts/test_check_evidence_row_integration.py +187 -0
- package/vendor/ars/scripts/test_check_field_norm_severity.py +173 -0
- package/vendor/ars/scripts/test_check_firm_rules_sync.py +342 -0
- package/vendor/ars/scripts/test_check_heldout_measurement_report.py +1508 -0
- package/vendor/ars/scripts/test_check_human_subjects_output_contract.py +129 -0
- package/vendor/ars/scripts/test_check_human_subjects_reference_migration.py +756 -0
- package/vendor/ars/scripts/test_check_instruction_data_boundary.py +204 -0
- package/vendor/ars/scripts/test_check_judge_prompt_version.py +90 -0
- package/vendor/ars/scripts/test_check_model_tiering.py +236 -0
- package/vendor/ars/scripts/test_check_panel_synthesis.py +1658 -0
- package/vendor/ars/scripts/test_check_passport_reset_contract.py +249 -0
- package/vendor/ars/scripts/test_check_pattern_eval_manifest.py +381 -0
- package/vendor/ars/scripts/test_check_persuasion_invariance_fixtures.py +591 -0
- package/vendor/ars/scripts/test_check_phase_conformance.py +4366 -0
- package/vendor/ars/scripts/test_check_pipeline_boundary_semantics.py +931 -0
- package/vendor/ars/scripts/test_check_pipeline_integrity.py +243 -0
- package/vendor/ars/scripts/test_check_policy_anchor_protocol.py +236 -0
- package/vendor/ars/scripts/test_check_policy_anchor_table.py +295 -0
- package/vendor/ars/scripts/test_check_prisma_trAIce_freshness.py +68 -0
- package/vendor/ars/scripts/test_check_promotion_bakeoff_preregistration.py +799 -0
- package/vendor/ars/scripts/test_check_ranking_lift.py +377 -0
- package/vendor/ars/scripts/test_check_re_review_synthesis.py +3398 -0
- package/vendor/ars/scripts/test_check_receipt_enum_sync.py +176 -0
- package/vendor/ars/scripts/test_check_repro_lock.py +107 -0
- package/vendor/ars/scripts/test_check_reviewer_data_fences.py +334 -0
- package/vendor/ars/scripts/test_check_reviewer_finding_contract.py +1107 -0
- package/vendor/ars/scripts/test_check_reviewer_role_label.py +491 -0
- package/vendor/ars/scripts/test_check_reviewer_scoring_honesty.py +222 -0
- package/vendor/ars/scripts/test_check_reviewer_sprint_prompt_sync.py +373 -0
- package/vendor/ars/scripts/test_check_revision_claim_drift_suite_v2.py +1925 -0
- package/vendor/ars/scripts/test_check_revision_token_conservation.py +398 -0
- package/vendor/ars/scripts/test_check_risk_register.py +336 -0
- package/vendor/ars/scripts/test_check_role_scoped_contract.py +1104 -0
- package/vendor/ars/scripts/test_check_rq_framing_patterns.py +110 -0
- package/vendor/ars/scripts/test_check_rubric_weight_consistency.py +32 -0
- package/vendor/ars/scripts/test_check_seeded_defect_fixtures.py +392 -0
- package/vendor/ars/scripts/test_check_setup_cross_model_parity.py +121 -0
- package/vendor/ars/scripts/test_check_spec_consistency.py +1081 -0
- package/vendor/ars/scripts/test_check_sprint_contract.py +458 -0
- package/vendor/ars/scripts/test_check_stage_capability_matrix.py +698 -0
- package/vendor/ars/scripts/test_check_submission_packet_manifest_integration.py +177 -0
- package/vendor/ars/scripts/test_check_surface_form_parity.py +417 -0
- package/vendor/ars/scripts/test_check_task_type.py +116 -0
- package/vendor/ars/scripts/test_check_tools_allowlist.py +835 -0
- package/vendor/ars/scripts/test_check_tortured_phrase_screening_integration.py +1306 -0
- package/vendor/ars/scripts/test_check_v3_10_134_write_scope.py +251 -0
- package/vendor/ars/scripts/test_check_v3_10_policy.py +547 -0
- package/vendor/ars/scripts/test_check_v3_6_7_pattern_protection.py +960 -0
- package/vendor/ars/scripts/test_check_v3_6_8_audit_scope_block.py +1000 -0
- package/vendor/ars/scripts/test_check_v3_6_8_cite_provenance_pipeline.py +454 -0
- package/vendor/ars/scripts/test_check_v3_6_8_frontmatter_trust_schema.py +581 -0
- package/vendor/ars/scripts/test_check_v3_6_8_mark_read_commands.py +120 -0
- package/vendor/ars/scripts/test_check_v3_6_8_pattern_protection.py +1138 -0
- package/vendor/ars/scripts/test_check_v3_7_3_three_layer_citation.py +566 -0
- package/vendor/ars/scripts/test_check_v3_8_annotation_literal_sync.py +262 -0
- package/vendor/ars/scripts/test_check_v3_9_0_triangulation.py +321 -0
- package/vendor/ars/scripts/test_check_v3_9_2_phase_boundary.py +190 -0
- package/vendor/ars/scripts/test_check_v3_9_4_temporal_verification.py +471 -0
- package/vendor/ars/scripts/test_check_version_consistency.py +1462 -0
- package/vendor/ars/scripts/test_check_workflow_classification.py +225 -0
- package/vendor/ars/scripts/test_chinese_literature_client.py +1889 -0
- package/vendor/ars/scripts/test_citation_existence_policy.py +480 -0
- package/vendor/ars/scripts/test_citation_verification_summary.py +342 -0
- package/vendor/ars/scripts/test_claim_audit_calibration.py +882 -0
- package/vendor/ars/scripts/test_claim_audit_finalizer.py +979 -0
- package/vendor/ars/scripts/test_claim_audit_pipeline.py +2398 -0
- package/vendor/ars/scripts/test_claim_audit_schema.py +1778 -0
- package/vendor/ars/scripts/test_claim_intent_manifest.py +666 -0
- package/vendor/ars/scripts/test_claim_registry_coverage.py +246 -0
- package/vendor/ars/scripts/test_claim_standing_discovery.py +579 -0
- package/vendor/ars/scripts/test_claim_standing_pipeline_wiring.py +290 -0
- package/vendor/ars/scripts/test_claim_standing_stance_assets.py +399 -0
- package/vendor/ars/scripts/test_claim_standing_stance_contracts.py +383 -0
- package/vendor/ars/scripts/test_claim_standing_stance_runner.py +349 -0
- package/vendor/ars/scripts/test_claim_standing_transmissions.py +605 -0
- package/vendor/ars/scripts/test_claim_strength_drift_disposition.py +517 -0
- package/vendor/ars/scripts/test_claim_verification_coverage_contract.py +104 -0
- package/vendor/ars/scripts/test_contamination_signals.py +1086 -0
- package/vendor/ars/scripts/test_content_coverage_advisory.py +1767 -0
- package/vendor/ars/scripts/test_cross_document_consistency_advisory.py +1270 -0
- package/vendor/ars/scripts/test_cross_model_codex_transport.py +1152 -0
- package/vendor/ars/scripts/test_cross_model_handoff.py +571 -0
- package/vendor/ars/scripts/test_cross_model_verification_guards.py +792 -0
- package/vendor/ars/scripts/test_crossref_client.py +393 -0
- package/vendor/ars/scripts/test_dispatch_e4_panel.py +4235 -0
- package/vendor/ars/scripts/test_e2e_claim_audit.py +540 -0
- package/vendor/ars/scripts/test_eval_harness_workflow.py +140 -0
- package/vendor/ars/scripts/test_evals_citation_extraction.py +150 -0
- package/vendor/ars/scripts/test_evals_lift_report_schema.py +108 -0
- package/vendor/ars/scripts/test_evidence_rows.py +2593 -0
- package/vendor/ars/scripts/test_experiment_provenance.py +915 -0
- package/vendor/ars/scripts/test_human_read_attestation_resolver.py +477 -0
- package/vendor/ars/scripts/test_ideation_diversity_assignment_gate.py +615 -0
- package/vendor/ars/scripts/test_indirect_prompt_injection_behavior_probe.py +250 -0
- package/vendor/ars/scripts/test_inquiry_branch_ledger.py +2296 -0
- package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_10.py +248 -0
- package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_7_3.py +545 -0
- package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_9_0.py +497 -0
- package/vendor/ars/scripts/test_normalize_compat_verdict.py +149 -0
- package/vendor/ars/scripts/test_openalex_client.py +490 -0
- package/vendor/ars/scripts/test_passport_yaml.py +104 -0
- package/vendor/ars/scripts/test_pattern_eval_runtime.py +1295 -0
- package/vendor/ars/scripts/test_pdf_read_preflight.py +1943 -0
- package/vendor/ars/scripts/test_policy_anchor_disclosure.py +666 -0
- package/vendor/ars/scripts/test_reading_probe_lint.py +218 -0
- package/vendor/ars/scripts/test_recompute_receipts.py +778 -0
- package/vendor/ars/scripts/test_render_claim_standing_view.py +192 -0
- package/vendor/ars/scripts/test_render_eval_comment.py +162 -0
- package/vendor/ars/scripts/test_render_harness_retirement_issue.py +110 -0
- package/vendor/ars/scripts/test_repro_lock_validation_drift.py +100 -0
- package/vendor/ars/scripts/test_research_workflow_profile.py +734 -0
- package/vendor/ars/scripts/test_resolve_human_subjects_authority.py +1219 -0
- package/vendor/ars/scripts/test_resolve_review_target_context.py +703 -0
- package/vendor/ars/scripts/test_resume_e4_record.py +315 -0
- package/vendor/ars/scripts/test_retraction_status.py +456 -0
- package/vendor/ars/scripts/test_review_criteria_binding.py +629 -0
- package/vendor/ars/scripts/test_review_panel_provenance.py +565 -0
- package/vendor/ars/scripts/test_review_pathway_rule_trace.py +821 -0
- package/vendor/ars/scripts/test_revision_roadmap.py +1255 -0
- package/vendor/ars/scripts/test_run_ci_pytest_manifest.py +186 -0
- package/vendor/ars/scripts/test_run_codex_audit_e2e.py +369 -0
- package/vendor/ars/scripts/test_run_evals.py +430 -0
- package/vendor/ars/scripts/test_run_guard_launcher.py +500 -0
- package/vendor/ars/scripts/test_run_ideation_diversity_no_call.py +1833 -0
- package/vendor/ars/scripts/test_run_indirect_prompt_injection_no_call.py +1889 -0
- package/vendor/ars/scripts/test_run_review_criteria_constructive_value.py +586 -0
- package/vendor/ars/scripts/test_run_role_topology_utility_dry_run.py +428 -0
- package/vendor/ars/scripts/test_score_review_criteria_constructive_value.py +340 -0
- package/vendor/ars/scripts/test_semantic_scholar_client.py +554 -0
- package/vendor/ars/scripts/test_slr_lineage_emission.py +230 -0
- package/vendor/ars/scripts/test_socratic_rq_non_generation_contract.py +173 -0
- package/vendor/ars/scripts/test_temporal_integrity_audit.py +438 -0
- package/vendor/ars/scripts/test_text_similarity.py +95 -0
- package/vendor/ars/scripts/test_title_fuzzy_false_positive.py +111 -0
- package/vendor/ars/scripts/test_tortured_phrase_screening.py +2959 -0
- package/vendor/ars/scripts/test_transport_fixture_citation_gate.py +338 -0
- package/vendor/ars/scripts/test_uncited_assertion.py +558 -0
- package/vendor/ars/scripts/test_v3_6_7_phase_6_6.py +1279 -0
- package/vendor/ars/scripts/test_validate_compliance_fixtures.py +36 -0
- package/vendor/ars/scripts/test_validate_ideation_diversity_assets.py +230 -0
- package/vendor/ars/scripts/test_venue_disclosure_contract.py +755 -0
- package/vendor/ars/scripts/test_verification_cache.py +280 -0
- package/vendor/ars/scripts/test_verification_gate.py +461 -0
- package/vendor/ars/scripts/test_verify_passport_cli.py +123 -0
- package/vendor/ars/scripts/test_verify_submission_package.py +1407 -0
- package/vendor/ars/scripts/test_version_records_schema.py +211 -0
- package/vendor/ars/scripts/tortured_phrase_screening.py +3502 -0
- package/vendor/ars/scripts/uncited_assertion_detector.py +254 -0
- package/vendor/ars/scripts/v3_6_7_inversion_manifest.json +9 -0
- package/vendor/ars/scripts/v3_6_8_inversion_manifest.json +10 -0
- package/vendor/ars/scripts/validate_claim_standing_stance_assets.py +249 -0
- package/vendor/ars/scripts/validate_compliance_fixtures.py +56 -0
- package/vendor/ars/scripts/validate_ideation_diversity_assets.py +303 -0
- package/vendor/ars/scripts/venue_disclosure_contract_harness.py +837 -0
- package/vendor/ars/scripts/verification_cache.py +276 -0
- package/vendor/ars/scripts/verification_gate/__init__.py +345 -0
- package/vendor/ars/scripts/verify_passport.py +133 -0
- package/vendor/ars/scripts/verify_submission_package.py +1657 -0
- package/vendor/ars/shared/agents/compliance_agent.md +136 -0
- package/vendor/ars/shared/artifact_reproducibility_pattern.md +173 -0
- package/vendor/ars/shared/benchmark_report.schema.json +81 -0
- package/vendor/ars/shared/benchmark_report_pattern.md +180 -0
- package/vendor/ars/shared/bibliographic_integrity_signals.md +142 -0
- package/vendor/ars/shared/collaboration_depth_rubric.md +154 -0
- package/vendor/ars/shared/compliance_checkpoint_protocol.md +162 -0
- package/vendor/ars/shared/compliance_report.schema.json +187 -0
- package/vendor/ars/shared/contracts/README.md +938 -0
- package/vendor/ars/shared/contracts/activity/adjudication_activity_input.schema.json +555 -0
- package/vendor/ars/shared/contracts/activity/adjudication_activity_store.schema.json +560 -0
- package/vendor/ars/shared/contracts/audit/audit_jsonl.schema.json +128 -0
- package/vendor/ars/shared/contracts/audit/audit_sidecar.schema.json +169 -0
- package/vendor/ars/shared/contracts/audit/audit_verdict.schema.json +133 -0
- package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory.schema.json +480 -0
- package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory_draft.schema.json +564 -0
- package/vendor/ars/shared/contracts/audit/cross_document_source_manifest.schema.json +202 -0
- package/vendor/ars/shared/contracts/audit/tortured_phrase_advisory.schema.json +1362 -0
- package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot.schema.json +208 -0
- package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot_manifest.schema.json +335 -0
- package/vendor/ars/shared/contracts/capability/stage_capability_matrix.json +434 -0
- package/vendor/ars/shared/contracts/claim_standing/candidate_ledger.schema.json +705 -0
- package/vendor/ars/shared/contracts/claim_standing/query_plan.schema.json +389 -0
- package/vendor/ars/shared/contracts/claim_standing/query_plan_v1_1.schema.json +782 -0
- package/vendor/ars/shared/contracts/claim_standing/retrieval_input.schema.json +440 -0
- package/vendor/ars/shared/contracts/claim_standing/stance_record.schema.json +460 -0
- package/vendor/ars/shared/contracts/claim_standing/transmission_ledger.schema.json +282 -0
- package/vendor/ars/shared/contracts/cross_model/codex_citation_receipt.schema.json +157 -0
- package/vendor/ars/shared/contracts/cross_model/codex_citation_request.schema.json +23 -0
- package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_commitment.schema.json +55 -0
- package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_reveal.schema.json +50 -0
- package/vendor/ars/shared/contracts/degradation_registry.json +428 -0
- package/vendor/ars/shared/contracts/evaluator/full.json +126 -0
- package/vendor/ars/shared/contracts/evidence/claim_registry.schema.json +50 -0
- package/vendor/ars/shared/contracts/evidence/claim_registry_coverage_report.schema.json +79 -0
- package/vendor/ars/shared/contracts/evidence/evidence_row.schema.json +504 -0
- package/vendor/ars/shared/contracts/evidence/evidence_row_v1_1.schema.json +364 -0
- package/vendor/ars/shared/contracts/evidence/evidence_row_v1_2.schema.json +714 -0
- package/vendor/ars/shared/contracts/evidence/evidence_row_v1_3.schema.json +524 -0
- package/vendor/ars/shared/contracts/human_subjects/authority_profile_registry.schema.json +497 -0
- package/vendor/ars/shared/contracts/human_subjects/committee_correspondence.schema.json +253 -0
- package/vendor/ars/shared/contracts/human_subjects/content_coverage_advisory.schema.json +620 -0
- package/vendor/ars/shared/contracts/human_subjects/irb_context_record.schema.json +329 -0
- package/vendor/ars/shared/contracts/human_subjects/resolved_authority_context.schema.json +347 -0
- package/vendor/ars/shared/contracts/human_subjects/review_pathway_rule_trace.schema.json +265 -0
- package/vendor/ars/shared/contracts/human_subjects/review_pathway_trace_request.schema.json +127 -0
- package/vendor/ars/shared/contracts/human_subjects/submission_packet_inventory.schema.json +240 -0
- package/vendor/ars/shared/contracts/human_subjects/submission_packet_manifest.schema.json +999 -0
- package/vendor/ars/shared/contracts/passport/audit_artifact_entry.schema.json +266 -0
- package/vendor/ars/shared/contracts/passport/bibliographic_integrity_signal.schema.json +1676 -0
- package/vendor/ars/shared/contracts/passport/citation_provenance.schema.json +107 -0
- package/vendor/ars/shared/contracts/passport/citation_verification_summary.schema.json +164 -0
- package/vendor/ars/shared/contracts/passport/claim_audit_result.schema.json +124 -0
- package/vendor/ars/shared/contracts/passport/claim_drift.schema.json +58 -0
- package/vendor/ars/shared/contracts/passport/claim_intent_manifest.schema.json +107 -0
- package/vendor/ars/shared/contracts/passport/constraint_violation.schema.json +65 -0
- package/vendor/ars/shared/contracts/passport/experiment_alignment_result.schema.json +69 -0
- package/vendor/ars/shared/contracts/passport/experiment_provenance_entry.schema.json +169 -0
- package/vendor/ars/shared/contracts/passport/human_read_log.schema.json +86 -0
- package/vendor/ars/shared/contracts/passport/inquiry_ledger_ref.schema.json +22 -0
- package/vendor/ars/shared/contracts/passport/literature_corpus_entry.schema.json +650 -0
- package/vendor/ars/shared/contracts/passport/preregistration_artifact.schema.json +132 -0
- package/vendor/ars/shared/contracts/passport/rejection_log.schema.json +89 -0
- package/vendor/ars/shared/contracts/passport/reset_ledger_entry.schema.json +158 -0
- package/vendor/ars/shared/contracts/passport/temporal_audit_results.schema.json +208 -0
- package/vendor/ars/shared/contracts/passport/terminal_policies.schema.json +50 -0
- package/vendor/ars/shared/contracts/passport/timeline.schema.json +102 -0
- package/vendor/ars/shared/contracts/passport/uncited_assertion.schema.json +56 -0
- package/vendor/ars/shared/contracts/passport/uncited_audit_failure.schema.json +72 -0
- package/vendor/ars/shared/contracts/passport/user_attested_read_resolution.schema.json +87 -0
- package/vendor/ars/shared/contracts/passport/version_records.schema.json +138 -0
- package/vendor/ars/shared/contracts/patch/block_manifest.schema.json +45 -0
- package/vendor/ars/shared/contracts/patch/legacy/v1_0/revision_patch.schema.json +110 -0
- package/vendor/ars/shared/contracts/patch/revision_patch.schema.json +250 -0
- package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_diagnostic.schema.json +39 -0
- package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_worker.schema.json +88 -0
- package/vendor/ars/shared/contracts/pdf/pdf_read_preflight.schema.json +227 -0
- package/vendor/ars/shared/contracts/re_review/input_manifest.schema.json +161 -0
- package/vendor/ars/shared/contracts/re_review/legacy/v1_0/input_manifest.schema.json +134 -0
- package/vendor/ars/shared/contracts/re_review/legacy/v1_0/precommitment.schema.json +193 -0
- package/vendor/ars/shared/contracts/re_review/legacy/v1_0/traceability.schema.json +841 -0
- package/vendor/ars/shared/contracts/re_review/legacy/v1_0/verdict_record.schema.json +244 -0
- package/vendor/ars/shared/contracts/re_review/precommitment.schema.json +193 -0
- package/vendor/ars/shared/contracts/re_review/traceability.schema.json +916 -0
- package/vendor/ars/shared/contracts/re_review/verdict_record.schema.json +245 -0
- package/vendor/ars/shared/contracts/research_workflow/inquiry_branch_ledger.schema.json +449 -0
- package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile.schema.json +222 -0
- package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile_selection_receipt.schema.json +111 -0
- package/vendor/ars/shared/contracts/review_target/constructive_review_findings.schema.json +172 -0
- package/vendor/ars/shared/contracts/review_target/criteria_registry.schema.json +97 -0
- package/vendor/ars/shared/contracts/review_target/review_criteria_binding_manifest.schema.json +235 -0
- package/vendor/ars/shared/contracts/review_target/review_criteria_source_receipt.schema.json +158 -0
- package/vendor/ars/shared/contracts/review_target/review_target_context.schema.json +106 -0
- package/vendor/ars/shared/contracts/review_target/review_target_declaration.schema.json +118 -0
- package/vendor/ars/shared/contracts/reviewer/full.json +114 -0
- package/vendor/ars/shared/contracts/reviewer/methodology_focus.json +75 -0
- package/vendor/ars/shared/contracts/reviewer/review_panel_provenance.schema.json +263 -0
- package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_carrier.schema.json +122 -0
- package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_input.schema.json +120 -0
- package/vendor/ars/shared/contracts/revision/author_adjudication.schema.json +251 -0
- package/vendor/ars/shared/contracts/revision/author_adjudication_input.schema.json +25 -0
- package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition.schema.json +97 -0
- package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition_input.schema.json +80 -0
- package/vendor/ars/shared/contracts/revision/claim_strength_drift_findings.schema.json +139 -0
- package/vendor/ars/shared/contracts/revision/claim_surface_manifest.schema.json +99 -0
- package/vendor/ars/shared/contracts/revision/integrity_correction_authorization.schema.json +140 -0
- package/vendor/ars/shared/contracts/revision/integrity_correction_authorization_input.schema.json +28 -0
- package/vendor/ars/shared/contracts/revision/integrity_correction_list.schema.json +48 -0
- package/vendor/ars/shared/contracts/revision/integrity_pass_receipt.schema.json +16 -0
- package/vendor/ars/shared/contracts/revision/revision_evidence_bundle.schema.json +134 -0
- package/vendor/ars/shared/contracts/revision/revision_roadmap.schema.json +334 -0
- package/vendor/ars/shared/contracts/submission/format_profile.example.yaml +33 -0
- package/vendor/ars/shared/contracts/submission/format_profile.schema.json +102 -0
- package/vendor/ars/shared/contracts/submission/submission_verification_report.schema.json +233 -0
- package/vendor/ars/shared/contracts/submission/venue_profile.schema.json +114 -0
- package/vendor/ars/shared/contracts/writer/full.json +87 -0
- package/vendor/ars/shared/cross_model_verification.md +714 -0
- package/vendor/ars/shared/evals_lift_report.schema.json +141 -0
- package/vendor/ars/shared/ground_truth_isolation_pattern.md +275 -0
- package/vendor/ars/shared/handoff_schemas.md +1209 -0
- package/vendor/ars/shared/human_subjects_authority_registry.json +1278 -0
- package/vendor/ars/shared/mode_spectrum.md +57 -0
- package/vendor/ars/shared/model_tiering.md +83 -0
- package/vendor/ars/shared/policy_data/nature_policy.md +56 -0
- package/vendor/ars/shared/prisma_trAIce_protocol.md +157 -0
- package/vendor/ars/shared/raise_framework.md +129 -0
- package/vendor/ars/shared/references/authority_content_coverage_advisory_protocol.md +275 -0
- package/vendor/ars/shared/references/claim_standing_candidate_ledger_protocol.md +66 -0
- package/vendor/ars/shared/references/claim_strength_ladder.md +93 -0
- package/vendor/ars/shared/references/cross_document_consistency_advisory_protocol.md +263 -0
- package/vendor/ars/shared/references/evidence_row_protocol.md +260 -0
- package/vendor/ars/shared/references/firm_rules.md +90 -0
- package/vendor/ars/shared/references/human_subjects_authority_protocol.md +274 -0
- package/vendor/ars/shared/references/intent_clarification_protocol.md +168 -0
- package/vendor/ars/shared/references/irb_terminology_glossary.md +229 -0
- package/vendor/ars/shared/references/protected_hedging_phrases.md +118 -0
- package/vendor/ars/shared/references/psychometric_terminology_glossary.md +109 -0
- package/vendor/ars/shared/references/review_criteria_consumer_protocol.md +238 -0
- package/vendor/ars/shared/references/review_pathway_rule_trace_protocol.md +166 -0
- package/vendor/ars/shared/references/submission_packet_manifest_protocol.md +292 -0
- package/vendor/ars/shared/references/word_count_conventions.md +124 -0
- package/vendor/ars/shared/research_workflow_profiles/field_general.json +1 -0
- package/vendor/ars/shared/review_criteria_registry.json +207 -0
- package/vendor/ars/shared/review_criteria_sources/msr-2027-technical-papers.2026-08-24.json +46 -0
- package/vendor/ars/shared/review_criteria_sources/sigsoft-empirical-standards.2026-08-24.json +31 -0
- package/vendor/ars/shared/sprint_contract.schema.json +482 -0
- package/vendor/ars/shared/style_calibration_protocol.md +151 -0
- package/vendor/ars/shared/templates/codex_audit_multifile_template.md +263 -0
- package/vendor/ars/tools/release-discipline/.toolkit-version +1 -0
- package/vendor/ars/tools/release-discipline/README.md +4 -0
- package/vendor/ars/tools/release-discipline/scripts/_release_doc_alignment_schema.py +1011 -0
- package/vendor/ars/tools/release-discipline/scripts/check_command_invariants.py +497 -0
- package/vendor/ars/tools/release-discipline/scripts/check_release_doc_alignment.py +263 -0
- package/vendor/ars/tools/release-discipline/scripts/sync-toolkit.sh +147 -0
- package/vendor/windows/NOTICE.md +12 -0
- package/vendor/windows/arm64/fd.exe +0 -0
- package/vendor/windows/arm64/licenses/fd/LICENSE-APACHE +201 -0
- package/vendor/windows/arm64/licenses/fd/LICENSE-MIT +21 -0
- package/vendor/windows/arm64/licenses/ripgrep/COPYING +3 -0
- package/vendor/windows/arm64/licenses/ripgrep/LICENSE-MIT +21 -0
- package/vendor/windows/arm64/licenses/ripgrep/UNLICENSE +24 -0
- package/vendor/windows/arm64/rg.exe +0 -0
- package/vendor/windows/x64/fd.exe +0 -0
- package/vendor/windows/x64/licenses/fd/LICENSE-APACHE +201 -0
- package/vendor/windows/x64/licenses/fd/LICENSE-MIT +21 -0
- package/vendor/windows/x64/licenses/ripgrep/COPYING +3 -0
- package/vendor/windows/x64/licenses/ripgrep/LICENSE-MIT +21 -0
- package/vendor/windows/x64/licenses/ripgrep/UNLICENSE +24 -0
- package/vendor/windows/x64/rg.exe +0 -0
|
@@ -0,0 +1,3398 @@
|
|
|
1
|
+
"""Mutation suite for scripts/check_re_review_synthesis.py (#576 Spec B §13).
|
|
2
|
+
|
|
3
|
+
Convention (Spec A §13 witness style): three hand-pinned golden scenarios
|
|
4
|
+
pass end-to-end; each invariant then has a fixture violating exactly it,
|
|
5
|
+
asserting the graded exit code and the invariant-specific message. The §10
|
|
6
|
+
card-normalization fixtures pin the two shipped example files line-by-line
|
|
7
|
+
(neither example has a DA seat, so DA vocabulary coverage rides synthetic
|
|
8
|
+
fixtures). The schema-parity tests run the golden artifacts through the
|
|
9
|
+
four shared/contracts/re_review/*.schema.json documents with jsonschema so
|
|
10
|
+
the checker's self-contained validators and the schema files cannot drift
|
|
11
|
+
apart on the positive path.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import copy
|
|
16
|
+
import hashlib
|
|
17
|
+
import json
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import jsonschema
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
from scripts import check_re_review_synthesis as crs
|
|
24
|
+
from scripts._block_parser import base_draft_hash, parse_document
|
|
25
|
+
from scripts.ars_anchorize_draft import build_manifest
|
|
26
|
+
from scripts.ars_apply_revision_patch import run as apply_revision_patch
|
|
27
|
+
from scripts.revision_roadmap import author_decision_digest
|
|
28
|
+
|
|
29
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
30
|
+
SCHEMA_DIR = REPO / "shared" / "contracts" / "re_review"
|
|
31
|
+
EXAMPLES = REPO / "academic-paper-reviewer" / "examples"
|
|
32
|
+
|
|
33
|
+
ORIGINAL_BYTES = b"<!--block:B0001-->\nOriginal body.\n"
|
|
34
|
+
REVISED_BYTES = b"<!--block:B0001-->\nRevised body.\n"
|
|
35
|
+
PATCH_BYTES = b'{"synthetic": "patch bytes"}\n'
|
|
36
|
+
ORIGINAL_SHA = hashlib.sha256(ORIGINAL_BYTES).hexdigest()
|
|
37
|
+
REVISED_SHA = hashlib.sha256(REVISED_BYTES).hexdigest()
|
|
38
|
+
PATCH_SHA = hashlib.sha256(PATCH_BYTES).hexdigest()
|
|
39
|
+
SYNTH_SHA = hashlib.sha256(b"synthetic artifact").hexdigest()
|
|
40
|
+
|
|
41
|
+
MANUSCRIPT_ANCHOR = 'text: §3.2 "power analysis"'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _entry(ref: str, sha: str, version_label=None, origin_date=None):
|
|
45
|
+
return {
|
|
46
|
+
"present": True,
|
|
47
|
+
"path_or_passport_ref": ref,
|
|
48
|
+
"sha256": sha,
|
|
49
|
+
"version_label": version_label,
|
|
50
|
+
"origin_date": origin_date,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _anchor(text=MANUSCRIPT_ANCHOR, artifact="manuscript"):
|
|
55
|
+
return {"anchor": text, "anchor_artifact": artifact}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _new_issue(n, attribution, severity, nearest=None):
|
|
59
|
+
return {
|
|
60
|
+
"new_issue_id": f"NEW-{n}",
|
|
61
|
+
"description": f"New issue {n}",
|
|
62
|
+
"location_anchor": 'text: §4 "issue span"',
|
|
63
|
+
"severity": severity,
|
|
64
|
+
"found_by": "R1",
|
|
65
|
+
"confidence": 4,
|
|
66
|
+
"competence_basis": "methods reviewer",
|
|
67
|
+
"attribution": attribution,
|
|
68
|
+
"attribution_evidence": "diff-supported comparison" if attribution == "regression" else "anchored in both versions",
|
|
69
|
+
"nearest_roadmap_item": nearest,
|
|
70
|
+
"non_match_rationale": "outside every committed criterion scope",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# --- golden scenario 1: minimal Accept ----------------------------------------
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def scenario_accept():
|
|
78
|
+
roadmap = {
|
|
79
|
+
"items": [
|
|
80
|
+
{
|
|
81
|
+
"id": "REV-001",
|
|
82
|
+
"description": "Justify the sample size",
|
|
83
|
+
"reviewer": "R1",
|
|
84
|
+
"type": "Major",
|
|
85
|
+
"obligation_class": "must_fix",
|
|
86
|
+
"severity": "major",
|
|
87
|
+
"target_section": "Methods",
|
|
88
|
+
"suggested_action": "Add a power analysis",
|
|
89
|
+
"consensus_level": "CONSENSUS-3",
|
|
90
|
+
"verification_criteria": "A power analysis with a stated effect size appears in Methods.",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "REV-002",
|
|
94
|
+
"description": "Clarify limitations",
|
|
95
|
+
"reviewer": "EIC",
|
|
96
|
+
"type": "Minor",
|
|
97
|
+
"obligation_class": "should_fix",
|
|
98
|
+
"severity": "minor",
|
|
99
|
+
"target_section": "Discussion",
|
|
100
|
+
"suggested_action": "Add a limitations paragraph",
|
|
101
|
+
"consensus_level": "CONSENSUS-3",
|
|
102
|
+
"verification_criteria": "A limitations paragraph names the generalizability bounds.",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
"total_items": 2,
|
|
106
|
+
"must_fix_count": 1,
|
|
107
|
+
"editorial_decision": "Major Revision",
|
|
108
|
+
"consensus_summary": "Panel agreed on the two items.",
|
|
109
|
+
"dissenting_opinions": [],
|
|
110
|
+
}
|
|
111
|
+
letter = (
|
|
112
|
+
"# Editorial Decision\n\n"
|
|
113
|
+
"## Required Revisions * (Must Fix)\n\n"
|
|
114
|
+
"### Required Item Details\n\n"
|
|
115
|
+
"**R1: Sample size justification**\n"
|
|
116
|
+
"- **Problem**: Sample size is asserted, not justified.\n"
|
|
117
|
+
"- **Requirement**: Add a formal power analysis.\n"
|
|
118
|
+
"- **Acceptance criteria**: A formal power analysis appears in Methods §3.2.\n\n"
|
|
119
|
+
"## Closing\n"
|
|
120
|
+
)
|
|
121
|
+
precommitment = {
|
|
122
|
+
"contract_version": "1.1",
|
|
123
|
+
"round_id": "round-2",
|
|
124
|
+
"input_manifest_hash": "0" * 64,
|
|
125
|
+
"items": [
|
|
126
|
+
{
|
|
127
|
+
"item_id": "REV-001",
|
|
128
|
+
"obligation_class": "must_fix",
|
|
129
|
+
"inherited_criterion": {
|
|
130
|
+
"roadmap_text": "A power analysis with a stated effect size appears in Methods.",
|
|
131
|
+
"letter_text": "A formal power analysis appears in Methods §3.2.",
|
|
132
|
+
"letter_item_ref": "R1",
|
|
133
|
+
},
|
|
134
|
+
"operationalization": {
|
|
135
|
+
"fully_addressed": "Methods carries a power analysis naming effect size, alpha, and power.",
|
|
136
|
+
"partially_addressed": "A power analysis appears but omits the effect size.",
|
|
137
|
+
"made_worse_discriminator": "The revision removes the existing sample-size rationale.",
|
|
138
|
+
},
|
|
139
|
+
"expected_change_surface": "Methods §3.2",
|
|
140
|
+
"equivalence_policy": "allowed",
|
|
141
|
+
"source_reviewer": "R1",
|
|
142
|
+
"source_reviewer_labels": ["R1"],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"item_id": "REV-002",
|
|
146
|
+
"obligation_class": "should_fix",
|
|
147
|
+
"inherited_criterion": {
|
|
148
|
+
"roadmap_text": "A limitations paragraph names the generalizability bounds.",
|
|
149
|
+
},
|
|
150
|
+
"operationalization": {
|
|
151
|
+
"fully_addressed": "Discussion carries a limitations paragraph naming generalizability bounds.",
|
|
152
|
+
},
|
|
153
|
+
"expected_change_surface": "Discussion",
|
|
154
|
+
"equivalence_policy": "allowed",
|
|
155
|
+
"source_reviewer": "EIC",
|
|
156
|
+
"source_reviewer_labels": ["EIC"],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
"new_standards": [],
|
|
160
|
+
}
|
|
161
|
+
verdict_record = {
|
|
162
|
+
"contract_version": "1.1",
|
|
163
|
+
"round_id": "round-2",
|
|
164
|
+
"precommitment_hash": "0" * 64,
|
|
165
|
+
"items": [
|
|
166
|
+
{
|
|
167
|
+
"item_id": "REV-001",
|
|
168
|
+
"verdict": "FULLY_ADDRESSED",
|
|
169
|
+
"evidence_anchor": [MANUSCRIPT_ANCHOR],
|
|
170
|
+
"change_summary": "Methods §3.2 gains a formal power analysis.",
|
|
171
|
+
"verified_by": "R1",
|
|
172
|
+
"applied_criterion": "precommitted",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"item_id": "REV-002",
|
|
176
|
+
"verdict": "FULLY_ADDRESSED",
|
|
177
|
+
"evidence_anchor": ['text: §5 "limitations"'],
|
|
178
|
+
"change_summary": "Discussion gains a limitations paragraph.",
|
|
179
|
+
"verified_by": "EIC",
|
|
180
|
+
"applied_criterion": "precommitted",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
"new_issues": [],
|
|
184
|
+
"dissents": [],
|
|
185
|
+
"escalation_exceptions": [],
|
|
186
|
+
}
|
|
187
|
+
traceability = {
|
|
188
|
+
"contract_version": "1.1",
|
|
189
|
+
"round_id": "round-2",
|
|
190
|
+
"revision": 1,
|
|
191
|
+
"verdict_record_hash": "0" * 64,
|
|
192
|
+
"rows": [
|
|
193
|
+
{
|
|
194
|
+
"item_id": "REV-001",
|
|
195
|
+
"concern_id": "R1",
|
|
196
|
+
"obligation_class": "MUST_FIX",
|
|
197
|
+
"original_comment": "Sample size is asserted, not justified.",
|
|
198
|
+
"authors_claim": "We added a power analysis in §3.2.",
|
|
199
|
+
"revision_location": "Methods §3.2",
|
|
200
|
+
"verified": "YES",
|
|
201
|
+
"status": "FULLY_ADDRESSED",
|
|
202
|
+
"quality_assessment": "Meets the committed pattern.",
|
|
203
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
204
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
205
|
+
"verified_by": "R1",
|
|
206
|
+
"cross_model_status": "not_configured",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"item_id": "REV-002",
|
|
210
|
+
"concern_id": "S1",
|
|
211
|
+
"obligation_class": "SHOULD_FIX",
|
|
212
|
+
"original_comment": "Limitations are unclear.",
|
|
213
|
+
"authors_claim": "We added a limitations paragraph.",
|
|
214
|
+
"revision_location": "Discussion §5",
|
|
215
|
+
"verified": "YES",
|
|
216
|
+
"status": "FULLY_ADDRESSED",
|
|
217
|
+
"quality_assessment": "Adequate.",
|
|
218
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
219
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
220
|
+
"verified_by": "EIC",
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
"adjustments": [],
|
|
224
|
+
"new_issues": [],
|
|
225
|
+
"post_letter_observations": [],
|
|
226
|
+
"dissent_adjudications": [],
|
|
227
|
+
"resolution_intents": [],
|
|
228
|
+
"cross_model_resolutions": [],
|
|
229
|
+
"rebuttal_adjudications": [],
|
|
230
|
+
"g2d_acceptances": [],
|
|
231
|
+
"pending_rebuttal_upgrades": [],
|
|
232
|
+
"escalation_approvals": [],
|
|
233
|
+
"reapplications": [],
|
|
234
|
+
"decision_inputs": {
|
|
235
|
+
"per_item": [
|
|
236
|
+
{"item_id": "REV-001", "final_verdict": "FULLY_ADDRESSED", "driving_severity": "major"},
|
|
237
|
+
],
|
|
238
|
+
"verdict_counts": _counts(must_fix={"FULLY_ADDRESSED": 1}, should_fix={"FULLY_ADDRESSED": 1}),
|
|
239
|
+
"residual_obligation_class_counts": _magnitudes(),
|
|
240
|
+
"should_fix_addressed_rate": {"numerator": 1, "denominator": 1},
|
|
241
|
+
"regressions": [],
|
|
242
|
+
"non_regression_new_issue_ids": [],
|
|
243
|
+
"escalations": [],
|
|
244
|
+
"reject_recommended": False,
|
|
245
|
+
"apply_chain_witness": "pass",
|
|
246
|
+
},
|
|
247
|
+
"decision_state": "Accept",
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
"cross_model_active": False,
|
|
251
|
+
"roadmap": roadmap,
|
|
252
|
+
"letter": letter,
|
|
253
|
+
"reports": [
|
|
254
|
+
{
|
|
255
|
+
"report_format_version": "1.3",
|
|
256
|
+
"base_draft_hash": ORIGINAL_SHA[:12],
|
|
257
|
+
"output_draft_hash": REVISED_SHA[:12],
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
"precommitment": precommitment,
|
|
261
|
+
"verdict_record": verdict_record,
|
|
262
|
+
"traceability": traceability,
|
|
263
|
+
"manifest_overrides": {},
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _counts(must_fix=None, should_fix=None, consider=None):
|
|
268
|
+
result = {}
|
|
269
|
+
for name, given in (("must_fix", must_fix), ("should_fix", should_fix), ("consider", consider)):
|
|
270
|
+
bucket = {verdict: 0 for verdict in crs.VERDICTS}
|
|
271
|
+
bucket.update(given or {})
|
|
272
|
+
result[name] = bucket
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _magnitudes(must_fix=None, should_fix=None, consider=None):
|
|
277
|
+
result = {}
|
|
278
|
+
for name, given in (("must_fix", must_fix), ("should_fix", should_fix), ("consider", consider)):
|
|
279
|
+
bucket = {mag: 0 for mag in crs.RESIDUAL_OBLIGATION_CLASSES}
|
|
280
|
+
bucket.update(given or {})
|
|
281
|
+
result[name] = bucket
|
|
282
|
+
return result
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# --- golden scenario 2: complex Minor Revision (cross-model active) ------------
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def scenario_complex():
|
|
289
|
+
roadmap = {
|
|
290
|
+
"items": [
|
|
291
|
+
{
|
|
292
|
+
"id": "REV-001",
|
|
293
|
+
"description": "Confounded treatment assignment",
|
|
294
|
+
"reviewer": "R1, R3",
|
|
295
|
+
"type": "Major",
|
|
296
|
+
"obligation_class": "must_fix",
|
|
297
|
+
"severity": "critical",
|
|
298
|
+
"target_section": "Design",
|
|
299
|
+
"suggested_action": "Re-run with matched controls or rebut",
|
|
300
|
+
"consensus_level": "CONSENSUS-4",
|
|
301
|
+
"verification_criteria": "Assignment confound is removed or rebutted with evidence.",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"id": "REV-002",
|
|
305
|
+
"description": "Missing robustness checks",
|
|
306
|
+
"reviewer": "Peer Reviewer 2 (Domain)",
|
|
307
|
+
"type": "Major",
|
|
308
|
+
"obligation_class": "must_fix",
|
|
309
|
+
"severity": "major",
|
|
310
|
+
"target_section": "Results",
|
|
311
|
+
"suggested_action": "Add robustness table",
|
|
312
|
+
"consensus_level": "CONSENSUS-3",
|
|
313
|
+
"verification_criteria": "A robustness table covers the two alternative specifications.",
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"id": "REV-003",
|
|
317
|
+
"description": "Terminology drift",
|
|
318
|
+
"reviewer": "EIC",
|
|
319
|
+
"type": "Editorial",
|
|
320
|
+
"obligation_class": "should_fix",
|
|
321
|
+
"severity": "minor",
|
|
322
|
+
"target_section": "Throughout",
|
|
323
|
+
"suggested_action": "Unify terminology",
|
|
324
|
+
"consensus_level": "CONSENSUS-3",
|
|
325
|
+
"verification_criteria": "The construct is named consistently throughout.",
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"id": "REV-004",
|
|
329
|
+
"description": "Consider a figure for the pipeline",
|
|
330
|
+
"reviewer": "EIC",
|
|
331
|
+
"type": "Editorial",
|
|
332
|
+
"obligation_class": "consider",
|
|
333
|
+
"source_kind": "editorial",
|
|
334
|
+
"target_section": "Methods",
|
|
335
|
+
"suggested_action": "Optional figure",
|
|
336
|
+
"consensus_level": "CONSENSUS-3",
|
|
337
|
+
"verification_criteria": "A pipeline figure exists or the omission is reasonable.",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"id": "REV-005",
|
|
341
|
+
"description": "Legacy-style item without transported severity",
|
|
342
|
+
"reviewer": "R2",
|
|
343
|
+
"type": "Minor",
|
|
344
|
+
"obligation_class": "should_fix",
|
|
345
|
+
"target_section": "Appendix",
|
|
346
|
+
"suggested_action": "Tidy appendix tables",
|
|
347
|
+
"consensus_level": "SPLIT",
|
|
348
|
+
"verification_criteria": "Appendix tables carry readable headers.",
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
"total_items": 5,
|
|
352
|
+
"must_fix_count": 2,
|
|
353
|
+
"editorial_decision": "Major Revision",
|
|
354
|
+
"consensus_summary": "Two structural concerns dominate.",
|
|
355
|
+
"dissenting_opinions": ["R3 doubts the confound is fatal."],
|
|
356
|
+
}
|
|
357
|
+
letter = (
|
|
358
|
+
"# Editorial Decision\n\n"
|
|
359
|
+
"## Required Revisions * (Must Fix)\n\n"
|
|
360
|
+
"### Required Item Details\n\n"
|
|
361
|
+
"**R1: Confounded treatment assignment**\n"
|
|
362
|
+
"- **Problem**: Assignment correlates with cohort.\n"
|
|
363
|
+
"- **Requirement**: Remove or rebut the confound.\n"
|
|
364
|
+
"- **Acceptance criteria**: The confound is removed by design or rebutted with matched evidence.\n\n"
|
|
365
|
+
"**R2: Missing robustness checks**\n"
|
|
366
|
+
"- **Problem**: Single specification only.\n"
|
|
367
|
+
"- **Requirement**: Add the robustness table.\n"
|
|
368
|
+
"- **Acceptance criteria**: A robustness table covers both alternative specifications.\n\n"
|
|
369
|
+
"## Closing\n"
|
|
370
|
+
)
|
|
371
|
+
pre_items = [
|
|
372
|
+
{
|
|
373
|
+
"item_id": "REV-001",
|
|
374
|
+
"obligation_class": "must_fix",
|
|
375
|
+
"inherited_criterion": {
|
|
376
|
+
"roadmap_text": "Assignment confound is removed or rebutted with evidence.",
|
|
377
|
+
"letter_text": "The confound is removed by design or rebutted with matched evidence.",
|
|
378
|
+
"letter_item_ref": "R1",
|
|
379
|
+
},
|
|
380
|
+
"operationalization": {
|
|
381
|
+
"fully_addressed": "Design removes the confound or the rebuttal carries matched-cohort evidence.",
|
|
382
|
+
"partially_addressed": "A partial control is added but one cohort remains unmatched.",
|
|
383
|
+
"made_worse_discriminator": "The revision introduces an additional assignment confound.",
|
|
384
|
+
},
|
|
385
|
+
"expected_change_surface": "Design §2",
|
|
386
|
+
"equivalence_policy": "allowed",
|
|
387
|
+
"source_reviewer": "R1, R3",
|
|
388
|
+
"source_reviewer_labels": ["R1", "R3"],
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"item_id": "REV-002",
|
|
392
|
+
"obligation_class": "must_fix",
|
|
393
|
+
"inherited_criterion": {
|
|
394
|
+
"roadmap_text": "A robustness table covers the two alternative specifications.",
|
|
395
|
+
"letter_text": "A robustness table covers both alternative specifications.",
|
|
396
|
+
"letter_item_ref": "R2",
|
|
397
|
+
},
|
|
398
|
+
"operationalization": {
|
|
399
|
+
"fully_addressed": "Results carries a robustness table with both alternative specifications.",
|
|
400
|
+
"partially_addressed": "The table covers only one alternative specification.",
|
|
401
|
+
"made_worse_discriminator": "The revision drops the primary specification results.",
|
|
402
|
+
},
|
|
403
|
+
"expected_change_surface": "Results §4",
|
|
404
|
+
"equivalence_policy": "allowed",
|
|
405
|
+
"source_reviewer": "Peer Reviewer 2 (Domain)",
|
|
406
|
+
"source_reviewer_labels": ["R2"],
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"item_id": "REV-003",
|
|
410
|
+
"obligation_class": "should_fix",
|
|
411
|
+
"inherited_criterion": {"roadmap_text": "The construct is named consistently throughout."},
|
|
412
|
+
"operationalization": {"fully_addressed": "One construct name is used consistently in every section."},
|
|
413
|
+
"expected_change_surface": "Throughout",
|
|
414
|
+
"equivalence_policy": "allowed",
|
|
415
|
+
"source_reviewer": "EIC",
|
|
416
|
+
"source_reviewer_labels": ["EIC"],
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"item_id": "REV-005",
|
|
420
|
+
"obligation_class": "should_fix",
|
|
421
|
+
"inherited_criterion": {"roadmap_text": "Appendix tables carry readable headers."},
|
|
422
|
+
"operationalization": {"fully_addressed": "Every appendix table carries descriptive column headers."},
|
|
423
|
+
"expected_change_surface": "Appendix",
|
|
424
|
+
"equivalence_policy": "allowed",
|
|
425
|
+
"source_reviewer": "R2",
|
|
426
|
+
"source_reviewer_labels": ["R2"],
|
|
427
|
+
},
|
|
428
|
+
]
|
|
429
|
+
precommitment = {
|
|
430
|
+
"contract_version": "1.1",
|
|
431
|
+
"round_id": "round-2",
|
|
432
|
+
"input_manifest_hash": "0" * 64,
|
|
433
|
+
"items": pre_items,
|
|
434
|
+
"new_standards": [
|
|
435
|
+
{
|
|
436
|
+
"new_standard_id": "NS-1",
|
|
437
|
+
"item_id": "REV-001",
|
|
438
|
+
"standard_text": "The design section must disclose cohort recruitment windows.",
|
|
439
|
+
"why_not_in_round1": "Round 1 never named recruitment windows explicitly.",
|
|
440
|
+
"classification": "escalation_requested",
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
}
|
|
444
|
+
dissent = {
|
|
445
|
+
"dissent_id": "DIS-1",
|
|
446
|
+
"item_id": "REV-003",
|
|
447
|
+
"criterion_hash": "0" * 64, # recomputed at emit
|
|
448
|
+
"reason_code": "criterion_ambiguous",
|
|
449
|
+
"original_operationalization": "One construct name is used consistently in every section.",
|
|
450
|
+
"replacement_operationalization": "One construct name is used consistently in body sections; appendix aliases allowed.",
|
|
451
|
+
"evidence": 'text: §A.1 "alias table"',
|
|
452
|
+
"decision_impact_note": "Slightly narrows the consistency surface.",
|
|
453
|
+
}
|
|
454
|
+
verdict_record = {
|
|
455
|
+
"contract_version": "1.1",
|
|
456
|
+
"round_id": "round-2",
|
|
457
|
+
"precommitment_hash": "0" * 64,
|
|
458
|
+
"items": [
|
|
459
|
+
{
|
|
460
|
+
"item_id": "REV-001",
|
|
461
|
+
"verdict": "PARTIALLY_ADDRESSED",
|
|
462
|
+
"evidence_anchor": ['text: §2 "matched controls"'],
|
|
463
|
+
"change_summary": "Design adds matched controls for one cohort.",
|
|
464
|
+
"residual_gap": {"text": "Second cohort remains unmatched.", "residual_obligation_class": "should_fix"},
|
|
465
|
+
"verified_by": "R1",
|
|
466
|
+
"applied_criterion": "precommitted",
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
"item_id": "REV-002",
|
|
470
|
+
"verdict": "NOT_ADDRESSED",
|
|
471
|
+
"evidence_anchor": ['absence: Results §4 — expected robustness table; checked Results and appendix'],
|
|
472
|
+
"change_summary": "No robustness table was added.",
|
|
473
|
+
"verified_by": "R2",
|
|
474
|
+
"applied_criterion": "precommitted",
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"item_id": "REV-003",
|
|
478
|
+
"verdict": "FULLY_ADDRESSED",
|
|
479
|
+
"evidence_anchor": ['text: §1-§5 "construct naming"'],
|
|
480
|
+
"change_summary": "Terminology unified in body sections.",
|
|
481
|
+
"verified_by": "EIC",
|
|
482
|
+
"applied_criterion": "dissented:DIS-1",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"item_id": "REV-004",
|
|
486
|
+
"verdict": "FULLY_ADDRESSED",
|
|
487
|
+
"evidence_anchor": ['figure: Figure 2'],
|
|
488
|
+
"change_summary": "A pipeline figure was added.",
|
|
489
|
+
"verified_by": "EIC",
|
|
490
|
+
"applied_criterion": "not_precommitted",
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"item_id": "REV-005",
|
|
494
|
+
"verdict": "MADE_WORSE",
|
|
495
|
+
"evidence_anchor": ['table: Table A2'],
|
|
496
|
+
"change_summary": "Appendix headers were replaced with opaque codes.",
|
|
497
|
+
"verified_by": "R2",
|
|
498
|
+
"applied_criterion": "precommitted",
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
"new_issues": [
|
|
502
|
+
_new_issue(1, "regression", "minor"),
|
|
503
|
+
_new_issue(2, "previously_missed", "major"),
|
|
504
|
+
_new_issue(3, "indeterminate", "minor"),
|
|
505
|
+
],
|
|
506
|
+
"dissents": [dissent],
|
|
507
|
+
"escalation_exceptions": [
|
|
508
|
+
{
|
|
509
|
+
"exception_id": "ESC-1",
|
|
510
|
+
"new_standard_ref": "NS-1",
|
|
511
|
+
"escalation_class": "fatal_validity",
|
|
512
|
+
"reason_code": "undisclosed-recruitment-window",
|
|
513
|
+
"evidence_anchor": 'text: §2 "recruitment"',
|
|
514
|
+
"why_round1_missed_it": "Round 1 focused on assignment, not recruitment.",
|
|
515
|
+
"mechanical_decision_impact": "Major Revision",
|
|
516
|
+
"approval_state": "pending",
|
|
517
|
+
}
|
|
518
|
+
],
|
|
519
|
+
}
|
|
520
|
+
rebuttal_anchors = [_anchor('text: §2 "matched cohort evidence"'), _anchor('text: letter §3 "counter-evidence"', "letter")]
|
|
521
|
+
adj1 = {
|
|
522
|
+
"adjustment_id": "ADJ-1",
|
|
523
|
+
"item_id": "REV-001",
|
|
524
|
+
"from_verdict": "PARTIALLY_ADDRESSED",
|
|
525
|
+
"to_verdict": "FULLY_ADDRESSED",
|
|
526
|
+
"basis": "valid_rebuttal",
|
|
527
|
+
"evidence_anchor": rebuttal_anchors,
|
|
528
|
+
"critical_rebuttal_check": "adjudicated:RADJ-1",
|
|
529
|
+
"rationale": "The rebuttal's matched-cohort evidence rebuts the residual on the merits.",
|
|
530
|
+
}
|
|
531
|
+
rap1_anchors = [_anchor('table: Table 4 [robustness]')]
|
|
532
|
+
adj2 = {
|
|
533
|
+
"adjustment_id": "ADJ-2",
|
|
534
|
+
"item_id": "REV-002",
|
|
535
|
+
"from_verdict": "NOT_ADDRESSED",
|
|
536
|
+
"to_verdict": "FULLY_ADDRESSED",
|
|
537
|
+
"basis": "cross_model_adjudication",
|
|
538
|
+
"evidence_anchor": rap1_anchors,
|
|
539
|
+
"rationale": "Re-application located the robustness table in the appendix.",
|
|
540
|
+
"source_ref": "reapplication:RAP-1",
|
|
541
|
+
}
|
|
542
|
+
traceability = {
|
|
543
|
+
"contract_version": "1.1",
|
|
544
|
+
"round_id": "round-2",
|
|
545
|
+
"revision": 2,
|
|
546
|
+
"supersedes_hash": "ab" * 32,
|
|
547
|
+
"verdict_record_hash": "0" * 64,
|
|
548
|
+
"rows": [
|
|
549
|
+
{
|
|
550
|
+
"item_id": "REV-001",
|
|
551
|
+
"concern_id": "R1",
|
|
552
|
+
"obligation_class": "MUST_FIX",
|
|
553
|
+
"original_comment": "Assignment correlates with cohort.",
|
|
554
|
+
"authors_claim": "We disagree; matched evidence attached.",
|
|
555
|
+
"revision_location": "Design §2",
|
|
556
|
+
"verified": "YES",
|
|
557
|
+
"status": "FULLY_ADDRESSED",
|
|
558
|
+
"quality_assessment": "Rebuttal upheld by the adjudication pass.",
|
|
559
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
560
|
+
"phase2a_verdict": "PARTIALLY_ADDRESSED",
|
|
561
|
+
"verified_by": "R1",
|
|
562
|
+
"adjustment_id": "ADJ-1",
|
|
563
|
+
"addressed_by_rebuttal": True,
|
|
564
|
+
"cross_model_status": "agree",
|
|
565
|
+
"cross_model_verdict": "FULLY_ADDRESSED",
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
"item_id": "REV-002",
|
|
569
|
+
"concern_id": "R2",
|
|
570
|
+
"obligation_class": "MUST_FIX",
|
|
571
|
+
"original_comment": "Single specification only.",
|
|
572
|
+
"authors_claim": "Robustness table added (appendix).",
|
|
573
|
+
"revision_location": "Appendix Table 4",
|
|
574
|
+
"verified": "YES",
|
|
575
|
+
"status": "FULLY_ADDRESSED",
|
|
576
|
+
"quality_assessment": "Located by re-application after divergence.",
|
|
577
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
578
|
+
"phase2a_verdict": "NOT_ADDRESSED",
|
|
579
|
+
"verified_by": "R2",
|
|
580
|
+
"adjustment_id": "ADJ-2",
|
|
581
|
+
"cross_model_status": "agree",
|
|
582
|
+
"cross_model_verdict": "FULLY_ADDRESSED",
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
"item_id": "REV-003",
|
|
586
|
+
"concern_id": "S1",
|
|
587
|
+
"obligation_class": "SHOULD_FIX",
|
|
588
|
+
"original_comment": "Terminology drifts.",
|
|
589
|
+
"authors_claim": "Unified throughout.",
|
|
590
|
+
"revision_location": "Body sections",
|
|
591
|
+
"verified": "YES",
|
|
592
|
+
"status": "FULLY_ADDRESSED",
|
|
593
|
+
"quality_assessment": "Consistent under the dissented criterion.",
|
|
594
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
595
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
596
|
+
"verified_by": "EIC",
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
"item_id": "REV-004",
|
|
600
|
+
"concern_id": "S2",
|
|
601
|
+
"obligation_class": "CONSIDER",
|
|
602
|
+
"original_comment": "A pipeline figure would help.",
|
|
603
|
+
"authors_claim": "Figure 2 added.",
|
|
604
|
+
"revision_location": "Methods Figure 2",
|
|
605
|
+
"verified": "YES",
|
|
606
|
+
"status": "FULLY_ADDRESSED",
|
|
607
|
+
"quality_assessment": "Nice addition.",
|
|
608
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
609
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
610
|
+
"verified_by": "EIC",
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
"item_id": "REV-005",
|
|
614
|
+
"concern_id": "S3",
|
|
615
|
+
"obligation_class": "SHOULD_FIX",
|
|
616
|
+
"original_comment": "Appendix tables unreadable.",
|
|
617
|
+
"authors_claim": "Headers streamlined.",
|
|
618
|
+
"revision_location": "Appendix",
|
|
619
|
+
"verified": "NO",
|
|
620
|
+
"status": "MADE_WORSE",
|
|
621
|
+
"quality_assessment": "Headers replaced with opaque codes.",
|
|
622
|
+
"final_verdict": "MADE_WORSE",
|
|
623
|
+
"phase2a_verdict": "MADE_WORSE",
|
|
624
|
+
"verified_by": "R2",
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
"adjustments": [adj1, adj2],
|
|
628
|
+
"new_issues": [
|
|
629
|
+
_new_issue(1, "regression", "minor"),
|
|
630
|
+
_new_issue(2, "previously_missed", "major"),
|
|
631
|
+
_new_issue(3, "indeterminate", "minor"),
|
|
632
|
+
],
|
|
633
|
+
"post_letter_observations": ["Abstract tone reads more promotional after the letter."],
|
|
634
|
+
"dissent_adjudications": [],
|
|
635
|
+
"resolution_intents": [{"intent_id": "INT-1", "item_id": "REV-002", "answered_by": "system"}],
|
|
636
|
+
"cross_model_resolutions": [
|
|
637
|
+
{
|
|
638
|
+
"resolution_id": "RES-1",
|
|
639
|
+
"item_id": "REV-002",
|
|
640
|
+
"intent_id": "INT-1",
|
|
641
|
+
"reapplication_id": "RAP-1",
|
|
642
|
+
"state": "primary_revised",
|
|
643
|
+
"resolved_by": "system",
|
|
644
|
+
"rationale": "Re-application located the table; verdict revised.",
|
|
645
|
+
}
|
|
646
|
+
],
|
|
647
|
+
"rebuttal_adjudications": [
|
|
648
|
+
{
|
|
649
|
+
"rebuttal_adjudication_id": "RADJ-1",
|
|
650
|
+
"item_id": "REV-001",
|
|
651
|
+
"verdict": "upheld",
|
|
652
|
+
"rationale": "Counter-evidence rebuts the original finding on the merits.",
|
|
653
|
+
}
|
|
654
|
+
],
|
|
655
|
+
"g2d_acceptances": [],
|
|
656
|
+
"pending_rebuttal_upgrades": [
|
|
657
|
+
{
|
|
658
|
+
"proposal_id": "PRB-1",
|
|
659
|
+
"item_id": "REV-001",
|
|
660
|
+
"drafted_adjustment": {
|
|
661
|
+
"item_id": "REV-001",
|
|
662
|
+
"from_verdict": "PARTIALLY_ADDRESSED",
|
|
663
|
+
"to_verdict": "FULLY_ADDRESSED",
|
|
664
|
+
"basis": "valid_rebuttal",
|
|
665
|
+
"evidence_anchor": copy.deepcopy(rebuttal_anchors),
|
|
666
|
+
"rationale": "The rebuttal's matched-cohort evidence rebuts the residual on the merits.",
|
|
667
|
+
},
|
|
668
|
+
"disposition": "booked:ADJ-1",
|
|
669
|
+
}
|
|
670
|
+
],
|
|
671
|
+
"escalation_approvals": [
|
|
672
|
+
{"exception_id": "ESC-1", "approval_state": "rejected", "approved_by": "user"}
|
|
673
|
+
],
|
|
674
|
+
"reapplications": [
|
|
675
|
+
{
|
|
676
|
+
"reapplication_id": "RAP-1",
|
|
677
|
+
"item_id": "REV-002",
|
|
678
|
+
"answer_refs": ["intent:INT-1"],
|
|
679
|
+
"pre_reapplication_verdict": "NOT_ADDRESSED",
|
|
680
|
+
"reapplied_verdict": "FULLY_ADDRESSED",
|
|
681
|
+
"evidence_anchor": copy.deepcopy(rap1_anchors),
|
|
682
|
+
"rationale": "Re-application located the robustness table in the appendix.",
|
|
683
|
+
"criterion_ref": "phase1:REV-002",
|
|
684
|
+
}
|
|
685
|
+
],
|
|
686
|
+
"decision_inputs": {
|
|
687
|
+
"per_item": [
|
|
688
|
+
{"item_id": "REV-001", "final_verdict": "FULLY_ADDRESSED", "driving_severity": "critical"},
|
|
689
|
+
{"item_id": "REV-002", "final_verdict": "FULLY_ADDRESSED", "driving_severity": "major"},
|
|
690
|
+
],
|
|
691
|
+
"verdict_counts": _counts(
|
|
692
|
+
must_fix={"FULLY_ADDRESSED": 2},
|
|
693
|
+
should_fix={"FULLY_ADDRESSED": 1, "MADE_WORSE": 1},
|
|
694
|
+
consider={"FULLY_ADDRESSED": 1},
|
|
695
|
+
),
|
|
696
|
+
"residual_obligation_class_counts": _magnitudes(),
|
|
697
|
+
"should_fix_addressed_rate": {"numerator": 1, "denominator": 2},
|
|
698
|
+
"regressions": [{"new_issue_id": "NEW-1", "severity": "minor"}],
|
|
699
|
+
"non_regression_new_issue_ids": ["NEW-2", "NEW-3"],
|
|
700
|
+
"escalations": [
|
|
701
|
+
{
|
|
702
|
+
"exception_id": "ESC-1",
|
|
703
|
+
"effective_approval_state": "rejected",
|
|
704
|
+
"escalation_class": "fatal_validity",
|
|
705
|
+
"mechanical_decision_impact": "Major Revision",
|
|
706
|
+
}
|
|
707
|
+
],
|
|
708
|
+
"reject_recommended": False,
|
|
709
|
+
"apply_chain_witness": "pass",
|
|
710
|
+
},
|
|
711
|
+
"decision_state": "Minor Revision",
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
"cross_model_active": True,
|
|
715
|
+
"roadmap": roadmap,
|
|
716
|
+
"letter": letter,
|
|
717
|
+
"reports": [
|
|
718
|
+
{
|
|
719
|
+
"report_format_version": "1.3",
|
|
720
|
+
"base_draft_hash": ORIGINAL_SHA[:12],
|
|
721
|
+
"output_draft_hash": REVISED_SHA[:12],
|
|
722
|
+
}
|
|
723
|
+
],
|
|
724
|
+
"precommitment": precommitment,
|
|
725
|
+
"verdict_record": verdict_record,
|
|
726
|
+
"traceability": traceability,
|
|
727
|
+
"manifest_overrides": {},
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
# --- golden scenario 3: G2(d) fail-closed (deferred and accepted forms) --------
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def scenario_g2d(accepted: bool):
|
|
735
|
+
roadmap = {
|
|
736
|
+
"items": [
|
|
737
|
+
{
|
|
738
|
+
"id": "REV-001",
|
|
739
|
+
"description": "Anonymization claim unsupported",
|
|
740
|
+
"reviewer": "R1",
|
|
741
|
+
"type": "Major",
|
|
742
|
+
"obligation_class": "must_fix",
|
|
743
|
+
"severity": "critical",
|
|
744
|
+
"target_section": "Ethics",
|
|
745
|
+
"suggested_action": "Document the de-identification procedure",
|
|
746
|
+
"consensus_level": "CONSENSUS-4",
|
|
747
|
+
"verification_criteria": "The de-identification procedure is documented and matches the data release.",
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
"id": "REV-002",
|
|
751
|
+
"description": "Figure captions too terse",
|
|
752
|
+
"reviewer": "R2",
|
|
753
|
+
"type": "Minor",
|
|
754
|
+
"obligation_class": "should_fix",
|
|
755
|
+
"severity": "minor",
|
|
756
|
+
"target_section": "Figures",
|
|
757
|
+
"suggested_action": "Expand captions",
|
|
758
|
+
"consensus_level": "CONSENSUS-3",
|
|
759
|
+
"verification_criteria": "Every figure caption states what the figure shows.",
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
"total_items": 2,
|
|
763
|
+
"must_fix_count": 1,
|
|
764
|
+
"editorial_decision": "Major Revision",
|
|
765
|
+
"consensus_summary": "Ethics documentation dominates.",
|
|
766
|
+
"dissenting_opinions": [],
|
|
767
|
+
}
|
|
768
|
+
letter = (
|
|
769
|
+
"# Editorial Decision\n\n"
|
|
770
|
+
"## Required Revisions * (Must Fix)\n\n"
|
|
771
|
+
"### Required Item Details\n\n"
|
|
772
|
+
"**R1: Anonymization claim unsupported**\n"
|
|
773
|
+
"- **Problem**: The claim is asserted without procedure.\n"
|
|
774
|
+
"- **Requirement**: Document the de-identification steps.\n"
|
|
775
|
+
"- **Acceptance criteria**: The de-identification procedure is documented step by step.\n\n"
|
|
776
|
+
"## Closing\n"
|
|
777
|
+
)
|
|
778
|
+
pre_rev001 = {
|
|
779
|
+
"item_id": "REV-001",
|
|
780
|
+
"obligation_class": "must_fix",
|
|
781
|
+
"inherited_criterion": {
|
|
782
|
+
"roadmap_text": "The de-identification procedure is documented and matches the data release.",
|
|
783
|
+
"letter_text": "The de-identification procedure is documented step by step.",
|
|
784
|
+
"letter_item_ref": "R1",
|
|
785
|
+
},
|
|
786
|
+
"operationalization": {
|
|
787
|
+
"fully_addressed": "Ethics section documents each de-identification step against the release.",
|
|
788
|
+
"partially_addressed": "Steps are documented but not matched to the release.",
|
|
789
|
+
"made_worse_discriminator": "The revision removes the existing ethics statement.",
|
|
790
|
+
},
|
|
791
|
+
"expected_change_surface": "Ethics §6",
|
|
792
|
+
"equivalence_policy": "allowed",
|
|
793
|
+
"source_reviewer": "R1",
|
|
794
|
+
"source_reviewer_labels": ["R1"],
|
|
795
|
+
}
|
|
796
|
+
precommitment = {
|
|
797
|
+
"contract_version": "1.1",
|
|
798
|
+
"round_id": "round-3",
|
|
799
|
+
"input_manifest_hash": "0" * 64,
|
|
800
|
+
"items": [
|
|
801
|
+
pre_rev001,
|
|
802
|
+
{
|
|
803
|
+
"item_id": "REV-002",
|
|
804
|
+
"obligation_class": "should_fix",
|
|
805
|
+
"inherited_criterion": {"roadmap_text": "Every figure caption states what the figure shows."},
|
|
806
|
+
"operationalization": {"fully_addressed": "Each caption names the variable and the takeaway."},
|
|
807
|
+
"expected_change_surface": "Figures",
|
|
808
|
+
"equivalence_policy": "allowed",
|
|
809
|
+
"source_reviewer": "R2",
|
|
810
|
+
"source_reviewer_labels": ["R2"],
|
|
811
|
+
},
|
|
812
|
+
],
|
|
813
|
+
"new_standards": [],
|
|
814
|
+
}
|
|
815
|
+
dissent = {
|
|
816
|
+
"dissent_id": "DIS-1",
|
|
817
|
+
"item_id": "REV-001",
|
|
818
|
+
"criterion_hash": "0" * 64, # recomputed at emit
|
|
819
|
+
"reason_code": "criterion_infeasible_as_written",
|
|
820
|
+
"original_operationalization": "Ethics section documents each de-identification step against the release.",
|
|
821
|
+
"replacement_operationalization": "Ethics section documents the steps; release matching deferred to the data appendix.",
|
|
822
|
+
"evidence": 'text: §6 "procedure"',
|
|
823
|
+
"decision_impact_note": "Release matching moves to the appendix surface.",
|
|
824
|
+
}
|
|
825
|
+
verdict_record = {
|
|
826
|
+
"contract_version": "1.1",
|
|
827
|
+
"round_id": "round-3",
|
|
828
|
+
"precommitment_hash": "0" * 64,
|
|
829
|
+
"items": [
|
|
830
|
+
{
|
|
831
|
+
"item_id": "REV-001",
|
|
832
|
+
"verdict": "FULLY_ADDRESSED",
|
|
833
|
+
"evidence_anchor": ['text: §6 "de-identification steps"'],
|
|
834
|
+
"change_summary": "Ethics section now documents the procedure.",
|
|
835
|
+
"verified_by": "R1",
|
|
836
|
+
"applied_criterion": "dissented:DIS-1",
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
"item_id": "REV-002",
|
|
840
|
+
"verdict": "FULLY_ADDRESSED",
|
|
841
|
+
"evidence_anchor": ['figure: Figure 1'],
|
|
842
|
+
"change_summary": "Captions expanded.",
|
|
843
|
+
"verified_by": "R2",
|
|
844
|
+
"applied_criterion": "precommitted",
|
|
845
|
+
},
|
|
846
|
+
],
|
|
847
|
+
"new_issues": [],
|
|
848
|
+
"dissents": [dissent],
|
|
849
|
+
"escalation_exceptions": [],
|
|
850
|
+
}
|
|
851
|
+
reapplication = {
|
|
852
|
+
"reapplication_id": "RAP-1",
|
|
853
|
+
"item_id": "REV-001",
|
|
854
|
+
"answer_refs": ["adjudication:DIS-1"],
|
|
855
|
+
"pre_reapplication_verdict": "FULLY_ADDRESSED",
|
|
856
|
+
"reapplied_verdict": "CANNOT_VERIFY",
|
|
857
|
+
"cannot_verify_reason": "dispatch_failed: transport timeout",
|
|
858
|
+
"rationale": "The scoped 2B' call could not be dispatched.",
|
|
859
|
+
"criterion_ref": "phase1:REV-001",
|
|
860
|
+
}
|
|
861
|
+
traceability = {
|
|
862
|
+
"contract_version": "1.1",
|
|
863
|
+
"round_id": "round-3",
|
|
864
|
+
"revision": 2 if accepted else 1,
|
|
865
|
+
"verdict_record_hash": "0" * 64,
|
|
866
|
+
"rows": [
|
|
867
|
+
{
|
|
868
|
+
"item_id": "REV-001",
|
|
869
|
+
"concern_id": "R1",
|
|
870
|
+
"obligation_class": "MUST_FIX",
|
|
871
|
+
"original_comment": "The claim is asserted without procedure.",
|
|
872
|
+
"authors_claim": "Procedure documented in §6.",
|
|
873
|
+
"revision_location": "Ethics §6",
|
|
874
|
+
"verified": "CANNOT_VERIFY" if accepted else "YES",
|
|
875
|
+
"status": "CANNOT_VERIFY" if accepted else "FULLY_ADDRESSED",
|
|
876
|
+
"quality_assessment": "Fail-closed under the rejected replacement criterion." if accepted else "Meets the dissented criterion.",
|
|
877
|
+
"final_verdict": "CANNOT_VERIFY" if accepted else "FULLY_ADDRESSED",
|
|
878
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
879
|
+
"verified_by": "R1",
|
|
880
|
+
"cross_model_status": "unavailable",
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
"item_id": "REV-002",
|
|
884
|
+
"concern_id": "S1",
|
|
885
|
+
"obligation_class": "SHOULD_FIX",
|
|
886
|
+
"original_comment": "Captions too terse.",
|
|
887
|
+
"authors_claim": "Expanded.",
|
|
888
|
+
"revision_location": "Figures",
|
|
889
|
+
"verified": "YES",
|
|
890
|
+
"status": "FULLY_ADDRESSED",
|
|
891
|
+
"quality_assessment": "Adequate.",
|
|
892
|
+
"final_verdict": "FULLY_ADDRESSED",
|
|
893
|
+
"phase2a_verdict": "FULLY_ADDRESSED",
|
|
894
|
+
"verified_by": "R2",
|
|
895
|
+
},
|
|
896
|
+
],
|
|
897
|
+
"adjustments": [],
|
|
898
|
+
"new_issues": [],
|
|
899
|
+
"post_letter_observations": [],
|
|
900
|
+
"dissent_adjudications": [
|
|
901
|
+
{
|
|
902
|
+
"dissent_id": "DIS-1",
|
|
903
|
+
"adjudicator": "cross_model",
|
|
904
|
+
"outcome": "original_upheld",
|
|
905
|
+
"rationale": "The original criterion is applicable as written.",
|
|
906
|
+
}
|
|
907
|
+
],
|
|
908
|
+
"resolution_intents": [],
|
|
909
|
+
"cross_model_resolutions": [],
|
|
910
|
+
"rebuttal_adjudications": [],
|
|
911
|
+
"g2d_acceptances": [],
|
|
912
|
+
"pending_rebuttal_upgrades": [],
|
|
913
|
+
"escalation_approvals": [],
|
|
914
|
+
"reapplications": [reapplication],
|
|
915
|
+
"decision_inputs": {
|
|
916
|
+
"per_item": [
|
|
917
|
+
{
|
|
918
|
+
"item_id": "REV-001",
|
|
919
|
+
"final_verdict": "CANNOT_VERIFY" if accepted else "FULLY_ADDRESSED",
|
|
920
|
+
"driving_severity": "critical",
|
|
921
|
+
}
|
|
922
|
+
],
|
|
923
|
+
"verdict_counts": _counts(
|
|
924
|
+
must_fix={"CANNOT_VERIFY": 1} if accepted else {"FULLY_ADDRESSED": 1},
|
|
925
|
+
should_fix={"FULLY_ADDRESSED": 1},
|
|
926
|
+
),
|
|
927
|
+
"residual_obligation_class_counts": _magnitudes(),
|
|
928
|
+
"should_fix_addressed_rate": {"numerator": 1, "denominator": 1},
|
|
929
|
+
"regressions": [],
|
|
930
|
+
"non_regression_new_issue_ids": [],
|
|
931
|
+
"escalations": [],
|
|
932
|
+
"apply_chain_witness": "pass",
|
|
933
|
+
},
|
|
934
|
+
"decision_state": "Major Revision" if accepted else "user_review_required",
|
|
935
|
+
}
|
|
936
|
+
if accepted:
|
|
937
|
+
traceability["supersedes_hash"] = "cd" * 32
|
|
938
|
+
traceability["g2d_acceptances"] = [
|
|
939
|
+
{"acceptance_id": "ACC-1", "item_id": "REV-001", "reapplication_id": "RAP-1", "accepted_by": "user"}
|
|
940
|
+
]
|
|
941
|
+
traceability["adjustments"] = [
|
|
942
|
+
{
|
|
943
|
+
"adjustment_id": "ADJ-1",
|
|
944
|
+
"item_id": "REV-001",
|
|
945
|
+
"from_verdict": "FULLY_ADDRESSED",
|
|
946
|
+
"to_verdict": "CANNOT_VERIFY",
|
|
947
|
+
"basis": "user_accepted_fail_closed",
|
|
948
|
+
"cannot_verify_reason": "dispatch_failed: transport timeout",
|
|
949
|
+
"rationale": "User accepted the fail-closed outcome at the Stage 3' checkpoint.",
|
|
950
|
+
"source_ref": "acceptance:ACC-1",
|
|
951
|
+
}
|
|
952
|
+
]
|
|
953
|
+
traceability["rows"][0]["adjustment_id"] = "ADJ-1"
|
|
954
|
+
traceability["decision_inputs"]["reject_recommended"] = False
|
|
955
|
+
return {
|
|
956
|
+
"cross_model_active": True,
|
|
957
|
+
"roadmap": roadmap,
|
|
958
|
+
"letter": letter,
|
|
959
|
+
"reports": [
|
|
960
|
+
{
|
|
961
|
+
"report_format_version": "1.3",
|
|
962
|
+
"base_draft_hash": ORIGINAL_SHA[:12],
|
|
963
|
+
"output_draft_hash": REVISED_SHA[:12],
|
|
964
|
+
}
|
|
965
|
+
],
|
|
966
|
+
"precommitment": precommitment,
|
|
967
|
+
"verdict_record": verdict_record,
|
|
968
|
+
"traceability": traceability,
|
|
969
|
+
"manifest_overrides": {},
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def scenario_g2d_retry():
|
|
974
|
+
"""G2(d) guided re-examination retry that SUCCEEDS: RAP-1 (CANNOT_VERIFY,
|
|
975
|
+
dispatch_failed) superseded by RAP-2 with CUMULATIVE answer_refs, whose
|
|
976
|
+
derived adjustment moves the row FULLY -> PARTIALLY (§6 deferral loop)."""
|
|
977
|
+
s = scenario_g2d(accepted=False)
|
|
978
|
+
t = s["traceability"]
|
|
979
|
+
t["revision"] = 2
|
|
980
|
+
t["supersedes_hash"] = "ef" * 32
|
|
981
|
+
t["resolution_intents"] = [
|
|
982
|
+
{
|
|
983
|
+
"intent_id": "INT-1",
|
|
984
|
+
"item_id": "REV-001",
|
|
985
|
+
"answered_by": "user",
|
|
986
|
+
"guidance_note": "re-examine against the data appendix",
|
|
987
|
+
}
|
|
988
|
+
]
|
|
989
|
+
rap2_anchors = [_anchor('text: §6 "procedure vs release table"')]
|
|
990
|
+
residual = {"text": "Release matching still undocumented for one dataset.", "residual_obligation_class": "should_fix"}
|
|
991
|
+
rationale = "Re-application under the original criterion finds partial satisfaction."
|
|
992
|
+
t["reapplications"].append(
|
|
993
|
+
{
|
|
994
|
+
"reapplication_id": "RAP-2",
|
|
995
|
+
"item_id": "REV-001",
|
|
996
|
+
"answer_refs": ["adjudication:DIS-1", "intent:INT-1"],
|
|
997
|
+
"supersedes_reapplication_id": "RAP-1",
|
|
998
|
+
"pre_reapplication_verdict": "FULLY_ADDRESSED",
|
|
999
|
+
"reapplied_verdict": "PARTIALLY_ADDRESSED",
|
|
1000
|
+
"evidence_anchor": copy.deepcopy(rap2_anchors),
|
|
1001
|
+
"residual_gap": dict(residual),
|
|
1002
|
+
"rationale": rationale,
|
|
1003
|
+
"criterion_ref": "phase1:REV-001",
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
t["adjustments"] = [
|
|
1007
|
+
{
|
|
1008
|
+
"adjustment_id": "ADJ-1",
|
|
1009
|
+
"item_id": "REV-001",
|
|
1010
|
+
"from_verdict": "FULLY_ADDRESSED",
|
|
1011
|
+
"to_verdict": "PARTIALLY_ADDRESSED",
|
|
1012
|
+
"basis": "cross_model_adjudication",
|
|
1013
|
+
"evidence_anchor": copy.deepcopy(rap2_anchors),
|
|
1014
|
+
"residual_gap": dict(residual),
|
|
1015
|
+
"rationale": rationale,
|
|
1016
|
+
"source_ref": "reapplication:RAP-2",
|
|
1017
|
+
}
|
|
1018
|
+
]
|
|
1019
|
+
t["cross_model_resolutions"] = [
|
|
1020
|
+
{
|
|
1021
|
+
"resolution_id": "RES-1",
|
|
1022
|
+
"item_id": "REV-001",
|
|
1023
|
+
"intent_id": "INT-1",
|
|
1024
|
+
"reapplication_id": "RAP-2",
|
|
1025
|
+
"state": "primary_revised",
|
|
1026
|
+
"resolved_by": "system",
|
|
1027
|
+
"rationale": "Derived mechanically from the retry re-application.",
|
|
1028
|
+
}
|
|
1029
|
+
]
|
|
1030
|
+
row = t["rows"][0]
|
|
1031
|
+
row["final_verdict"] = "PARTIALLY_ADDRESSED"
|
|
1032
|
+
row["status"] = "PARTIALLY_ADDRESSED"
|
|
1033
|
+
row["verified"] = "PARTIAL"
|
|
1034
|
+
row["adjustment_id"] = "ADJ-1"
|
|
1035
|
+
di = t["decision_inputs"]
|
|
1036
|
+
di["per_item"] = [
|
|
1037
|
+
{
|
|
1038
|
+
"item_id": "REV-001",
|
|
1039
|
+
"final_verdict": "PARTIALLY_ADDRESSED",
|
|
1040
|
+
"driving_severity": "critical",
|
|
1041
|
+
"residual_obligation_class": "should_fix",
|
|
1042
|
+
}
|
|
1043
|
+
]
|
|
1044
|
+
di["verdict_counts"] = _counts(
|
|
1045
|
+
must_fix={"PARTIALLY_ADDRESSED": 1}, should_fix={"FULLY_ADDRESSED": 1}
|
|
1046
|
+
)
|
|
1047
|
+
di["residual_obligation_class_counts"] = _magnitudes(must_fix={"should_fix": 1})
|
|
1048
|
+
di["reject_recommended"] = False
|
|
1049
|
+
t["decision_state"] = "Minor Revision"
|
|
1050
|
+
return s
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
# --- emit + run ----------------------------------------------------------------
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def emit(tmp_path: Path, scenario: dict, *, resync: bool = True):
|
|
1057
|
+
"""Write every fixture file, resync hashes, and return the checker argv."""
|
|
1058
|
+
scenario = copy.deepcopy(scenario)
|
|
1059
|
+
|
|
1060
|
+
original_path = tmp_path / "manuscript.v1.md"
|
|
1061
|
+
revised_path = tmp_path / "manuscript.v2.md"
|
|
1062
|
+
original_path.write_bytes(ORIGINAL_BYTES)
|
|
1063
|
+
parsed_original = parse_document(ORIGINAL_BYTES.decode("utf-8"))
|
|
1064
|
+
block_manifest = build_manifest(ORIGINAL_BYTES, parsed_original)
|
|
1065
|
+
block_manifest_path = tmp_path / "block-manifest.json"
|
|
1066
|
+
block_manifest_path.write_text(
|
|
1067
|
+
json.dumps(block_manifest, ensure_ascii=False, indent=2) + "\n",
|
|
1068
|
+
encoding="utf-8",
|
|
1069
|
+
)
|
|
1070
|
+
block_manifest_sha = hashlib.sha256(block_manifest_path.read_bytes()).hexdigest()
|
|
1071
|
+
|
|
1072
|
+
# Scenario builders focus on #576 arithmetic. Upgrade their roadmap rows
|
|
1073
|
+
# to the closed #670 core before writing the exact artifact consumed by
|
|
1074
|
+
# current re-review and its replayed evidence bundle.
|
|
1075
|
+
roadmap_payload = scenario["roadmap"]
|
|
1076
|
+
roadmap_payload.pop("must_fix_count", None)
|
|
1077
|
+
roadmap_payload.update(
|
|
1078
|
+
{
|
|
1079
|
+
"schema_version": "revision-roadmap/1.0",
|
|
1080
|
+
"revision_round": 1,
|
|
1081
|
+
"base_draft_sha256": ORIGINAL_SHA,
|
|
1082
|
+
"block_manifest_sha256": block_manifest_sha,
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
counts = {"must_fix": 0, "should_fix": 0, "consider": 0}
|
|
1086
|
+
for ordinal, item in enumerate(roadmap_payload["items"], start=1):
|
|
1087
|
+
item.pop("type", None)
|
|
1088
|
+
counts[item["obligation_class"]] += 1
|
|
1089
|
+
item["source_refs"] = [
|
|
1090
|
+
{
|
|
1091
|
+
"seat": "R1",
|
|
1092
|
+
"channel": "finding" if "severity" in item else item.get("source_kind", "editorial"),
|
|
1093
|
+
"ordinal": ordinal,
|
|
1094
|
+
"subclaim_ordinal": 0,
|
|
1095
|
+
}
|
|
1096
|
+
]
|
|
1097
|
+
if "severity" in item:
|
|
1098
|
+
item.pop("source_kind", None)
|
|
1099
|
+
item.setdefault(
|
|
1100
|
+
"evidence_anchor",
|
|
1101
|
+
{
|
|
1102
|
+
"anchor_type": "text",
|
|
1103
|
+
"locator": "B0001",
|
|
1104
|
+
"quote": "Original body.",
|
|
1105
|
+
},
|
|
1106
|
+
)
|
|
1107
|
+
item.setdefault("confidence", 4)
|
|
1108
|
+
item.setdefault("competence_basis", "Current re-review fixture basis.")
|
|
1109
|
+
else:
|
|
1110
|
+
item.setdefault("source_kind", "editorial")
|
|
1111
|
+
item.setdefault("cost_scope", {"kind": "sentence", "locator": "B0001"})
|
|
1112
|
+
item.setdefault(
|
|
1113
|
+
"consequence_if_unaddressed",
|
|
1114
|
+
{
|
|
1115
|
+
"code": "acceptance_criterion_unmet",
|
|
1116
|
+
"target": {"kind": "manuscript", "locator": "B0001"},
|
|
1117
|
+
},
|
|
1118
|
+
)
|
|
1119
|
+
item["proposed_targets"] = [
|
|
1120
|
+
{"block_id": "B0001", "allowed_operations": ["replace_block"]}
|
|
1121
|
+
]
|
|
1122
|
+
roadmap_payload["obligation_counts"] = counts
|
|
1123
|
+
roadmap_path = tmp_path / "roadmap.json"
|
|
1124
|
+
roadmap_path.write_text(
|
|
1125
|
+
json.dumps(roadmap_payload, ensure_ascii=False, indent=2) + "\n",
|
|
1126
|
+
encoding="utf-8",
|
|
1127
|
+
)
|
|
1128
|
+
roadmap_sha = hashlib.sha256(roadmap_path.read_bytes()).hexdigest()
|
|
1129
|
+
|
|
1130
|
+
claim_surface = {
|
|
1131
|
+
"schema_version": "claim-surface-manifest/1.0",
|
|
1132
|
+
"revision_round": 1,
|
|
1133
|
+
"roadmap_sha256": roadmap_sha,
|
|
1134
|
+
"base_draft_sha256": ORIGINAL_SHA,
|
|
1135
|
+
"claim_intent_sources": [],
|
|
1136
|
+
"surfaces": [],
|
|
1137
|
+
}
|
|
1138
|
+
claim_surface_path = tmp_path / "claim-surfaces.json"
|
|
1139
|
+
claim_surface_path.write_text(
|
|
1140
|
+
json.dumps(claim_surface, ensure_ascii=False, indent=2) + "\n",
|
|
1141
|
+
encoding="utf-8",
|
|
1142
|
+
)
|
|
1143
|
+
claim_surface_sha = hashlib.sha256(claim_surface_path.read_bytes()).hexdigest()
|
|
1144
|
+
|
|
1145
|
+
event_id = "AUTHOR-EVENT-re-review-fixture"
|
|
1146
|
+
author_records = []
|
|
1147
|
+
author_overrides = scenario.get("author_triage_overrides", {})
|
|
1148
|
+
for item in roadmap_payload["items"]:
|
|
1149
|
+
record = {
|
|
1150
|
+
"item_id": item["id"],
|
|
1151
|
+
"author_event_id": event_id,
|
|
1152
|
+
"author_triage": "will_address",
|
|
1153
|
+
"authorized_targets": [
|
|
1154
|
+
{
|
|
1155
|
+
"block_id": "B0001",
|
|
1156
|
+
"allowed_operations": ["replace_block"],
|
|
1157
|
+
}
|
|
1158
|
+
],
|
|
1159
|
+
"claim_strength_authorizations": [],
|
|
1160
|
+
}
|
|
1161
|
+
record.update(copy.deepcopy(author_overrides.get(item["id"], {})))
|
|
1162
|
+
author_records.append(record)
|
|
1163
|
+
author_adjudication = {
|
|
1164
|
+
"schema_version": "author-adjudication/1.0",
|
|
1165
|
+
"revision_round": 1,
|
|
1166
|
+
"roadmap_sha256": roadmap_sha,
|
|
1167
|
+
"base_draft_sha256": ORIGINAL_SHA,
|
|
1168
|
+
"claim_surface_manifest_sha256": claim_surface_sha,
|
|
1169
|
+
"adjudication_status": "complete",
|
|
1170
|
+
"author_events": [
|
|
1171
|
+
{
|
|
1172
|
+
"event_id": event_id,
|
|
1173
|
+
"source": "explicit_session_user_message",
|
|
1174
|
+
"actor_role": "author",
|
|
1175
|
+
"input_sha256": hashlib.sha256(b"explicit fixture author triage").hexdigest(),
|
|
1176
|
+
}
|
|
1177
|
+
],
|
|
1178
|
+
"display_order": {
|
|
1179
|
+
"mode": "source_traceability",
|
|
1180
|
+
"item_ids": [item["id"] for item in roadmap_payload["items"]],
|
|
1181
|
+
"author_event_id": event_id,
|
|
1182
|
+
},
|
|
1183
|
+
"author_adjudications": author_records,
|
|
1184
|
+
"collateral_authorizations": [],
|
|
1185
|
+
}
|
|
1186
|
+
accepted_ids = [
|
|
1187
|
+
record["item_id"]
|
|
1188
|
+
for record in author_records
|
|
1189
|
+
if record["author_triage"] == "will_address"
|
|
1190
|
+
]
|
|
1191
|
+
if accepted_ids:
|
|
1192
|
+
for index, record in enumerate(author_records, start=1):
|
|
1193
|
+
if record["author_triage"] in ("wont_address", "not_on_point"):
|
|
1194
|
+
author_adjudication["collateral_authorizations"].append(
|
|
1195
|
+
{
|
|
1196
|
+
"authorization_id": f"COLLATERAL-AUTH-fixture-{index}",
|
|
1197
|
+
"author_event_id": event_id,
|
|
1198
|
+
"authorizing_item_id": accepted_ids[0],
|
|
1199
|
+
"constrained_item_id": record["item_id"],
|
|
1200
|
+
"block_id": "B0001",
|
|
1201
|
+
"operation": "replace_block",
|
|
1202
|
+
"reason": "Explicit fixture authority for the shared test block.",
|
|
1203
|
+
}
|
|
1204
|
+
)
|
|
1205
|
+
author_adjudication["display_order"].update(
|
|
1206
|
+
copy.deepcopy(scenario.get("author_display_override", {}))
|
|
1207
|
+
)
|
|
1208
|
+
author_path = tmp_path / "author-adjudication.json"
|
|
1209
|
+
author_path.write_text(
|
|
1210
|
+
json.dumps(author_adjudication, ensure_ascii=False, indent=2) + "\n",
|
|
1211
|
+
encoding="utf-8",
|
|
1212
|
+
)
|
|
1213
|
+
author_by_item = {record["item_id"]: record for record in author_records}
|
|
1214
|
+
for row in scenario["traceability"]["rows"]:
|
|
1215
|
+
author = author_by_item[row["item_id"]]
|
|
1216
|
+
for field in (
|
|
1217
|
+
"author_triage",
|
|
1218
|
+
"authorized_targets",
|
|
1219
|
+
"claim_strength_authorizations",
|
|
1220
|
+
):
|
|
1221
|
+
row.setdefault(field, copy.deepcopy(author[field]))
|
|
1222
|
+
if "author_reason" in author:
|
|
1223
|
+
row.setdefault("author_reason", author["author_reason"])
|
|
1224
|
+
|
|
1225
|
+
argv = []
|
|
1226
|
+
letter_sha = None
|
|
1227
|
+
if scenario["letter"] is not None:
|
|
1228
|
+
letter_path = tmp_path / "letter.md"
|
|
1229
|
+
letter_path.write_text(scenario["letter"], encoding="utf-8")
|
|
1230
|
+
letter_sha = hashlib.sha256(letter_path.read_bytes()).hexdigest()
|
|
1231
|
+
argv += ["--letter", str(letter_path)]
|
|
1232
|
+
|
|
1233
|
+
patch_path = tmp_path / "patch.json"
|
|
1234
|
+
canonical_report_path = tmp_path / "canonical-apply-report.json"
|
|
1235
|
+
will_address_ids = [
|
|
1236
|
+
record["item_id"]
|
|
1237
|
+
for record in author_records
|
|
1238
|
+
if record["author_triage"] == "will_address"
|
|
1239
|
+
]
|
|
1240
|
+
canonical_report = None
|
|
1241
|
+
patch_sha = None
|
|
1242
|
+
if scenario["reports"]:
|
|
1243
|
+
assert will_address_ids, "write-round fixture needs at least one will_address item"
|
|
1244
|
+
patch = {
|
|
1245
|
+
"patch_format_version": "1.1",
|
|
1246
|
+
"authorization_context": "review_roadmap",
|
|
1247
|
+
"revision_round": 1,
|
|
1248
|
+
"base_draft_hash": base_draft_hash(ORIGINAL_BYTES),
|
|
1249
|
+
"roadmap_sha256": roadmap_sha,
|
|
1250
|
+
"author_adjudication_sha256": hashlib.sha256(author_path.read_bytes()).hexdigest(),
|
|
1251
|
+
"author_decision_digest": author_decision_digest(author_adjudication),
|
|
1252
|
+
"claim_surface_manifest_sha256": claim_surface_sha,
|
|
1253
|
+
"ops": [
|
|
1254
|
+
{
|
|
1255
|
+
"op": "replace_block",
|
|
1256
|
+
"block_id": "B0001",
|
|
1257
|
+
"old_hash": parsed_original.block_by_id()["B0001"].norm_hash,
|
|
1258
|
+
"new_text": "Revised body.",
|
|
1259
|
+
"roadmap_item_ids": will_address_ids,
|
|
1260
|
+
"claim_strength_changes": [],
|
|
1261
|
+
"collateral_authorization_ids": [
|
|
1262
|
+
row["authorization_id"]
|
|
1263
|
+
for row in author_adjudication["collateral_authorizations"]
|
|
1264
|
+
],
|
|
1265
|
+
}
|
|
1266
|
+
],
|
|
1267
|
+
"emitted_by": "draft_writer_agent",
|
|
1268
|
+
}
|
|
1269
|
+
patch_path.write_text(
|
|
1270
|
+
json.dumps(patch, ensure_ascii=False, indent=2) + "\n",
|
|
1271
|
+
encoding="utf-8",
|
|
1272
|
+
)
|
|
1273
|
+
patch_sha = hashlib.sha256(patch_path.read_bytes()).hexdigest()
|
|
1274
|
+
canonical_report = apply_revision_patch(
|
|
1275
|
+
original_path,
|
|
1276
|
+
patch_path,
|
|
1277
|
+
revised_path,
|
|
1278
|
+
canonical_report_path,
|
|
1279
|
+
acknowledge_structural=False,
|
|
1280
|
+
touched_ratio_threshold=None,
|
|
1281
|
+
block_manifest_path=block_manifest_path,
|
|
1282
|
+
roadmap_path=roadmap_path,
|
|
1283
|
+
author_adjudication_path=author_path,
|
|
1284
|
+
claim_surface_manifest_path=claim_surface_path,
|
|
1285
|
+
artifact_root=tmp_path,
|
|
1286
|
+
)
|
|
1287
|
+
assert revised_path.read_bytes() == REVISED_BYTES
|
|
1288
|
+
else:
|
|
1289
|
+
revised_path.write_bytes(ORIGINAL_BYTES)
|
|
1290
|
+
|
|
1291
|
+
report_paths = []
|
|
1292
|
+
report_shas = []
|
|
1293
|
+
for i, payload in enumerate(scenario["reports"]):
|
|
1294
|
+
payload = {**(canonical_report or {}), **dict(payload)}
|
|
1295
|
+
try:
|
|
1296
|
+
format_12_plus = tuple(int(p) for p in payload["report_format_version"].split(".")) >= (1, 2)
|
|
1297
|
+
except ValueError:
|
|
1298
|
+
format_12_plus = False
|
|
1299
|
+
if format_12_plus:
|
|
1300
|
+
payload.setdefault("patch_digest", patch_sha)
|
|
1301
|
+
payload.setdefault(
|
|
1302
|
+
"authorization_witness",
|
|
1303
|
+
{
|
|
1304
|
+
"status": "pass",
|
|
1305
|
+
"unregistered_claim_drift_review_required": True,
|
|
1306
|
+
},
|
|
1307
|
+
)
|
|
1308
|
+
path = tmp_path / f"apply-report-{i}.json"
|
|
1309
|
+
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
1310
|
+
report_paths.append(path)
|
|
1311
|
+
report_shas.append(hashlib.sha256(path.read_bytes()).hexdigest())
|
|
1312
|
+
argv += ["--apply-report", str(path)]
|
|
1313
|
+
|
|
1314
|
+
receipt_path = tmp_path / "integrity-pass.json"
|
|
1315
|
+
receipt_path.write_text(
|
|
1316
|
+
json.dumps(
|
|
1317
|
+
{
|
|
1318
|
+
"schema_version": "integrity-pass-receipt/1.0",
|
|
1319
|
+
"receipt_id": "INTEGRITY-PASS-re-review-fixture",
|
|
1320
|
+
"checked_draft_sha256": ORIGINAL_SHA,
|
|
1321
|
+
"verdict": "PASS",
|
|
1322
|
+
"open_issue_count": 0,
|
|
1323
|
+
"issued_by": "integrity_verification_agent",
|
|
1324
|
+
},
|
|
1325
|
+
indent=2,
|
|
1326
|
+
)
|
|
1327
|
+
+ "\n",
|
|
1328
|
+
encoding="utf-8",
|
|
1329
|
+
)
|
|
1330
|
+
|
|
1331
|
+
def artifact(path: Path) -> dict:
|
|
1332
|
+
return {
|
|
1333
|
+
"path": path.relative_to(tmp_path).as_posix(),
|
|
1334
|
+
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
if scenario["reports"]:
|
|
1338
|
+
bundle_round = {
|
|
1339
|
+
"kind": "review_roadmap",
|
|
1340
|
+
"revision_round": 1,
|
|
1341
|
+
"pre_round_draft": artifact(original_path),
|
|
1342
|
+
"pre_round_block_manifest": artifact(block_manifest_path),
|
|
1343
|
+
"revision_roadmap": artifact(roadmap_path),
|
|
1344
|
+
"claim_surface_manifest": artifact(claim_surface_path),
|
|
1345
|
+
"author_adjudication": artifact(author_path),
|
|
1346
|
+
"revision_patch": artifact(patch_path),
|
|
1347
|
+
"apply_report": artifact(report_paths[0]),
|
|
1348
|
+
"post_round_draft": artifact(revised_path),
|
|
1349
|
+
}
|
|
1350
|
+
else:
|
|
1351
|
+
bundle_round = {
|
|
1352
|
+
"kind": "review_noop",
|
|
1353
|
+
"revision_round": 1,
|
|
1354
|
+
"pre_round_draft": artifact(original_path),
|
|
1355
|
+
"pre_round_block_manifest": artifact(block_manifest_path),
|
|
1356
|
+
"revision_roadmap": artifact(roadmap_path),
|
|
1357
|
+
"claim_surface_manifest": artifact(claim_surface_path),
|
|
1358
|
+
"author_adjudication": artifact(author_path),
|
|
1359
|
+
"post_round_draft": artifact(revised_path),
|
|
1360
|
+
}
|
|
1361
|
+
bundle = {
|
|
1362
|
+
"schema_version": "revision-evidence-bundle/1.0",
|
|
1363
|
+
"chain_start": {
|
|
1364
|
+
"first_revision_round": 1,
|
|
1365
|
+
"draft": artifact(original_path),
|
|
1366
|
+
"block_manifest": artifact(block_manifest_path),
|
|
1367
|
+
"integrity_pass_receipt": artifact(receipt_path),
|
|
1368
|
+
},
|
|
1369
|
+
"rounds": [bundle_round],
|
|
1370
|
+
"final_draft": artifact(revised_path),
|
|
1371
|
+
}
|
|
1372
|
+
bundle_path = tmp_path / "revision-evidence-bundle.json"
|
|
1373
|
+
bundle_path.write_text(
|
|
1374
|
+
json.dumps(bundle, ensure_ascii=False, indent=2) + "\n",
|
|
1375
|
+
encoding="utf-8",
|
|
1376
|
+
)
|
|
1377
|
+
|
|
1378
|
+
# Invalid top-level sidecar mutations are written only after the valid
|
|
1379
|
+
# apply/bundle fixture exists, so the checker—not fixture construction—
|
|
1380
|
+
# owns the expected failure.
|
|
1381
|
+
author_adjudication.update(copy.deepcopy(scenario.get("author_top_overrides", {})))
|
|
1382
|
+
author_path.write_text(
|
|
1383
|
+
json.dumps(author_adjudication, ensure_ascii=False, indent=2) + "\n",
|
|
1384
|
+
encoding="utf-8",
|
|
1385
|
+
)
|
|
1386
|
+
author_sha = hashlib.sha256(author_path.read_bytes()).hexdigest()
|
|
1387
|
+
bundle_sha = hashlib.sha256(bundle_path.read_bytes()).hexdigest()
|
|
1388
|
+
revised_sha = hashlib.sha256(revised_path.read_bytes()).hexdigest()
|
|
1389
|
+
|
|
1390
|
+
manifest = {
|
|
1391
|
+
"contract_version": "1.1",
|
|
1392
|
+
"round_id": scenario["precommitment"]["round_id"],
|
|
1393
|
+
"cross_model_active": scenario["cross_model_active"],
|
|
1394
|
+
"artifacts": {
|
|
1395
|
+
"original_manuscript": _entry("path:manuscript.v1.md", ORIGINAL_SHA),
|
|
1396
|
+
"revised_manuscript": _entry("path:manuscript.v2.md", revised_sha),
|
|
1397
|
+
"revision_roadmap": _entry("path:roadmap.json", roadmap_sha),
|
|
1398
|
+
"author_adjudication": _entry(
|
|
1399
|
+
"path:author-adjudication.json", author_sha
|
|
1400
|
+
),
|
|
1401
|
+
"revision_evidence_bundle": _entry(
|
|
1402
|
+
"path:revision-evidence-bundle.json", bundle_sha
|
|
1403
|
+
),
|
|
1404
|
+
"editorial_decision_letter": (
|
|
1405
|
+
_entry("path:letter.md", letter_sha) if letter_sha else {"present": False}
|
|
1406
|
+
),
|
|
1407
|
+
"response_to_reviewers": _entry("path:response.md", SYNTH_SHA),
|
|
1408
|
+
"revision_patches": {
|
|
1409
|
+
"present": True,
|
|
1410
|
+
"items": [
|
|
1411
|
+
{"path_or_passport_ref": "path:patch.json", "sha256": patch_sha,
|
|
1412
|
+
"version_label": None, "origin_date": None}
|
|
1413
|
+
],
|
|
1414
|
+
} if scenario["reports"] else {"present": False},
|
|
1415
|
+
"apply_reports": {
|
|
1416
|
+
"present": True,
|
|
1417
|
+
"items": [
|
|
1418
|
+
{"path_or_passport_ref": f"path:apply-report-{i}.json", "sha256": sha,
|
|
1419
|
+
"version_label": None, "origin_date": None}
|
|
1420
|
+
for i, sha in enumerate(report_shas)
|
|
1421
|
+
],
|
|
1422
|
+
} if scenario["reports"] else {"present": False},
|
|
1423
|
+
"round1_findings": _entry("path:findings.md", SYNTH_SHA),
|
|
1424
|
+
"round1_config_cards": _entry("path:cards.md", SYNTH_SHA),
|
|
1425
|
+
},
|
|
1426
|
+
}
|
|
1427
|
+
for key, value in scenario["manifest_overrides"].items():
|
|
1428
|
+
manifest["artifacts"][key] = value
|
|
1429
|
+
if manifest["artifacts"]["revision_patches"]["present"]:
|
|
1430
|
+
for item in manifest["artifacts"]["revision_patches"]["items"]:
|
|
1431
|
+
if item["sha256"] == PATCH_SHA:
|
|
1432
|
+
item["sha256"] = patch_sha
|
|
1433
|
+
|
|
1434
|
+
if resync:
|
|
1435
|
+
pre_by_item = {rec["item_id"]: rec for rec in scenario["precommitment"]["items"]}
|
|
1436
|
+
for dissent in scenario["verdict_record"]["dissents"]:
|
|
1437
|
+
target = pre_by_item.get(dissent["item_id"])
|
|
1438
|
+
if target is not None and dissent["criterion_hash"] == "0" * 64:
|
|
1439
|
+
dissent["criterion_hash"] = crs.canonical_hash(target)
|
|
1440
|
+
scenario["precommitment"]["input_manifest_hash"] = crs.canonical_hash(manifest)
|
|
1441
|
+
scenario["verdict_record"]["precommitment_hash"] = crs.canonical_hash(scenario["precommitment"])
|
|
1442
|
+
scenario["traceability"]["verdict_record_hash"] = crs.canonical_hash(scenario["verdict_record"])
|
|
1443
|
+
|
|
1444
|
+
manifest_path = tmp_path / "manifest.json"
|
|
1445
|
+
manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
1446
|
+
for name, artifact in (
|
|
1447
|
+
("precommitment", scenario["precommitment"]),
|
|
1448
|
+
("verdict_record", scenario["verdict_record"]),
|
|
1449
|
+
("traceability", scenario["traceability"]),
|
|
1450
|
+
):
|
|
1451
|
+
(tmp_path / f"{name}.json").write_text(json.dumps(artifact, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
1452
|
+
|
|
1453
|
+
argv = [
|
|
1454
|
+
"--manifest", str(manifest_path),
|
|
1455
|
+
"--precommitment", str(tmp_path / "precommitment.json"),
|
|
1456
|
+
"--verdict-record", str(tmp_path / "verdict_record.json"),
|
|
1457
|
+
"--traceability", str(tmp_path / "traceability.json"),
|
|
1458
|
+
"--roadmap", str(roadmap_path),
|
|
1459
|
+
"--author-adjudication", str(author_path),
|
|
1460
|
+
"--revision-evidence-bundle", str(bundle_path),
|
|
1461
|
+
"--revision-evidence-root", str(tmp_path),
|
|
1462
|
+
] + argv
|
|
1463
|
+
return argv, manifest
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
def run_checker(tmp_path, scenario, capsys, *, resync=True):
|
|
1467
|
+
argv, _manifest = emit(tmp_path, scenario, resync=resync)
|
|
1468
|
+
code = crs.run(argv)
|
|
1469
|
+
captured = capsys.readouterr()
|
|
1470
|
+
return code, captured.out, captured.err
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
def assert_exit2(tmp_path, scenario, capsys, reason, needle=None):
|
|
1474
|
+
code, out, _err = run_checker(tmp_path, scenario, capsys)
|
|
1475
|
+
assert code == crs.EXIT_INVALID, out
|
|
1476
|
+
assert f"[RE-REVIEW-ABORT: {reason}]" in out
|
|
1477
|
+
if needle:
|
|
1478
|
+
assert needle in out
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
def assert_mismatch(tmp_path, scenario, capsys, needle):
|
|
1482
|
+
code, out, _err = run_checker(tmp_path, scenario, capsys)
|
|
1483
|
+
assert code == crs.EXIT_SYNTHESIS, out
|
|
1484
|
+
assert "[RE-REVIEW-ABORT: synthesis_mismatch]" in out
|
|
1485
|
+
assert needle in out, out
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
# --- golden scenarios pass -----------------------------------------------------
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
def test_golden_accept_passes(tmp_path, capsys):
|
|
1492
|
+
code, out, err = run_checker(tmp_path, scenario_accept(), capsys)
|
|
1493
|
+
assert code == crs.EXIT_PASS, out
|
|
1494
|
+
assert "re-review synthesis ok" in out
|
|
1495
|
+
assert "'Accept'" in out
|
|
1496
|
+
assert err == ""
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
def test_golden_complex_minor_passes(tmp_path, capsys):
|
|
1500
|
+
code, out, err = run_checker(tmp_path, scenario_complex(), capsys)
|
|
1501
|
+
assert code == crs.EXIT_PASS, out
|
|
1502
|
+
assert "'Minor Revision'" in out
|
|
1503
|
+
# the regression advisory names NEW-1's nearest item only when non-null;
|
|
1504
|
+
# NEW-1 carries null here, so no advisory fires
|
|
1505
|
+
assert "ADVISORY" not in err
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
def test_golden_g2d_deferred_passes(tmp_path, capsys):
|
|
1509
|
+
code, out, _err = run_checker(tmp_path, scenario_g2d(accepted=False), capsys)
|
|
1510
|
+
assert code == crs.EXIT_PASS, out
|
|
1511
|
+
assert "'user_review_required'" in out
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
def test_golden_g2d_accepted_passes(tmp_path, capsys):
|
|
1515
|
+
code, out, _err = run_checker(tmp_path, scenario_g2d(accepted=True), capsys)
|
|
1516
|
+
assert code == crs.EXIT_PASS, out
|
|
1517
|
+
assert "'Major Revision'" in out
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
def test_golden_artifacts_validate_against_shipped_schemas(tmp_path, capsys):
|
|
1521
|
+
for i, scenario in enumerate(
|
|
1522
|
+
(scenario_accept(), scenario_complex(), scenario_g2d(True), scenario_g2d(False), scenario_g2d_retry())
|
|
1523
|
+
):
|
|
1524
|
+
subdir = tmp_path / f"s{i}"
|
|
1525
|
+
subdir.mkdir()
|
|
1526
|
+
argv, manifest = emit(subdir, scenario)
|
|
1527
|
+
files = dict(zip(argv[::2], argv[1::2]))
|
|
1528
|
+
for schema_name, path_key in (
|
|
1529
|
+
("precommitment", "--precommitment"),
|
|
1530
|
+
("verdict_record", "--verdict-record"),
|
|
1531
|
+
("traceability", "--traceability"),
|
|
1532
|
+
):
|
|
1533
|
+
schema = json.loads((SCHEMA_DIR / f"{schema_name}.schema.json").read_text(encoding="utf-8"))
|
|
1534
|
+
payload = json.loads(Path(files[path_key]).read_text(encoding="utf-8"))
|
|
1535
|
+
jsonschema.Draft202012Validator(schema).validate(payload)
|
|
1536
|
+
manifest_schema = json.loads((SCHEMA_DIR / "input_manifest.schema.json").read_text(encoding="utf-8"))
|
|
1537
|
+
jsonschema.Draft202012Validator(manifest_schema).validate(manifest)
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
# --- exit-2 class: manifest layer ---------------------------------------------
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
def test_missing_hard_required_artifact_is_manifest_incomplete(tmp_path, capsys):
|
|
1544
|
+
s = scenario_accept()
|
|
1545
|
+
s["manifest_overrides"]["revised_manuscript"] = {"present": False}
|
|
1546
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "hard-required artifact revised_manuscript")
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
def test_apply_reports_without_patches_is_manifest_incomplete(tmp_path, capsys):
|
|
1550
|
+
s = scenario_accept()
|
|
1551
|
+
s["manifest_overrides"]["revision_patches"] = {"present": False}
|
|
1552
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "must travel together")
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
def test_patches_without_reports_is_manifest_incomplete_even_for_noop_bundle(
|
|
1556
|
+
tmp_path, capsys
|
|
1557
|
+
):
|
|
1558
|
+
s = scenario_accept()
|
|
1559
|
+
s["reports"] = []
|
|
1560
|
+
s["author_triage_overrides"] = {
|
|
1561
|
+
item["id"]: {
|
|
1562
|
+
"author_triage": "wont_address",
|
|
1563
|
+
"author_reason": "The author explicitly declines this fixture item.",
|
|
1564
|
+
"authorized_targets": [],
|
|
1565
|
+
"claim_strength_authorizations": [],
|
|
1566
|
+
}
|
|
1567
|
+
for item in s["roadmap"]["items"]
|
|
1568
|
+
}
|
|
1569
|
+
s["traceability"]["decision_inputs"][
|
|
1570
|
+
"apply_chain_witness"
|
|
1571
|
+
] = "not_run_no_reports"
|
|
1572
|
+
s["manifest_overrides"]["revision_patches"] = {
|
|
1573
|
+
"present": True,
|
|
1574
|
+
"items": [
|
|
1575
|
+
{
|
|
1576
|
+
"path_or_passport_ref": "path:patch.json",
|
|
1577
|
+
"sha256": SYNTH_SHA,
|
|
1578
|
+
"version_label": None,
|
|
1579
|
+
"origin_date": None,
|
|
1580
|
+
}
|
|
1581
|
+
],
|
|
1582
|
+
}
|
|
1583
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "must travel together")
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
def test_array_length_mismatch_is_manifest_incomplete(tmp_path, capsys):
|
|
1587
|
+
s = scenario_accept()
|
|
1588
|
+
s["manifest_overrides"]["revision_patches"] = {
|
|
1589
|
+
"present": True,
|
|
1590
|
+
"items": [
|
|
1591
|
+
{"path_or_passport_ref": "path:patch.json", "sha256": PATCH_SHA, "version_label": None, "origin_date": None},
|
|
1592
|
+
{"path_or_passport_ref": "path:patch2.json", "sha256": SYNTH_SHA, "version_label": None, "origin_date": None},
|
|
1593
|
+
],
|
|
1594
|
+
}
|
|
1595
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "length mismatch")
|
|
1596
|
+
|
|
1597
|
+
|
|
1598
|
+
def test_passport_ref_with_null_freshness_is_manifest_incomplete(tmp_path, capsys):
|
|
1599
|
+
s = scenario_accept()
|
|
1600
|
+
s["manifest_overrides"]["round1_findings"] = _entry("passport:findings-ref", SYNTH_SHA)
|
|
1601
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "passport:-tagged")
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
def test_absent_artifact_with_fields_is_manifest_incomplete(tmp_path, capsys):
|
|
1605
|
+
s = scenario_accept()
|
|
1606
|
+
s["manifest_overrides"]["round1_findings"] = {"present": False, "sha256": SYNTH_SHA}
|
|
1607
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "NO ref, hash, or freshness fields")
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
def test_roadmap_older_than_letter_fails_closed(tmp_path, capsys):
|
|
1611
|
+
s = scenario_accept()
|
|
1612
|
+
seed = tmp_path / "seed"
|
|
1613
|
+
seed.mkdir()
|
|
1614
|
+
emit(seed, s)
|
|
1615
|
+
# rebuild with passport-tagged freshness on roadmap + letter
|
|
1616
|
+
roadmap_sha = hashlib.sha256((seed / "roadmap.json").read_bytes()).hexdigest()
|
|
1617
|
+
letter_sha = hashlib.sha256((seed / "letter.md").read_bytes()).hexdigest()
|
|
1618
|
+
s["manifest_overrides"]["revision_roadmap"] = _entry("passport:roadmap", roadmap_sha, "v1", "2026-07-01")
|
|
1619
|
+
s["manifest_overrides"]["editorial_decision_letter"] = _entry("passport:letter", letter_sha, "v1", "2026-07-15")
|
|
1620
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "freshness")
|
|
1621
|
+
|
|
1622
|
+
|
|
1623
|
+
def test_roadmap_file_hash_mismatch(tmp_path, capsys):
|
|
1624
|
+
s = scenario_accept()
|
|
1625
|
+
s["manifest_overrides"]["revision_roadmap"] = _entry("path:roadmap.json", SYNTH_SHA)
|
|
1626
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "revision_roadmap file")
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
def test_patch_digest_mismatch_is_content_binding_failure(tmp_path, capsys):
|
|
1630
|
+
s = scenario_accept()
|
|
1631
|
+
s["reports"][0]["patch_digest"] = SYNTH_SHA
|
|
1632
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "patch_digest")
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
def test_broken_apply_chain_last_link(tmp_path, capsys):
|
|
1636
|
+
s = scenario_accept()
|
|
1637
|
+
s["reports"][0]["output_draft_hash"] = "deadbeef0123"
|
|
1638
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "revised_manuscript hash prefix")
|
|
1639
|
+
|
|
1640
|
+
|
|
1641
|
+
def test_broken_apply_chain_first_link(tmp_path, capsys):
|
|
1642
|
+
s = scenario_accept()
|
|
1643
|
+
s["reports"][0]["base_draft_hash"] = "deadbeef0123"
|
|
1644
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "original_manuscript hash prefix")
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
def test_broken_apply_chain_inner_link(tmp_path, capsys):
|
|
1648
|
+
s = scenario_accept()
|
|
1649
|
+
s["reports"] = [
|
|
1650
|
+
{"report_format_version": "1.3", "base_draft_hash": ORIGINAL_SHA[:12], "output_draft_hash": "aaaaaaaaaaaa"},
|
|
1651
|
+
{"report_format_version": "1.3", "base_draft_hash": "bbbbbbbbbbbb", "output_draft_hash": REVISED_SHA[:12]},
|
|
1652
|
+
]
|
|
1653
|
+
s["manifest_overrides"]["revision_patches"] = {
|
|
1654
|
+
"present": True,
|
|
1655
|
+
"items": [
|
|
1656
|
+
{"path_or_passport_ref": "path:patch.json", "sha256": PATCH_SHA, "version_label": None, "origin_date": None},
|
|
1657
|
+
{"path_or_passport_ref": "path:patch2.json", "sha256": PATCH_SHA, "version_label": None, "origin_date": None},
|
|
1658
|
+
],
|
|
1659
|
+
}
|
|
1660
|
+
assert_exit2(tmp_path, s, capsys, "manifest_hash_mismatch", "output_draft_hash")
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
def test_bundle_binding_requires_exact_ordered_write_pair_projection():
|
|
1664
|
+
roadmap_sha = "1" * 64
|
|
1665
|
+
author_sha = "2" * 64
|
|
1666
|
+
original_sha = "3" * 64
|
|
1667
|
+
final_sha = "4" * 64
|
|
1668
|
+
first_patch = "5" * 64
|
|
1669
|
+
first_report = "6" * 64
|
|
1670
|
+
second_patch = "7" * 64
|
|
1671
|
+
second_report = "8" * 64
|
|
1672
|
+
bundle = {
|
|
1673
|
+
"final_draft": {"sha256": final_sha},
|
|
1674
|
+
"rounds": [
|
|
1675
|
+
{
|
|
1676
|
+
"kind": "review_roadmap",
|
|
1677
|
+
"pre_round_draft": {"sha256": original_sha},
|
|
1678
|
+
"revision_roadmap": {"sha256": roadmap_sha},
|
|
1679
|
+
"author_adjudication": {"sha256": author_sha},
|
|
1680
|
+
"revision_patch": {"sha256": first_patch},
|
|
1681
|
+
"apply_report": {"sha256": first_report},
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
"kind": "integrity_correction",
|
|
1685
|
+
"revision_patch": {"sha256": second_patch},
|
|
1686
|
+
"apply_report": {"sha256": second_report},
|
|
1687
|
+
},
|
|
1688
|
+
],
|
|
1689
|
+
}
|
|
1690
|
+
manifest = {
|
|
1691
|
+
"artifacts": {
|
|
1692
|
+
"revised_manuscript": {"sha256": final_sha},
|
|
1693
|
+
"revision_roadmap": {"sha256": roadmap_sha},
|
|
1694
|
+
"author_adjudication": {"sha256": author_sha},
|
|
1695
|
+
"original_manuscript": {"present": True, "sha256": original_sha},
|
|
1696
|
+
"revision_patches": {
|
|
1697
|
+
"present": True,
|
|
1698
|
+
"items": [{"sha256": first_patch}, {"sha256": second_patch}],
|
|
1699
|
+
},
|
|
1700
|
+
"apply_reports": {
|
|
1701
|
+
"present": True,
|
|
1702
|
+
"items": [{"sha256": first_report}, {"sha256": second_report}],
|
|
1703
|
+
},
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
crs.validate_revision_bundle_binding(bundle, manifest)
|
|
1707
|
+
|
|
1708
|
+
for name, mutated in (
|
|
1709
|
+
(
|
|
1710
|
+
"omitted",
|
|
1711
|
+
[
|
|
1712
|
+
{"sha256": first_patch},
|
|
1713
|
+
],
|
|
1714
|
+
),
|
|
1715
|
+
(
|
|
1716
|
+
"reordered",
|
|
1717
|
+
[
|
|
1718
|
+
{"sha256": second_patch},
|
|
1719
|
+
{"sha256": first_patch},
|
|
1720
|
+
],
|
|
1721
|
+
),
|
|
1722
|
+
(
|
|
1723
|
+
"substituted",
|
|
1724
|
+
[
|
|
1725
|
+
{"sha256": first_patch},
|
|
1726
|
+
{"sha256": "9" * 64},
|
|
1727
|
+
],
|
|
1728
|
+
),
|
|
1729
|
+
):
|
|
1730
|
+
with pytest.raises(crs.ManifestError, match="ordered write patch/report pairs"):
|
|
1731
|
+
changed = copy.deepcopy(manifest)
|
|
1732
|
+
changed["artifacts"]["revision_patches"]["items"] = mutated
|
|
1733
|
+
if name == "omitted":
|
|
1734
|
+
changed["artifacts"]["apply_reports"]["items"] = [
|
|
1735
|
+
{"sha256": first_report}
|
|
1736
|
+
]
|
|
1737
|
+
crs.validate_revision_bundle_binding(bundle, changed)
|
|
1738
|
+
|
|
1739
|
+
|
|
1740
|
+
def test_format_12_report_without_patch_digest_is_incomplete(tmp_path, capsys):
|
|
1741
|
+
s = scenario_accept()
|
|
1742
|
+
s["reports"][0]["patch_digest"] = None # blocks the setdefault fill
|
|
1743
|
+
argv, _m = emit(tmp_path, s)
|
|
1744
|
+
payload = json.loads((tmp_path / "apply-report-0.json").read_text())
|
|
1745
|
+
del payload["patch_digest"]
|
|
1746
|
+
(tmp_path / "apply-report-0.json").write_text(json.dumps(payload, indent=2))
|
|
1747
|
+
# re-sync the manifest hash for the rewritten report so ONLY the missing
|
|
1748
|
+
# digest fires (not the file-hash binding)
|
|
1749
|
+
manifest = json.loads((tmp_path / "manifest.json").read_text())
|
|
1750
|
+
manifest["artifacts"]["apply_reports"]["items"][0]["sha256"] = hashlib.sha256(
|
|
1751
|
+
(tmp_path / "apply-report-0.json").read_bytes()
|
|
1752
|
+
).hexdigest()
|
|
1753
|
+
(tmp_path / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2))
|
|
1754
|
+
pre = json.loads((tmp_path / "precommitment.json").read_text())
|
|
1755
|
+
pre["input_manifest_hash"] = crs.canonical_hash(manifest)
|
|
1756
|
+
(tmp_path / "precommitment.json").write_text(json.dumps(pre, ensure_ascii=False, indent=2))
|
|
1757
|
+
vr = json.loads((tmp_path / "verdict_record.json").read_text())
|
|
1758
|
+
vr["precommitment_hash"] = crs.canonical_hash(pre)
|
|
1759
|
+
(tmp_path / "verdict_record.json").write_text(json.dumps(vr, ensure_ascii=False, indent=2))
|
|
1760
|
+
tr = json.loads((tmp_path / "traceability.json").read_text())
|
|
1761
|
+
tr["verdict_record_hash"] = crs.canonical_hash(vr)
|
|
1762
|
+
(tmp_path / "traceability.json").write_text(json.dumps(tr, ensure_ascii=False, indent=2))
|
|
1763
|
+
code = crs.run(argv)
|
|
1764
|
+
out = capsys.readouterr().out
|
|
1765
|
+
assert code == crs.EXIT_INVALID
|
|
1766
|
+
assert "[RE-REVIEW-ABORT: manifest_incomplete]" in out
|
|
1767
|
+
assert "requires patch_digest" in out
|
|
1768
|
+
|
|
1769
|
+
|
|
1770
|
+
def test_legacy_report_is_rejected_by_current_contract(tmp_path, capsys):
|
|
1771
|
+
s = scenario_accept()
|
|
1772
|
+
s["reports"][0] = {
|
|
1773
|
+
"report_format_version": "1.1",
|
|
1774
|
+
"base_draft_hash": ORIGINAL_SHA[:12],
|
|
1775
|
+
"output_draft_hash": REVISED_SHA[:12],
|
|
1776
|
+
}
|
|
1777
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
1778
|
+
assert code == crs.EXIT_INVALID, out
|
|
1779
|
+
assert "current contract requires report format 1.3" in out
|
|
1780
|
+
assert "archived loader" in out
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
@pytest.mark.parametrize(
|
|
1784
|
+
"artifact_name",
|
|
1785
|
+
("manifest", "precommitment", "verdict_record", "traceability"),
|
|
1786
|
+
)
|
|
1787
|
+
def test_current_checker_rejects_every_mixed_legacy_contract_version(
|
|
1788
|
+
tmp_path, capsys, artifact_name
|
|
1789
|
+
):
|
|
1790
|
+
argv, _manifest = emit(tmp_path, scenario_accept())
|
|
1791
|
+
path = tmp_path / f"{artifact_name}.json"
|
|
1792
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
1793
|
+
payload["contract_version"] = "1.0"
|
|
1794
|
+
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
1795
|
+
code = crs.run(argv)
|
|
1796
|
+
out = capsys.readouterr().out
|
|
1797
|
+
assert code == crs.EXIT_INVALID, out
|
|
1798
|
+
assert "1.1" in out
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
@pytest.mark.parametrize("artifact_name", ("author_adjudication", "revision_evidence_bundle"))
|
|
1802
|
+
def test_current_manifest_hard_requires_author_and_bundle_artifacts(
|
|
1803
|
+
tmp_path, capsys, artifact_name
|
|
1804
|
+
):
|
|
1805
|
+
scenario = scenario_accept()
|
|
1806
|
+
scenario["manifest_overrides"][artifact_name] = {"present": False}
|
|
1807
|
+
assert_exit2(
|
|
1808
|
+
tmp_path,
|
|
1809
|
+
scenario,
|
|
1810
|
+
capsys,
|
|
1811
|
+
"manifest_incomplete",
|
|
1812
|
+
f"hard-required artifact {artifact_name} absent",
|
|
1813
|
+
)
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
def test_current_checker_requires_existing_bundle_file(tmp_path, capsys):
|
|
1817
|
+
argv, _manifest = emit(tmp_path, scenario_accept())
|
|
1818
|
+
(tmp_path / "revision-evidence-bundle.json").unlink()
|
|
1819
|
+
code = crs.run(argv)
|
|
1820
|
+
out = capsys.readouterr().out
|
|
1821
|
+
assert code == crs.EXIT_INVALID, out
|
|
1822
|
+
assert "[RE-REVIEW-ABORT: manifest_incomplete]" in out
|
|
1823
|
+
assert "revision_evidence_bundle file" in out
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def test_current_checker_rejects_bundle_file_hash_drift(tmp_path, capsys):
|
|
1827
|
+
argv, _manifest = emit(tmp_path, scenario_accept())
|
|
1828
|
+
bundle_path = tmp_path / "revision-evidence-bundle.json"
|
|
1829
|
+
bundle_path.write_bytes(bundle_path.read_bytes() + b"\n")
|
|
1830
|
+
code = crs.run(argv)
|
|
1831
|
+
out = capsys.readouterr().out
|
|
1832
|
+
assert code == crs.EXIT_INVALID, out
|
|
1833
|
+
assert "[RE-REVIEW-ABORT: manifest_hash_mismatch]" in out
|
|
1834
|
+
assert "revision_evidence_bundle file" in out
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
def test_author_sidecar_raw_roadmap_binding_rejects_substitution(tmp_path, capsys):
|
|
1838
|
+
scenario = scenario_accept()
|
|
1839
|
+
scenario["author_top_overrides"] = {"roadmap_sha256": "f" * 64}
|
|
1840
|
+
assert_exit2(
|
|
1841
|
+
tmp_path,
|
|
1842
|
+
scenario,
|
|
1843
|
+
capsys,
|
|
1844
|
+
"manifest_hash_mismatch",
|
|
1845
|
+
"does not match the exact roadmap bytes",
|
|
1846
|
+
)
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
def test_traceability_author_triage_must_equal_bound_sidecar(tmp_path, capsys):
|
|
1850
|
+
scenario = scenario_accept()
|
|
1851
|
+
scenario["traceability"]["rows"][0].update(
|
|
1852
|
+
{
|
|
1853
|
+
"author_triage": "wont_address",
|
|
1854
|
+
"author_reason": "A forged downstream decline.",
|
|
1855
|
+
"authorized_targets": [],
|
|
1856
|
+
"claim_strength_authorizations": [],
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1859
|
+
assert_mismatch(
|
|
1860
|
+
tmp_path,
|
|
1861
|
+
scenario,
|
|
1862
|
+
capsys,
|
|
1863
|
+
"author_triage is not an exact copy",
|
|
1864
|
+
)
|
|
1865
|
+
|
|
1866
|
+
|
|
1867
|
+
def test_declined_author_reason_roundtrips_into_traceability(tmp_path, capsys):
|
|
1868
|
+
scenario = scenario_accept()
|
|
1869
|
+
scenario["author_triage_overrides"] = {
|
|
1870
|
+
"REV-002": {
|
|
1871
|
+
"author_triage": "not_on_point",
|
|
1872
|
+
"author_reason": "The author explicitly states that this request is out of scope.",
|
|
1873
|
+
"authorized_targets": [],
|
|
1874
|
+
"claim_strength_authorizations": [],
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
code, out, _err = run_checker(tmp_path, scenario, capsys)
|
|
1878
|
+
assert code == crs.EXIT_PASS, out
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
def test_user_display_order_cannot_change_roadmap_or_letter_transport_order(
|
|
1882
|
+
tmp_path, capsys
|
|
1883
|
+
):
|
|
1884
|
+
scenario = scenario_accept()
|
|
1885
|
+
scenario["author_display_override"] = {
|
|
1886
|
+
"mode": "user_selected",
|
|
1887
|
+
"item_ids": ["REV-002", "REV-001"],
|
|
1888
|
+
}
|
|
1889
|
+
code, out, _err = run_checker(tmp_path, scenario, capsys)
|
|
1890
|
+
assert code == crs.EXIT_PASS, out
|
|
1891
|
+
# The letter still joins R1 to the first must_fix item in immutable
|
|
1892
|
+
# roadmap order; the author's view permutation is presentation-only.
|
|
1893
|
+
assert "'Accept'" in out
|
|
1894
|
+
|
|
1895
|
+
|
|
1896
|
+
def test_no_reports_is_not_run_no_reports_witness(tmp_path, capsys):
|
|
1897
|
+
s = scenario_accept()
|
|
1898
|
+
s["reports"] = []
|
|
1899
|
+
s["author_triage_overrides"] = {
|
|
1900
|
+
item["id"]: {
|
|
1901
|
+
"author_triage": "wont_address",
|
|
1902
|
+
"author_reason": "The author explicitly declines this fixture item.",
|
|
1903
|
+
"authorized_targets": [],
|
|
1904
|
+
"claim_strength_authorizations": [],
|
|
1905
|
+
}
|
|
1906
|
+
for item in s["roadmap"]["items"]
|
|
1907
|
+
}
|
|
1908
|
+
s["traceability"]["decision_inputs"]["apply_chain_witness"] = "not_run_no_reports"
|
|
1909
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
1910
|
+
assert code == crs.EXIT_PASS, out
|
|
1911
|
+
assert "'not_run_no_reports'" in out
|
|
1912
|
+
|
|
1913
|
+
|
|
1914
|
+
def test_absent_original_is_manifest_incomplete_in_current_contract(tmp_path, capsys):
|
|
1915
|
+
s = scenario_accept()
|
|
1916
|
+
s["manifest_overrides"]["original_manuscript"] = {"present": False}
|
|
1917
|
+
assert_exit2(
|
|
1918
|
+
tmp_path,
|
|
1919
|
+
s,
|
|
1920
|
+
capsys,
|
|
1921
|
+
"manifest_incomplete",
|
|
1922
|
+
"hard-required artifact original_manuscript",
|
|
1923
|
+
)
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
def test_letter_declared_but_not_provided_is_incomplete(tmp_path, capsys):
|
|
1927
|
+
s = scenario_accept()
|
|
1928
|
+
argv, _m = emit(tmp_path, s)
|
|
1929
|
+
idx = argv.index("--letter")
|
|
1930
|
+
del argv[idx:idx + 2]
|
|
1931
|
+
code = crs.run(argv)
|
|
1932
|
+
out = capsys.readouterr().out
|
|
1933
|
+
assert code == crs.EXIT_INVALID
|
|
1934
|
+
assert "--letter is required" in out
|
|
1935
|
+
|
|
1936
|
+
|
|
1937
|
+
# --- exit-2 class: hash chain --------------------------------------------------
|
|
1938
|
+
|
|
1939
|
+
|
|
1940
|
+
def test_stale_input_manifest_hash(tmp_path, capsys):
|
|
1941
|
+
s = scenario_accept()
|
|
1942
|
+
argv, _m = emit(tmp_path, s)
|
|
1943
|
+
pre = json.loads((tmp_path / "precommitment.json").read_text())
|
|
1944
|
+
pre["input_manifest_hash"] = "f" * 64
|
|
1945
|
+
(tmp_path / "precommitment.json").write_text(json.dumps(pre, ensure_ascii=False))
|
|
1946
|
+
code = crs.run(argv)
|
|
1947
|
+
out = capsys.readouterr().out
|
|
1948
|
+
assert code == crs.EXIT_INVALID
|
|
1949
|
+
assert "input_manifest_hash does not recompute" in out
|
|
1950
|
+
|
|
1951
|
+
|
|
1952
|
+
def test_stale_precommitment_hash(tmp_path, capsys):
|
|
1953
|
+
s = scenario_accept()
|
|
1954
|
+
argv, _m = emit(tmp_path, s)
|
|
1955
|
+
vr = json.loads((tmp_path / "verdict_record.json").read_text())
|
|
1956
|
+
vr["precommitment_hash"] = "f" * 64
|
|
1957
|
+
(tmp_path / "verdict_record.json").write_text(json.dumps(vr, ensure_ascii=False))
|
|
1958
|
+
code = crs.run(argv)
|
|
1959
|
+
out = capsys.readouterr().out
|
|
1960
|
+
assert code == crs.EXIT_INVALID
|
|
1961
|
+
assert "precommitment_hash does not recompute" in out
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
def test_stale_verdict_record_hash(tmp_path, capsys):
|
|
1965
|
+
s = scenario_accept()
|
|
1966
|
+
argv, _m = emit(tmp_path, s)
|
|
1967
|
+
tr = json.loads((tmp_path / "traceability.json").read_text())
|
|
1968
|
+
tr["verdict_record_hash"] = "f" * 64
|
|
1969
|
+
(tmp_path / "traceability.json").write_text(json.dumps(tr, ensure_ascii=False))
|
|
1970
|
+
code = crs.run(argv)
|
|
1971
|
+
out = capsys.readouterr().out
|
|
1972
|
+
assert code == crs.EXIT_INVALID
|
|
1973
|
+
assert "verdict_record_hash does not recompute" in out
|
|
1974
|
+
|
|
1975
|
+
|
|
1976
|
+
# --- exit-2 class: phase-artifact schemas -------------------------------------
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
def test_invalid_precommitment_is_phase1_lint(tmp_path, capsys):
|
|
1980
|
+
s = scenario_accept()
|
|
1981
|
+
s["precommitment"]["items"][1]["operationalization"]["made_worse_discriminator"] = "forbidden on P2"
|
|
1982
|
+
assert_exit2(tmp_path, s, capsys, "phase1_lint_failed", "should_fix lighter form")
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
def test_invalid_verdict_record_is_phase2a_lint(tmp_path, capsys):
|
|
1986
|
+
s = scenario_accept()
|
|
1987
|
+
s["verdict_record"]["items"][0]["verdict"] = "PARTIALLY_ADDRESSED" # no residual_gap
|
|
1988
|
+
assert_exit2(tmp_path, s, capsys, "phase2a_lint_failed", "requires residual_gap")
|
|
1989
|
+
|
|
1990
|
+
|
|
1991
|
+
def test_invalid_traceability_is_phase2b_lint(tmp_path, capsys):
|
|
1992
|
+
s = scenario_accept()
|
|
1993
|
+
s["traceability"]["decision_state"] = "aborted" # abort_reason missing
|
|
1994
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "requires abort_reason")
|
|
1995
|
+
|
|
1996
|
+
|
|
1997
|
+
def test_letter_fields_on_p2_item_is_phase1_lint(tmp_path, capsys):
|
|
1998
|
+
s = scenario_accept()
|
|
1999
|
+
s["precommitment"]["items"][1]["inherited_criterion"]["letter_text"] = "no"
|
|
2000
|
+
s["precommitment"]["items"][1]["inherited_criterion"]["letter_item_ref"] = "R2"
|
|
2001
|
+
assert_exit2(tmp_path, s, capsys, "phase1_lint_failed", "ONLY on must_fix")
|
|
2002
|
+
|
|
2003
|
+
|
|
2004
|
+
def test_letter_field_biconditional_is_phase1_lint(tmp_path, capsys):
|
|
2005
|
+
s = scenario_accept()
|
|
2006
|
+
del s["precommitment"]["items"][0]["inherited_criterion"]["letter_item_ref"]
|
|
2007
|
+
assert_exit2(tmp_path, s, capsys, "phase1_lint_failed", "biconditional")
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
def test_supersedes_hash_biconditional_is_phase2b_lint(tmp_path, capsys):
|
|
2011
|
+
s = scenario_accept()
|
|
2012
|
+
s["traceability"]["supersedes_hash"] = "ab" * 32 # revision 1
|
|
2013
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "revision 1")
|
|
2014
|
+
|
|
2015
|
+
|
|
2016
|
+
def test_cannot_verify_adjustment_off_basis_is_phase2b_lint(tmp_path, capsys):
|
|
2017
|
+
s = scenario_g2d(accepted=True)
|
|
2018
|
+
s["traceability"]["adjustments"][0]["basis"] = "scope_correction"
|
|
2019
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "ONLY basis")
|
|
2020
|
+
|
|
2021
|
+
|
|
2022
|
+
def test_source_ref_forbidden_off_basis_is_phase2b_lint(tmp_path, capsys):
|
|
2023
|
+
s = scenario_complex()
|
|
2024
|
+
s["traceability"]["adjustments"][0]["source_ref"] = "reapplication:RAP-1"
|
|
2025
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "FORBIDDEN")
|
|
2026
|
+
|
|
2027
|
+
|
|
2028
|
+
def test_manuscript_only_anchor_rule_is_phase2b_lint(tmp_path, capsys):
|
|
2029
|
+
s = scenario_complex()
|
|
2030
|
+
adj = s["traceability"]["adjustments"][1]
|
|
2031
|
+
adj["basis"] = "scope_correction"
|
|
2032
|
+
adj.pop("source_ref")
|
|
2033
|
+
adj["evidence_anchor"] = [_anchor('text: letter "claim"', "letter")]
|
|
2034
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "manuscript-side anchors")
|
|
2035
|
+
|
|
2036
|
+
|
|
2037
|
+
def test_p1_row_without_cross_model_status_is_phase2b_lint(tmp_path, capsys):
|
|
2038
|
+
s = scenario_accept()
|
|
2039
|
+
del s["traceability"]["rows"][0]["cross_model_status"]
|
|
2040
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "MUST_FIX rows always carry")
|
|
2041
|
+
|
|
2042
|
+
|
|
2043
|
+
def test_p2_row_with_cross_model_field_is_phase2b_lint(tmp_path, capsys):
|
|
2044
|
+
s = scenario_accept()
|
|
2045
|
+
s["traceability"]["rows"][1]["cross_model_status"] = "not_configured"
|
|
2046
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "neither cross-model field")
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
def test_system_intent_with_guidance_note_is_phase2b_lint(tmp_path, capsys):
|
|
2050
|
+
s = scenario_complex()
|
|
2051
|
+
s["traceability"]["resolution_intents"][0]["guidance_note"] = "look in the appendix"
|
|
2052
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "guidance_note exists only on user intents")
|
|
2053
|
+
|
|
2054
|
+
|
|
2055
|
+
def test_user_resolution_hand_set_state_is_phase2b_lint(tmp_path, capsys):
|
|
2056
|
+
s = scenario_complex()
|
|
2057
|
+
s["traceability"]["cross_model_resolutions"][0]["resolved_by"] = "user"
|
|
2058
|
+
s["traceability"]["cross_model_resolutions"][0]["state"] = "primary_revised"
|
|
2059
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "never hand-sets a verdict")
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
# --- exit-1 class: criterion inheritance binding (§4/§5.1) ---------------------
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
def test_roadmap_text_must_match_verbatim(tmp_path, capsys):
|
|
2066
|
+
s = scenario_accept()
|
|
2067
|
+
s["precommitment"]["items"][0]["inherited_criterion"]["roadmap_text"] = "A softer paraphrase."
|
|
2068
|
+
assert_mismatch(tmp_path, s, capsys, "roadmap_text does not match the roadmap verbatim")
|
|
2069
|
+
|
|
2070
|
+
|
|
2071
|
+
def test_letter_text_must_match_byte_for_byte(tmp_path, capsys):
|
|
2072
|
+
s = scenario_accept()
|
|
2073
|
+
s["precommitment"]["items"][0]["inherited_criterion"]["letter_text"] = "A formal power analysis appears somewhere."
|
|
2074
|
+
assert_mismatch(tmp_path, s, capsys, "letter_text does not match")
|
|
2075
|
+
|
|
2076
|
+
|
|
2077
|
+
def test_letter_item_ref_is_derived_never_chosen(tmp_path, capsys):
|
|
2078
|
+
s = scenario_complex()
|
|
2079
|
+
item0 = s["precommitment"]["items"][0]["inherited_criterion"]
|
|
2080
|
+
item0["letter_item_ref"] = "R2"
|
|
2081
|
+
item0["letter_text"] = "A robustness table covers both alternative specifications."
|
|
2082
|
+
assert_mismatch(tmp_path, s, capsys, "derived ordinal")
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
def test_letter_ordinal_gap_degrades_whole_letter_layer(tmp_path, capsys):
|
|
2086
|
+
s = scenario_complex()
|
|
2087
|
+
s["letter"] = s["letter"].replace("**R2: Missing robustness checks**", "**R3: Missing robustness checks**")
|
|
2088
|
+
code, out, err = run_checker(tmp_path, s, capsys)
|
|
2089
|
+
assert code == crs.EXIT_SYNTHESIS
|
|
2090
|
+
assert "[CRITERIA-LAYER-ABSENT: letter/roadmap ordinal mismatch]" in err
|
|
2091
|
+
assert "letter fields must be absent" in out
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
def test_letter_fields_absent_when_layer_degraded_passes(tmp_path, capsys):
|
|
2095
|
+
s = scenario_complex()
|
|
2096
|
+
s["letter"] = s["letter"].replace("**R2: Missing robustness checks**", "**R3: Missing robustness checks**")
|
|
2097
|
+
for rec in s["precommitment"]["items"]:
|
|
2098
|
+
rec["inherited_criterion"].pop("letter_text", None)
|
|
2099
|
+
rec["inherited_criterion"].pop("letter_item_ref", None)
|
|
2100
|
+
code, out, err = run_checker(tmp_path, s, capsys)
|
|
2101
|
+
assert code == crs.EXIT_PASS, out
|
|
2102
|
+
assert "[CRITERIA-LAYER-ABSENT: letter/roadmap ordinal mismatch]" in err
|
|
2103
|
+
|
|
2104
|
+
|
|
2105
|
+
def test_letter_block_present_requires_letter_fields(tmp_path, capsys):
|
|
2106
|
+
s = scenario_accept()
|
|
2107
|
+
crit = s["precommitment"]["items"][0]["inherited_criterion"]
|
|
2108
|
+
del crit["letter_text"]
|
|
2109
|
+
del crit["letter_item_ref"]
|
|
2110
|
+
assert_mismatch(tmp_path, s, capsys, "letter_text/letter_item_ref REQUIRED")
|
|
2111
|
+
|
|
2112
|
+
|
|
2113
|
+
def test_precommitment_coverage_missing_p2(tmp_path, capsys):
|
|
2114
|
+
s = scenario_accept()
|
|
2115
|
+
del s["precommitment"]["items"][1]
|
|
2116
|
+
assert_mismatch(tmp_path, s, capsys, "precommitment coverage")
|
|
2117
|
+
|
|
2118
|
+
|
|
2119
|
+
def test_precommitment_record_for_p3_item_is_extra(tmp_path, capsys):
|
|
2120
|
+
s = scenario_complex()
|
|
2121
|
+
s["precommitment"]["items"].append({
|
|
2122
|
+
"item_id": "REV-004",
|
|
2123
|
+
"obligation_class": "should_fix",
|
|
2124
|
+
"inherited_criterion": {"roadmap_text": "A pipeline figure exists or the omission is reasonable."},
|
|
2125
|
+
"operationalization": {"fully_addressed": "A pipeline figure exists."},
|
|
2126
|
+
"expected_change_surface": "Methods",
|
|
2127
|
+
"equivalence_policy": "allowed",
|
|
2128
|
+
"source_reviewer": "EIC",
|
|
2129
|
+
"source_reviewer_labels": ["EIC"],
|
|
2130
|
+
})
|
|
2131
|
+
assert_mismatch(tmp_path, s, capsys, "precommitment coverage")
|
|
2132
|
+
|
|
2133
|
+
|
|
2134
|
+
def test_source_reviewer_labels_must_recompute(tmp_path, capsys):
|
|
2135
|
+
s = scenario_accept()
|
|
2136
|
+
s["precommitment"]["items"][0]["source_reviewer_labels"] = ["R2"]
|
|
2137
|
+
assert_mismatch(tmp_path, s, capsys, "§10 normalization")
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
def test_round_id_equality(tmp_path, capsys):
|
|
2141
|
+
s = scenario_accept()
|
|
2142
|
+
s["verdict_record"]["round_id"] = "round-999"
|
|
2143
|
+
assert_mismatch(tmp_path, s, capsys, "round_id != manifest.round_id")
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
# --- exit-1 class: Phase 2A invariants ----------------------------------------
|
|
2147
|
+
|
|
2148
|
+
|
|
2149
|
+
def test_verdict_record_coverage_all_priorities(tmp_path, capsys):
|
|
2150
|
+
s = scenario_complex()
|
|
2151
|
+
del s["verdict_record"]["items"][3] # REV-004 (consider)
|
|
2152
|
+
assert_mismatch(tmp_path, s, capsys, "verdict_record coverage")
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
def test_p3_item_must_be_not_precommitted(tmp_path, capsys):
|
|
2156
|
+
s = scenario_complex()
|
|
2157
|
+
s["verdict_record"]["items"][3]["applied_criterion"] = "precommitted"
|
|
2158
|
+
assert_mismatch(tmp_path, s, capsys, "consider items carry applied_criterion not_precommitted")
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
def test_p1_item_must_not_be_not_precommitted(tmp_path, capsys):
|
|
2162
|
+
s = scenario_accept()
|
|
2163
|
+
s["verdict_record"]["items"][0]["applied_criterion"] = "not_precommitted"
|
|
2164
|
+
assert_mismatch(tmp_path, s, capsys, "valid ONLY for consider items")
|
|
2165
|
+
|
|
2166
|
+
|
|
2167
|
+
def test_dissent_ref_must_resolve_on_same_item(tmp_path, capsys):
|
|
2168
|
+
s = scenario_accept()
|
|
2169
|
+
s["verdict_record"]["items"][0]["applied_criterion"] = "dissented:DIS-9"
|
|
2170
|
+
assert_mismatch(tmp_path, s, capsys, "does not resolve to a dissent on this item")
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
def test_dissent_criterion_hash_recomputes(tmp_path, capsys):
|
|
2174
|
+
s = scenario_complex()
|
|
2175
|
+
s["verdict_record"]["dissents"][0]["criterion_hash"] = "e" * 64
|
|
2176
|
+
assert_mismatch(tmp_path, s, capsys, "criterion_hash does not recompute")
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
def test_new_standard_ref_must_resolve(tmp_path, capsys):
|
|
2180
|
+
s = scenario_complex()
|
|
2181
|
+
s["verdict_record"]["escalation_exceptions"][0]["new_standard_ref"] = "NS-9"
|
|
2182
|
+
assert_mismatch(tmp_path, s, capsys, "new_standard_ref 'NS-9' does not resolve")
|
|
2183
|
+
|
|
2184
|
+
|
|
2185
|
+
def test_new_standard_ref_target_must_be_escalation_requested(tmp_path, capsys):
|
|
2186
|
+
s = scenario_complex()
|
|
2187
|
+
s["precommitment"]["new_standards"][0]["classification"] = "advisory"
|
|
2188
|
+
assert_mismatch(tmp_path, s, capsys, "not escalation_requested")
|
|
2189
|
+
|
|
2190
|
+
|
|
2191
|
+
# --- exit-1 class: freeze witness + rows ---------------------------------------
|
|
2192
|
+
|
|
2193
|
+
|
|
2194
|
+
def test_no_original_manuscript_is_rejected_at_manifest_gate(tmp_path, capsys):
|
|
2195
|
+
s = scenario_complex()
|
|
2196
|
+
s["manifest_overrides"]["original_manuscript"] = {"present": False}
|
|
2197
|
+
assert_exit2(
|
|
2198
|
+
tmp_path,
|
|
2199
|
+
s,
|
|
2200
|
+
capsys,
|
|
2201
|
+
"manifest_incomplete",
|
|
2202
|
+
"hard-required artifact original_manuscript",
|
|
2203
|
+
)
|
|
2204
|
+
|
|
2205
|
+
|
|
2206
|
+
def test_new_issue_freeze_is_whole_record(tmp_path, capsys):
|
|
2207
|
+
s = scenario_complex()
|
|
2208
|
+
s["traceability"]["new_issues"][0]["found_by"] = "R2" # edit one frozen field
|
|
2209
|
+
assert_mismatch(tmp_path, s, capsys, "WHOLE-RECORD byte-identical")
|
|
2210
|
+
|
|
2211
|
+
|
|
2212
|
+
def test_new_issue_reclassification_breaks_freeze(tmp_path, capsys):
|
|
2213
|
+
s = scenario_complex()
|
|
2214
|
+
s["traceability"]["new_issues"][1]["attribution"] = "regression"
|
|
2215
|
+
assert_mismatch(tmp_path, s, capsys, "WHOLE-RECORD byte-identical")
|
|
2216
|
+
|
|
2217
|
+
|
|
2218
|
+
def test_row_coverage_every_item(tmp_path, capsys):
|
|
2219
|
+
s = scenario_complex()
|
|
2220
|
+
del s["traceability"]["rows"][3]
|
|
2221
|
+
assert_mismatch(tmp_path, s, capsys, "row coverage")
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
def test_row_phase2a_verdict_binds_to_committed_record(tmp_path, capsys):
|
|
2225
|
+
s = scenario_accept()
|
|
2226
|
+
s["traceability"]["rows"][1]["phase2a_verdict"] = "NOT_ADDRESSED"
|
|
2227
|
+
assert_mismatch(tmp_path, s, capsys, "phase2a_verdict != committed")
|
|
2228
|
+
|
|
2229
|
+
|
|
2230
|
+
def test_row_verified_by_copied_from_2a(tmp_path, capsys):
|
|
2231
|
+
s = scenario_accept()
|
|
2232
|
+
s["traceability"]["rows"][0]["verified_by"] = "EIC"
|
|
2233
|
+
assert_mismatch(tmp_path, s, capsys, "verified_by not copied")
|
|
2234
|
+
|
|
2235
|
+
|
|
2236
|
+
def test_status_maps_1_to_1_from_final_verdict(tmp_path, capsys):
|
|
2237
|
+
s = scenario_accept()
|
|
2238
|
+
s["traceability"]["rows"][0]["status"] = "PARTIALLY_ADDRESSED"
|
|
2239
|
+
assert_mismatch(tmp_path, s, capsys, "status must map 1:1")
|
|
2240
|
+
|
|
2241
|
+
|
|
2242
|
+
def test_verified_follows_mechanical_map(tmp_path, capsys):
|
|
2243
|
+
s = scenario_accept()
|
|
2244
|
+
s["traceability"]["rows"][0]["verified"] = "PARTIAL"
|
|
2245
|
+
assert_mismatch(tmp_path, s, capsys, "mechanical 5->4 map")
|
|
2246
|
+
|
|
2247
|
+
|
|
2248
|
+
def test_cross_model_status_rederives_per_emission(tmp_path, capsys):
|
|
2249
|
+
s = scenario_complex()
|
|
2250
|
+
s["traceability"]["rows"][0]["cross_model_status"] = "diverges" # cmv == final => agree
|
|
2251
|
+
assert_mismatch(tmp_path, s, capsys, "per-emission derivation")
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
def test_row_obligation_class_binds_to_roadmap(tmp_path, capsys):
|
|
2255
|
+
s = scenario_accept()
|
|
2256
|
+
s["traceability"]["rows"][1]["obligation_class"] = "CONSIDER"
|
|
2257
|
+
assert_mismatch(tmp_path, s, capsys, "does not match the roadmap")
|
|
2258
|
+
|
|
2259
|
+
|
|
2260
|
+
# --- exit-1 class: adjustment chains -------------------------------------------
|
|
2261
|
+
|
|
2262
|
+
|
|
2263
|
+
def test_g1_silent_verdict_change_is_criteria_drift(tmp_path, capsys):
|
|
2264
|
+
s = scenario_accept()
|
|
2265
|
+
row = s["traceability"]["rows"][1]
|
|
2266
|
+
row["final_verdict"] = "NOT_ADDRESSED"
|
|
2267
|
+
row["status"] = "NOT_ADDRESSED"
|
|
2268
|
+
row["verified"] = "NO"
|
|
2269
|
+
# keep DecisionInputs consistent so the G1 message is the load-bearing one
|
|
2270
|
+
di = s["traceability"]["decision_inputs"]
|
|
2271
|
+
di["verdict_counts"]["should_fix"] = {v: 0 for v in crs.VERDICTS}
|
|
2272
|
+
di["verdict_counts"]["should_fix"]["NOT_ADDRESSED"] = 1
|
|
2273
|
+
di["should_fix_addressed_rate"] = {"numerator": 0, "denominator": 1}
|
|
2274
|
+
assert_mismatch(tmp_path, s, capsys, "[RE-REVIEW-ABORT: criteria_drift]")
|
|
2275
|
+
|
|
2276
|
+
|
|
2277
|
+
def test_chain_head_starts_at_phase2a(tmp_path, capsys):
|
|
2278
|
+
s = scenario_complex()
|
|
2279
|
+
s["traceability"]["adjustments"][0]["from_verdict"] = "NOT_ADDRESSED"
|
|
2280
|
+
assert_mismatch(tmp_path, s, capsys, "head from_verdict != phase2a_verdict")
|
|
2281
|
+
|
|
2282
|
+
|
|
2283
|
+
def test_chain_tail_ends_at_final(tmp_path, capsys):
|
|
2284
|
+
s = scenario_g2d(accepted=True)
|
|
2285
|
+
s["traceability"]["rows"][0]["final_verdict"] = "NOT_ADDRESSED"
|
|
2286
|
+
s["traceability"]["rows"][0]["status"] = "NOT_ADDRESSED"
|
|
2287
|
+
s["traceability"]["rows"][0]["verified"] = "NO"
|
|
2288
|
+
assert_mismatch(tmp_path, s, capsys, "tail to_verdict != final_verdict")
|
|
2289
|
+
|
|
2290
|
+
|
|
2291
|
+
def test_adjustment_id_names_latest_record(tmp_path, capsys):
|
|
2292
|
+
s = scenario_complex()
|
|
2293
|
+
s["traceability"]["rows"][0]["adjustment_id"] = "ADJ-2"
|
|
2294
|
+
assert_mismatch(tmp_path, s, capsys, "adjustment_id must name the chain's LATEST record")
|
|
2295
|
+
|
|
2296
|
+
|
|
2297
|
+
def test_adjustment_id_present_iff_chain_exists(tmp_path, capsys):
|
|
2298
|
+
s = scenario_accept()
|
|
2299
|
+
s["traceability"]["rows"][0]["adjustment_id"] = "ADJ-1"
|
|
2300
|
+
assert_mismatch(tmp_path, s, capsys, "adjustment_id present with no adjustment chain")
|
|
2301
|
+
|
|
2302
|
+
|
|
2303
|
+
def test_head_to_tail_join(tmp_path, capsys):
|
|
2304
|
+
s = scenario_complex()
|
|
2305
|
+
adj3 = {
|
|
2306
|
+
"adjustment_id": "ADJ-3",
|
|
2307
|
+
"item_id": "REV-001",
|
|
2308
|
+
"from_verdict": "NOT_ADDRESSED", # breaks the join (predecessor to = FULLY)
|
|
2309
|
+
"to_verdict": "FULLY_ADDRESSED",
|
|
2310
|
+
"basis": "scope_correction",
|
|
2311
|
+
"evidence_anchor": [_anchor()],
|
|
2312
|
+
"rationale": "The letter reveals the 2A reading misread the target.",
|
|
2313
|
+
"supersedes_adjustment_id": "ADJ-1",
|
|
2314
|
+
}
|
|
2315
|
+
s["traceability"]["adjustments"].append(adj3)
|
|
2316
|
+
s["traceability"]["rows"][0]["adjustment_id"] = "ADJ-3"
|
|
2317
|
+
assert_mismatch(tmp_path, s, capsys, "head-to-tail join")
|
|
2318
|
+
|
|
2319
|
+
|
|
2320
|
+
def test_addressed_by_rebuttal_marker_tracks_chain_tail(tmp_path, capsys):
|
|
2321
|
+
s = scenario_complex()
|
|
2322
|
+
del s["traceability"]["rows"][0]["addressed_by_rebuttal"]
|
|
2323
|
+
assert_mismatch(tmp_path, s, capsys, "addressed_by_rebuttal")
|
|
2324
|
+
|
|
2325
|
+
|
|
2326
|
+
# --- exit-1 class: critical-rebuttal machinery ---------------------------------
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
def test_critical_rebuttal_check_presence_biconditional(tmp_path, capsys):
|
|
2330
|
+
s = scenario_complex()
|
|
2331
|
+
del s["traceability"]["adjustments"][0]["critical_rebuttal_check"]
|
|
2332
|
+
assert_mismatch(tmp_path, s, capsys, "critical_rebuttal_check present iff")
|
|
2333
|
+
|
|
2334
|
+
|
|
2335
|
+
def test_single_family_disclosed_invalid_when_active(tmp_path, capsys):
|
|
2336
|
+
s = scenario_complex()
|
|
2337
|
+
s["traceability"]["adjustments"][0]["critical_rebuttal_check"] = "single_family_disclosed"
|
|
2338
|
+
s["traceability"]["pending_rebuttal_upgrades"][0]["disposition"] = "booked_single_family:ADJ-1"
|
|
2339
|
+
assert_mismatch(tmp_path, s, capsys, "single_family_disclosed is invalid")
|
|
2340
|
+
|
|
2341
|
+
|
|
2342
|
+
def test_adjudicated_ref_must_resolve_upheld(tmp_path, capsys):
|
|
2343
|
+
s = scenario_complex()
|
|
2344
|
+
s["traceability"]["rebuttal_adjudications"][0]["verdict"] = "challenged"
|
|
2345
|
+
assert_mismatch(tmp_path, s, capsys, "upheld RebuttalAdjudication")
|
|
2346
|
+
|
|
2347
|
+
|
|
2348
|
+
def test_booked_proposal_content_equality(tmp_path, capsys):
|
|
2349
|
+
s = scenario_complex()
|
|
2350
|
+
s["traceability"]["pending_rebuttal_upgrades"][0]["drafted_adjustment"]["rationale"] = "A different drafted rationale."
|
|
2351
|
+
assert_mismatch(tmp_path, s, capsys, "content-equal the drafted body")
|
|
2352
|
+
|
|
2353
|
+
|
|
2354
|
+
def test_critical_rebuttal_adjustment_requires_booked_proposal(tmp_path, capsys):
|
|
2355
|
+
s = scenario_complex()
|
|
2356
|
+
s["traceability"]["pending_rebuttal_upgrades"] = []
|
|
2357
|
+
assert_mismatch(tmp_path, s, capsys, "traces to exactly one booked* PendingRebuttalUpgrade")
|
|
2358
|
+
|
|
2359
|
+
|
|
2360
|
+
def test_challenged_disposition_requires_challenged_adjudication(tmp_path, capsys):
|
|
2361
|
+
s = scenario_complex()
|
|
2362
|
+
# keep RADJ-1 upheld (referenced by ADJ-1) and add a challenged proposal
|
|
2363
|
+
# pointing at it: the disposition/verdict cross-check must fire
|
|
2364
|
+
s["traceability"]["pending_rebuttal_upgrades"].append({
|
|
2365
|
+
"proposal_id": "PRB-2",
|
|
2366
|
+
"item_id": "REV-001",
|
|
2367
|
+
"drafted_adjustment": copy.deepcopy(s["traceability"]["pending_rebuttal_upgrades"][0]["drafted_adjustment"]),
|
|
2368
|
+
"disposition": "challenged:RADJ-1",
|
|
2369
|
+
})
|
|
2370
|
+
assert_mismatch(tmp_path, s, capsys, "must reference a challenged adjudication")
|
|
2371
|
+
|
|
2372
|
+
|
|
2373
|
+
def test_rebuttal_adjudication_requires_active_cross_model(tmp_path, capsys):
|
|
2374
|
+
s = scenario_complex()
|
|
2375
|
+
s["cross_model_active"] = False
|
|
2376
|
+
# strip the other active-implying surfaces so THIS invariant is the target
|
|
2377
|
+
for row in s["traceability"]["rows"]:
|
|
2378
|
+
if "cross_model_status" in row and row["cross_model_status"] != "not_configured":
|
|
2379
|
+
row["cross_model_status"] = "not_configured"
|
|
2380
|
+
row.pop("cross_model_verdict", None)
|
|
2381
|
+
assert_mismatch(tmp_path, s, capsys, "exists only under active cross-model")
|
|
2382
|
+
|
|
2383
|
+
|
|
2384
|
+
def test_agree_status_requires_active_cross_model(tmp_path, capsys):
|
|
2385
|
+
s = scenario_accept()
|
|
2386
|
+
s["traceability"]["rows"][0]["cross_model_status"] = "agree"
|
|
2387
|
+
s["traceability"]["rows"][0]["cross_model_verdict"] = "FULLY_ADDRESSED"
|
|
2388
|
+
assert_mismatch(tmp_path, s, capsys, "implies an active configuration")
|
|
2389
|
+
|
|
2390
|
+
|
|
2391
|
+
# --- exit-1 class: deferral-loop referential integrity -------------------------
|
|
2392
|
+
|
|
2393
|
+
|
|
2394
|
+
def test_answer_ref_must_resolve(tmp_path, capsys):
|
|
2395
|
+
s = scenario_complex()
|
|
2396
|
+
s["traceability"]["reapplications"][0]["answer_refs"] = ["intent:INT-9"]
|
|
2397
|
+
assert_mismatch(tmp_path, s, capsys, "does not resolve")
|
|
2398
|
+
|
|
2399
|
+
|
|
2400
|
+
def test_intent_appears_in_exactly_one_current_record(tmp_path, capsys):
|
|
2401
|
+
s = scenario_complex()
|
|
2402
|
+
s["traceability"]["reapplications"] = []
|
|
2403
|
+
s["traceability"]["cross_model_resolutions"] = []
|
|
2404
|
+
s["traceability"]["adjustments"] = [s["traceability"]["adjustments"][0]] # drop ADJ-2
|
|
2405
|
+
row = s["traceability"]["rows"][1]
|
|
2406
|
+
row["final_verdict"] = "NOT_ADDRESSED"
|
|
2407
|
+
row["status"] = "NOT_ADDRESSED"
|
|
2408
|
+
row["verified"] = "NO"
|
|
2409
|
+
row.pop("adjustment_id")
|
|
2410
|
+
row["cross_model_status"] = "diverges"
|
|
2411
|
+
di = s["traceability"]["decision_inputs"]
|
|
2412
|
+
di["per_item"][1]["final_verdict"] = "NOT_ADDRESSED"
|
|
2413
|
+
di["verdict_counts"]["must_fix"] = {v: 0 for v in crs.VERDICTS}
|
|
2414
|
+
di["verdict_counts"]["must_fix"].update({"FULLY_ADDRESSED": 1, "NOT_ADDRESSED": 1})
|
|
2415
|
+
di.pop("reject_recommended")
|
|
2416
|
+
s["traceability"]["decision_state"] = "user_review_required"
|
|
2417
|
+
assert_mismatch(tmp_path, s, capsys, "exactly one CURRENT reapplication's answer_refs")
|
|
2418
|
+
|
|
2419
|
+
|
|
2420
|
+
def test_original_upheld_adjudication_must_be_discharged(tmp_path, capsys):
|
|
2421
|
+
s = scenario_g2d(accepted=False)
|
|
2422
|
+
s["traceability"]["reapplications"] = []
|
|
2423
|
+
assert_mismatch(tmp_path, s, capsys, "original_upheld must appear in exactly one CURRENT")
|
|
2424
|
+
|
|
2425
|
+
|
|
2426
|
+
def test_criterion_ref_effective_rule_adjudication_mandated(tmp_path, capsys):
|
|
2427
|
+
s = scenario_g2d(accepted=False)
|
|
2428
|
+
s["traceability"]["reapplications"][0]["criterion_ref"] = "dissent:DIS-1"
|
|
2429
|
+
assert_mismatch(tmp_path, s, capsys, "effective-criterion rule")
|
|
2430
|
+
|
|
2431
|
+
|
|
2432
|
+
def test_verdict_changing_reapplication_requires_derived_adjustment(tmp_path, capsys):
|
|
2433
|
+
s = scenario_complex()
|
|
2434
|
+
s["traceability"]["adjustments"] = [s["traceability"]["adjustments"][0]] # drop ADJ-2
|
|
2435
|
+
row = s["traceability"]["rows"][1]
|
|
2436
|
+
row.pop("adjustment_id")
|
|
2437
|
+
assert_mismatch(tmp_path, s, capsys, "requires exactly one derived adjustment")
|
|
2438
|
+
|
|
2439
|
+
|
|
2440
|
+
def test_derived_adjustment_binds_to_recorded_pre_value(tmp_path, capsys):
|
|
2441
|
+
s = scenario_complex()
|
|
2442
|
+
s["traceability"]["reapplications"][0]["pre_reapplication_verdict"] = "PARTIALLY_ADDRESSED"
|
|
2443
|
+
assert_mismatch(tmp_path, s, capsys, "RECORDED pre-value")
|
|
2444
|
+
|
|
2445
|
+
|
|
2446
|
+
def test_derived_adjustment_copies_mechanically(tmp_path, capsys):
|
|
2447
|
+
s = scenario_complex()
|
|
2448
|
+
s["traceability"]["adjustments"][1]["rationale"] = "An embellished rationale."
|
|
2449
|
+
assert_mismatch(tmp_path, s, capsys, "MECHANICALLY COPIED")
|
|
2450
|
+
|
|
2451
|
+
|
|
2452
|
+
def test_cannot_verify_reapplication_appends_nothing(tmp_path, capsys):
|
|
2453
|
+
s = scenario_complex()
|
|
2454
|
+
rap = s["traceability"]["reapplications"][0]
|
|
2455
|
+
rap["reapplied_verdict"] = "CANNOT_VERIFY"
|
|
2456
|
+
rap["cannot_verify_reason"] = "evidence surface inaccessible"
|
|
2457
|
+
rap.pop("evidence_anchor")
|
|
2458
|
+
# ADJ-2 still claims to derive from RAP-1: the CANNOT_VERIFY append fires
|
|
2459
|
+
assert_mismatch(tmp_path, s, capsys, "CANNOT_VERIFY appends NOTHING mechanically")
|
|
2460
|
+
|
|
2461
|
+
|
|
2462
|
+
def test_intent_answered_derived_state_requires_system_resolution(tmp_path, capsys):
|
|
2463
|
+
s = scenario_complex()
|
|
2464
|
+
s["traceability"]["cross_model_resolutions"] = []
|
|
2465
|
+
assert_mismatch(tmp_path, s, capsys, "mandates a mechanically-created")
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
def test_system_resolution_state_follows_derivation(tmp_path, capsys):
|
|
2469
|
+
s = scenario_complex()
|
|
2470
|
+
s["traceability"]["cross_model_resolutions"][0]["state"] = "primary_upheld"
|
|
2471
|
+
assert_mismatch(tmp_path, s, capsys, "§9 derivation")
|
|
2472
|
+
|
|
2473
|
+
|
|
2474
|
+
def test_user_resolution_requires_cannot_verify_reapplication(tmp_path, capsys):
|
|
2475
|
+
s = scenario_complex()
|
|
2476
|
+
s["traceability"]["cross_model_resolutions"][0]["resolved_by"] = "user"
|
|
2477
|
+
s["traceability"]["cross_model_resolutions"][0]["state"] = "primary_upheld"
|
|
2478
|
+
assert_mismatch(tmp_path, s, capsys, "requires a CANNOT_VERIFY reapplication")
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
# --- exit-1 class: G2(d) acceptances -------------------------------------------
|
|
2482
|
+
|
|
2483
|
+
|
|
2484
|
+
def test_acceptance_reapplication_must_be_cannot_verify(tmp_path, capsys):
|
|
2485
|
+
s = scenario_g2d(accepted=True)
|
|
2486
|
+
rap = s["traceability"]["reapplications"][0]
|
|
2487
|
+
rap["reapplied_verdict"] = "FULLY_ADDRESSED"
|
|
2488
|
+
rap.pop("cannot_verify_reason")
|
|
2489
|
+
rap["evidence_anchor"] = [_anchor()]
|
|
2490
|
+
assert_mismatch(tmp_path, s, capsys, "must be CANNOT_VERIFY")
|
|
2491
|
+
|
|
2492
|
+
|
|
2493
|
+
def test_accepted_item_final_verdict_is_cannot_verify(tmp_path, capsys):
|
|
2494
|
+
s = scenario_g2d(accepted=True)
|
|
2495
|
+
row = s["traceability"]["rows"][0]
|
|
2496
|
+
row["final_verdict"] = "FULLY_ADDRESSED"
|
|
2497
|
+
row["status"] = "FULLY_ADDRESSED"
|
|
2498
|
+
row["verified"] = "YES"
|
|
2499
|
+
assert_mismatch(tmp_path, s, capsys, "final_verdict must be CANNOT_VERIFY")
|
|
2500
|
+
|
|
2501
|
+
|
|
2502
|
+
def test_fail_closed_adjustment_copies_reason(tmp_path, capsys):
|
|
2503
|
+
s = scenario_g2d(accepted=True)
|
|
2504
|
+
s["traceability"]["adjustments"][0]["cannot_verify_reason"] = "a different reason"
|
|
2505
|
+
assert_mismatch(tmp_path, s, capsys, "copied from the accepted reapplication")
|
|
2506
|
+
|
|
2507
|
+
|
|
2508
|
+
# --- exit-1 class: escalation approvals ----------------------------------------
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
def test_approval_must_reference_existing_exception(tmp_path, capsys):
|
|
2512
|
+
s = scenario_complex()
|
|
2513
|
+
s["traceability"]["escalation_approvals"].append(
|
|
2514
|
+
{"exception_id": "ESC-9", "approval_state": "approved", "approved_by": "user"}
|
|
2515
|
+
)
|
|
2516
|
+
assert_mismatch(tmp_path, s, capsys, "no such exception record")
|
|
2517
|
+
|
|
2518
|
+
|
|
2519
|
+
# --- exit-1 class: DecisionInputs equality -------------------------------------
|
|
2520
|
+
|
|
2521
|
+
|
|
2522
|
+
def test_per_item_operands_recompute(tmp_path, capsys):
|
|
2523
|
+
s = scenario_accept()
|
|
2524
|
+
s["traceability"]["decision_inputs"]["per_item"][0]["driving_severity"] = None
|
|
2525
|
+
assert_mismatch(tmp_path, s, capsys, "per_item does not equal")
|
|
2526
|
+
|
|
2527
|
+
|
|
2528
|
+
def test_verdict_counts_recompute(tmp_path, capsys):
|
|
2529
|
+
s = scenario_accept()
|
|
2530
|
+
s["traceability"]["decision_inputs"]["verdict_counts"]["must_fix"]["FULLY_ADDRESSED"] = 2
|
|
2531
|
+
assert_mismatch(tmp_path, s, capsys, "verdict_counts does not recompute")
|
|
2532
|
+
|
|
2533
|
+
|
|
2534
|
+
def test_residual_obligation_class_counts_recompute(tmp_path, capsys):
|
|
2535
|
+
s = scenario_accept()
|
|
2536
|
+
s["traceability"]["decision_inputs"]["residual_obligation_class_counts"]["must_fix"]["must_fix"] = 1
|
|
2537
|
+
assert_mismatch(tmp_path, s, capsys, "residual_obligation_class_counts does not recompute")
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
def test_p2_rate_recomputes_over_final_verdicts(tmp_path, capsys):
|
|
2541
|
+
s = scenario_complex()
|
|
2542
|
+
s["traceability"]["decision_inputs"]["should_fix_addressed_rate"]["numerator"] = 2
|
|
2543
|
+
assert_mismatch(tmp_path, s, capsys, "should_fix_addressed_rate does not recompute")
|
|
2544
|
+
|
|
2545
|
+
|
|
2546
|
+
def test_regression_list_recomputes(tmp_path, capsys):
|
|
2547
|
+
s = scenario_complex()
|
|
2548
|
+
s["traceability"]["decision_inputs"]["regressions"] = []
|
|
2549
|
+
assert_mismatch(tmp_path, s, capsys, "regression-attributed frozen new issues")
|
|
2550
|
+
|
|
2551
|
+
|
|
2552
|
+
def test_non_regression_ids_recompute(tmp_path, capsys):
|
|
2553
|
+
s = scenario_complex()
|
|
2554
|
+
s["traceability"]["decision_inputs"]["non_regression_new_issue_ids"] = ["NEW-2"]
|
|
2555
|
+
assert_mismatch(tmp_path, s, capsys, "previously_missed/indeterminate ids")
|
|
2556
|
+
|
|
2557
|
+
|
|
2558
|
+
def test_escalation_summary_joins_from_approvals(tmp_path, capsys):
|
|
2559
|
+
s = scenario_complex()
|
|
2560
|
+
s["traceability"]["decision_inputs"]["escalations"][0]["effective_approval_state"] = "approved"
|
|
2561
|
+
assert_mismatch(tmp_path, s, capsys, "joined exception/approval summary")
|
|
2562
|
+
|
|
2563
|
+
|
|
2564
|
+
def test_recorded_witness_must_match_recomputed(tmp_path, capsys):
|
|
2565
|
+
s = scenario_accept()
|
|
2566
|
+
s["traceability"]["decision_inputs"]["apply_chain_witness"] = "fail"
|
|
2567
|
+
assert_mismatch(tmp_path, s, capsys, "apply_chain_witness")
|
|
2568
|
+
|
|
2569
|
+
|
|
2570
|
+
# --- exit-1 class: gates + decision equality -----------------------------------
|
|
2571
|
+
|
|
2572
|
+
|
|
2573
|
+
def test_g2b_diverges_without_covering_resolution_defers(tmp_path, capsys):
|
|
2574
|
+
s = scenario_complex()
|
|
2575
|
+
# the mandated re-application concluded CANNOT_VERIFY: row stays NOT_ADDRESSED,
|
|
2576
|
+
# no adjustment, no resolution -> G2(b) pending; emitting Major anyway must fail
|
|
2577
|
+
rap = s["traceability"]["reapplications"][0]
|
|
2578
|
+
rap["reapplied_verdict"] = "CANNOT_VERIFY"
|
|
2579
|
+
rap["cannot_verify_reason"] = "evidence surface inaccessible"
|
|
2580
|
+
rap.pop("evidence_anchor")
|
|
2581
|
+
s["traceability"]["cross_model_resolutions"] = []
|
|
2582
|
+
s["traceability"]["adjustments"] = [s["traceability"]["adjustments"][0]]
|
|
2583
|
+
row = s["traceability"]["rows"][1]
|
|
2584
|
+
row["final_verdict"] = "NOT_ADDRESSED"
|
|
2585
|
+
row["status"] = "NOT_ADDRESSED"
|
|
2586
|
+
row["verified"] = "NO"
|
|
2587
|
+
row.pop("adjustment_id")
|
|
2588
|
+
row["cross_model_status"] = "diverges"
|
|
2589
|
+
di = s["traceability"]["decision_inputs"]
|
|
2590
|
+
di["per_item"][1]["final_verdict"] = "NOT_ADDRESSED"
|
|
2591
|
+
di["verdict_counts"]["must_fix"] = {v: 0 for v in crs.VERDICTS}
|
|
2592
|
+
di["verdict_counts"]["must_fix"].update({"FULLY_ADDRESSED": 1, "NOT_ADDRESSED": 1})
|
|
2593
|
+
di.pop("reject_recommended")
|
|
2594
|
+
s["traceability"]["decision_state"] = "Major Revision"
|
|
2595
|
+
assert_mismatch(tmp_path, s, capsys, "G2(b)")
|
|
2596
|
+
|
|
2597
|
+
|
|
2598
|
+
def test_g2c_pending_exception_defers(tmp_path, capsys):
|
|
2599
|
+
s = scenario_complex()
|
|
2600
|
+
s["traceability"]["escalation_approvals"] = []
|
|
2601
|
+
s["traceability"]["decision_inputs"]["escalations"][0]["effective_approval_state"] = "pending"
|
|
2602
|
+
assert_mismatch(tmp_path, s, capsys, "G2(c)")
|
|
2603
|
+
|
|
2604
|
+
|
|
2605
|
+
def test_g2d_fail_closed_without_acceptance_defers(tmp_path, capsys):
|
|
2606
|
+
s = scenario_g2d(accepted=False)
|
|
2607
|
+
s["traceability"]["decision_state"] = "Major Revision"
|
|
2608
|
+
assert_mismatch(tmp_path, s, capsys, "G2(d)")
|
|
2609
|
+
|
|
2610
|
+
|
|
2611
|
+
def test_deferred_state_without_pending_is_biconditional_violation(tmp_path, capsys):
|
|
2612
|
+
s = scenario_accept()
|
|
2613
|
+
s["traceability"]["decision_state"] = "user_review_required"
|
|
2614
|
+
s["traceability"]["decision_inputs"].pop("reject_recommended")
|
|
2615
|
+
assert_mismatch(tmp_path, s, capsys, "the G2 biconditional runs both directions")
|
|
2616
|
+
|
|
2617
|
+
|
|
2618
|
+
def test_reject_recommended_absent_on_gated_emission(tmp_path, capsys):
|
|
2619
|
+
s = scenario_complex()
|
|
2620
|
+
s["traceability"]["escalation_approvals"] = []
|
|
2621
|
+
s["traceability"]["decision_inputs"]["escalations"][0]["effective_approval_state"] = "pending"
|
|
2622
|
+
s["traceability"]["decision_state"] = "user_review_required"
|
|
2623
|
+
assert_mismatch(tmp_path, s, capsys, "must be ABSENT on a gated emission")
|
|
2624
|
+
|
|
2625
|
+
|
|
2626
|
+
def test_reject_recommended_present_on_non_gated_emission(tmp_path, capsys):
|
|
2627
|
+
s = scenario_accept()
|
|
2628
|
+
s["traceability"]["decision_inputs"].pop("reject_recommended")
|
|
2629
|
+
assert_mismatch(tmp_path, s, capsys, "must be PRESENT on a non-gated emission")
|
|
2630
|
+
|
|
2631
|
+
|
|
2632
|
+
def test_decision_state_equals_recomputed_derivation(tmp_path, capsys):
|
|
2633
|
+
s = scenario_complex()
|
|
2634
|
+
s["traceability"]["decision_state"] = "Accept"
|
|
2635
|
+
assert_mismatch(tmp_path, s, capsys, "Steps 2-3 recomputed from the raw records")
|
|
2636
|
+
|
|
2637
|
+
|
|
2638
|
+
def test_reject_recommended_equals_recomputed(tmp_path, capsys):
|
|
2639
|
+
s = scenario_accept()
|
|
2640
|
+
s["traceability"]["decision_inputs"]["reject_recommended"] = True
|
|
2641
|
+
assert_mismatch(tmp_path, s, capsys, "reject_recommended True != recomputed False")
|
|
2642
|
+
|
|
2643
|
+
|
|
2644
|
+
def test_goalpost_previously_missed_cannot_enter_decision(tmp_path, capsys):
|
|
2645
|
+
# moving NEW-2 (previously_missed, major) into the regression operands
|
|
2646
|
+
# would escalate B3; the checker recomputes both lists from the frozen
|
|
2647
|
+
# attribution and rejects the promoted copy
|
|
2648
|
+
s = scenario_complex()
|
|
2649
|
+
di = s["traceability"]["decision_inputs"]
|
|
2650
|
+
di["regressions"].append({"new_issue_id": "NEW-2", "severity": "major"})
|
|
2651
|
+
di["non_regression_new_issue_ids"] = ["NEW-3"]
|
|
2652
|
+
assert_mismatch(tmp_path, s, capsys, "regression-attributed frozen new issues")
|
|
2653
|
+
|
|
2654
|
+
|
|
2655
|
+
# --- round-1 review closures (three-track exact-head findings) -----------------
|
|
2656
|
+
|
|
2657
|
+
|
|
2658
|
+
def test_golden_g2d_retry_passes(tmp_path, capsys):
|
|
2659
|
+
# general P1-2 / codex #8 regression pin: a spec-legal successful retry
|
|
2660
|
+
# (superseded CANNOT_VERIFY attempt preserved) must PASS
|
|
2661
|
+
code, out, _err = run_checker(tmp_path, scenario_g2d_retry(), capsys)
|
|
2662
|
+
assert code == crs.EXIT_PASS, out
|
|
2663
|
+
assert "'Minor Revision'" in out
|
|
2664
|
+
|
|
2665
|
+
|
|
2666
|
+
def test_two_current_reapplications_for_one_answer(tmp_path, capsys):
|
|
2667
|
+
s = scenario_g2d_retry()
|
|
2668
|
+
del s["traceability"]["reapplications"][1]["supersedes_reapplication_id"]
|
|
2669
|
+
assert_mismatch(tmp_path, s, capsys, "exactly one CURRENT reapplication's answer_refs")
|
|
2670
|
+
|
|
2671
|
+
|
|
2672
|
+
def test_valid_rebuttal_upgrades_to_fully_only(tmp_path, capsys):
|
|
2673
|
+
# general P1-1 / codex #2: a letter-anchored sideways move must fail
|
|
2674
|
+
s = scenario_complex()
|
|
2675
|
+
adj = s["traceability"]["adjustments"][0]
|
|
2676
|
+
adj["to_verdict"] = "PARTIALLY_ADDRESSED"
|
|
2677
|
+
adj["residual_gap"] = {"text": "residual", "residual_obligation_class": "should_fix"}
|
|
2678
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "upgrade to FULLY_ADDRESSED")
|
|
2679
|
+
|
|
2680
|
+
|
|
2681
|
+
def test_valid_rebuttal_from_fully_is_not_an_upgrade(tmp_path, capsys):
|
|
2682
|
+
s = scenario_complex()
|
|
2683
|
+
s["traceability"]["adjustments"][0]["from_verdict"] = "FULLY_ADDRESSED"
|
|
2684
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "upgrade to FULLY_ADDRESSED")
|
|
2685
|
+
|
|
2686
|
+
|
|
2687
|
+
def test_author_pointer_must_upgrade_to_partially_or_fully(tmp_path, capsys):
|
|
2688
|
+
s = scenario_accept()
|
|
2689
|
+
s["traceability"]["adjustments"].append({
|
|
2690
|
+
"adjustment_id": "ADJ-9",
|
|
2691
|
+
"item_id": "REV-002",
|
|
2692
|
+
"from_verdict": "PARTIALLY_ADDRESSED",
|
|
2693
|
+
"to_verdict": "NOT_ADDRESSED",
|
|
2694
|
+
"basis": "author_pointer_located_evidence",
|
|
2695
|
+
"evidence_anchor": [_anchor()],
|
|
2696
|
+
"rationale": "not an upgrade",
|
|
2697
|
+
})
|
|
2698
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "upgrades to PARTIALLY/FULLY_ADDRESSED")
|
|
2699
|
+
|
|
2700
|
+
|
|
2701
|
+
def test_author_pointer_downgrade_rejected(tmp_path, capsys):
|
|
2702
|
+
s = scenario_accept()
|
|
2703
|
+
s["traceability"]["adjustments"].append({
|
|
2704
|
+
"adjustment_id": "ADJ-9",
|
|
2705
|
+
"item_id": "REV-002",
|
|
2706
|
+
"from_verdict": "FULLY_ADDRESSED",
|
|
2707
|
+
"to_verdict": "PARTIALLY_ADDRESSED",
|
|
2708
|
+
"basis": "author_pointer_located_evidence",
|
|
2709
|
+
"evidence_anchor": [_anchor()],
|
|
2710
|
+
"residual_gap": {"text": "residual", "residual_obligation_class": "consider"},
|
|
2711
|
+
"rationale": "a downgrade",
|
|
2712
|
+
})
|
|
2713
|
+
assert_exit2(tmp_path, s, capsys, "phase2b_lint_failed", "upgrades to PARTIALLY/FULLY_ADDRESSED")
|
|
2714
|
+
|
|
2715
|
+
|
|
2716
|
+
def test_malformed_report_version_is_manifest_incomplete(tmp_path, capsys):
|
|
2717
|
+
# A malformed value cannot cross the exact current report-1.3 boundary.
|
|
2718
|
+
s = scenario_accept()
|
|
2719
|
+
s["reports"][0]["report_format_version"] = "not-a-version"
|
|
2720
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "not a numeric dotted version")
|
|
2721
|
+
|
|
2722
|
+
|
|
2723
|
+
def test_report_draft_hashes_must_be_12_hex(tmp_path, capsys):
|
|
2724
|
+
s = scenario_accept()
|
|
2725
|
+
s["reports"][0]["base_draft_hash"] = "XYZ"
|
|
2726
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "12-hex")
|
|
2727
|
+
|
|
2728
|
+
|
|
2729
|
+
def test_orphan_g2d_acceptance_cannot_clear_deferral(tmp_path, capsys):
|
|
2730
|
+
# codex #3: acceptance with no backing adjustment must fail even when
|
|
2731
|
+
# phase2a_verdict == final_verdict == CANNOT_VERIFY
|
|
2732
|
+
s = scenario_g2d(accepted=True)
|
|
2733
|
+
vrec = s["verdict_record"]["items"][0]
|
|
2734
|
+
vrec["verdict"] = "CANNOT_VERIFY"
|
|
2735
|
+
vrec["cannot_verify_reason"] = "evidence surface inaccessible"
|
|
2736
|
+
vrec.pop("evidence_anchor")
|
|
2737
|
+
row = s["traceability"]["rows"][0]
|
|
2738
|
+
row["phase2a_verdict"] = "CANNOT_VERIFY"
|
|
2739
|
+
row.pop("adjustment_id")
|
|
2740
|
+
s["traceability"]["adjustments"] = []
|
|
2741
|
+
s["traceability"]["reapplications"][0]["pre_reapplication_verdict"] = "CANNOT_VERIFY"
|
|
2742
|
+
assert_mismatch(tmp_path, s, capsys, "backs exactly ONE user_accepted_fail_closed")
|
|
2743
|
+
|
|
2744
|
+
|
|
2745
|
+
def test_resolution_reapplication_must_answer_its_intent(tmp_path, capsys):
|
|
2746
|
+
# codex #4: cross-wired CrossModelResolution must fail
|
|
2747
|
+
s = scenario_complex()
|
|
2748
|
+
s["traceability"]["resolution_intents"].append(
|
|
2749
|
+
{"intent_id": "INT-2", "item_id": "REV-002", "answered_by": "user", "guidance_note": "recheck"}
|
|
2750
|
+
)
|
|
2751
|
+
s["traceability"]["reapplications"].append({
|
|
2752
|
+
"reapplication_id": "RAP-2",
|
|
2753
|
+
"item_id": "REV-002",
|
|
2754
|
+
"answer_refs": ["intent:INT-2"],
|
|
2755
|
+
"pre_reapplication_verdict": "FULLY_ADDRESSED",
|
|
2756
|
+
"reapplied_verdict": "FULLY_ADDRESSED",
|
|
2757
|
+
"evidence_anchor": [_anchor('table: Table 4 [robustness]')],
|
|
2758
|
+
"rationale": "re-examination upholds the located evidence",
|
|
2759
|
+
"criterion_ref": "phase1:REV-002",
|
|
2760
|
+
})
|
|
2761
|
+
s["traceability"]["cross_model_resolutions"].append({
|
|
2762
|
+
"resolution_id": "RES-2",
|
|
2763
|
+
"item_id": "REV-002",
|
|
2764
|
+
"intent_id": "INT-1", # cross-wired: RAP-2 answered INT-2, not INT-1
|
|
2765
|
+
"reapplication_id": "RAP-2",
|
|
2766
|
+
"state": "primary_upheld",
|
|
2767
|
+
"resolved_by": "system",
|
|
2768
|
+
"rationale": "cross-wired resolution",
|
|
2769
|
+
})
|
|
2770
|
+
assert_mismatch(tmp_path, s, capsys, "did not answer")
|
|
2771
|
+
|
|
2772
|
+
|
|
2773
|
+
def test_challenged_drafted_body_is_never_booked(tmp_path, capsys):
|
|
2774
|
+
# codex #6: a booked adjustment content-equal to a CHALLENGED proposal
|
|
2775
|
+
s = scenario_complex()
|
|
2776
|
+
s["traceability"]["rebuttal_adjudications"].append({
|
|
2777
|
+
"rebuttal_adjudication_id": "RADJ-2",
|
|
2778
|
+
"item_id": "REV-001",
|
|
2779
|
+
"verdict": "challenged",
|
|
2780
|
+
"rationale": "The counter-evidence does not rebut the finding.",
|
|
2781
|
+
})
|
|
2782
|
+
s["traceability"]["pending_rebuttal_upgrades"].append({
|
|
2783
|
+
"proposal_id": "PRB-2",
|
|
2784
|
+
"item_id": "REV-001",
|
|
2785
|
+
"drafted_adjustment": copy.deepcopy(s["traceability"]["pending_rebuttal_upgrades"][0]["drafted_adjustment"]),
|
|
2786
|
+
"disposition": "challenged:RADJ-2",
|
|
2787
|
+
})
|
|
2788
|
+
assert_mismatch(tmp_path, s, capsys, "NEVER booked")
|
|
2789
|
+
|
|
2790
|
+
|
|
2791
|
+
def test_duplicate_drafted_body_rejected(tmp_path, capsys):
|
|
2792
|
+
s = scenario_complex()
|
|
2793
|
+
s["traceability"]["pending_rebuttal_upgrades"].append({
|
|
2794
|
+
"proposal_id": "PRB-3",
|
|
2795
|
+
"item_id": "REV-001",
|
|
2796
|
+
"drafted_adjustment": copy.deepcopy(s["traceability"]["pending_rebuttal_upgrades"][0]["drafted_adjustment"]),
|
|
2797
|
+
"disposition": "booked:ADJ-1",
|
|
2798
|
+
})
|
|
2799
|
+
assert_mismatch(tmp_path, s, capsys, "duplicate PendingRebuttalUpgrade drafted body")
|
|
2800
|
+
|
|
2801
|
+
|
|
2802
|
+
def test_escalation_with_absent_original_is_rejected_at_manifest_gate(tmp_path, capsys):
|
|
2803
|
+
s = scenario_accept()
|
|
2804
|
+
s["manifest_overrides"]["original_manuscript"] = {"present": False}
|
|
2805
|
+
s["verdict_record"]["escalation_exceptions"] = [
|
|
2806
|
+
{
|
|
2807
|
+
"exception_id": "ESC-1",
|
|
2808
|
+
"escalation_class": "research_integrity",
|
|
2809
|
+
"reason_code": "fabricated-consent",
|
|
2810
|
+
"evidence_anchor": 'text: §2 "consent"',
|
|
2811
|
+
"why_round1_missed_it": "Round 1 focused elsewhere.",
|
|
2812
|
+
"mechanical_decision_impact": "Major Revision",
|
|
2813
|
+
"approval_state": "pending",
|
|
2814
|
+
}
|
|
2815
|
+
]
|
|
2816
|
+
di = s["traceability"]["decision_inputs"]
|
|
2817
|
+
di["escalations"] = [
|
|
2818
|
+
{
|
|
2819
|
+
"exception_id": "ESC-1",
|
|
2820
|
+
"effective_approval_state": "pending",
|
|
2821
|
+
"escalation_class": "research_integrity",
|
|
2822
|
+
"mechanical_decision_impact": "Major Revision",
|
|
2823
|
+
}
|
|
2824
|
+
]
|
|
2825
|
+
di["apply_chain_witness"] = "pass"
|
|
2826
|
+
di.pop("reject_recommended")
|
|
2827
|
+
s["traceability"]["decision_state"] = "user_review_required"
|
|
2828
|
+
assert_exit2(
|
|
2829
|
+
tmp_path,
|
|
2830
|
+
s,
|
|
2831
|
+
capsys,
|
|
2832
|
+
"manifest_incomplete",
|
|
2833
|
+
"hard-required artifact original_manuscript",
|
|
2834
|
+
)
|
|
2835
|
+
|
|
2836
|
+
|
|
2837
|
+
def test_source_reviewer_is_verbatim_roadmap_copy(tmp_path, capsys):
|
|
2838
|
+
# codex #9
|
|
2839
|
+
s = scenario_accept()
|
|
2840
|
+
s["precommitment"]["items"][0]["source_reviewer"] = "R2"
|
|
2841
|
+
s["precommitment"]["items"][0]["source_reviewer_labels"] = ["R2"]
|
|
2842
|
+
assert_mismatch(tmp_path, s, capsys, "VERBATIM copy of the Schema 7 reviewer field")
|
|
2843
|
+
|
|
2844
|
+
|
|
2845
|
+
def test_half_transported_item_cannot_carry_null_severity():
|
|
2846
|
+
# general P2-1 / codex #5
|
|
2847
|
+
s = scenario_accept()
|
|
2848
|
+
item = s["roadmap"]["items"][0]
|
|
2849
|
+
del item["severity"]
|
|
2850
|
+
item["confidence"] = 4
|
|
2851
|
+
with pytest.raises(crs.ManifestError, match="requires transported severity"):
|
|
2852
|
+
crs.load_roadmap({"items": [item]})
|
|
2853
|
+
|
|
2854
|
+
|
|
2855
|
+
def test_aborted_emission_with_lint_reason_is_exempt(tmp_path, capsys):
|
|
2856
|
+
# general P2-2: abort precedence over deferral — a lint-failure abort is
|
|
2857
|
+
# accepted as recorded (the checker cannot re-derive a fenced call's lint)
|
|
2858
|
+
s = scenario_accept()
|
|
2859
|
+
s["traceability"]["decision_state"] = "aborted"
|
|
2860
|
+
s["traceability"]["abort_reason"] = "phase2b_lint_failed"
|
|
2861
|
+
s["traceability"]["decision_inputs"].pop("reject_recommended")
|
|
2862
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
2863
|
+
assert code == crs.EXIT_PASS, out
|
|
2864
|
+
assert "'aborted'" in out
|
|
2865
|
+
|
|
2866
|
+
|
|
2867
|
+
def test_aborted_criteria_drift_claim_must_recompute(tmp_path, capsys):
|
|
2868
|
+
s = scenario_accept()
|
|
2869
|
+
s["traceability"]["decision_state"] = "aborted"
|
|
2870
|
+
s["traceability"]["abort_reason"] = "criteria_drift"
|
|
2871
|
+
s["traceability"]["decision_inputs"].pop("reject_recommended")
|
|
2872
|
+
assert_mismatch(tmp_path, s, capsys, "no silent verdict change recomputes")
|
|
2873
|
+
|
|
2874
|
+
|
|
2875
|
+
def test_path_ref_must_be_relative(tmp_path, capsys):
|
|
2876
|
+
# codex #10
|
|
2877
|
+
s = scenario_accept()
|
|
2878
|
+
s["manifest_overrides"]["round1_findings"] = _entry("path:../secrets.md", SYNTH_SHA)
|
|
2879
|
+
assert_exit2(tmp_path, s, capsys, "manifest_incomplete", "RELATIVE")
|
|
2880
|
+
|
|
2881
|
+
|
|
2882
|
+
def test_letter_without_required_item_details_notes_empty_layer(tmp_path, capsys):
|
|
2883
|
+
s = scenario_accept()
|
|
2884
|
+
s["letter"] = "# Editorial Decision\n\n## Closing\n"
|
|
2885
|
+
crit = s["precommitment"]["items"][0]["inherited_criterion"]
|
|
2886
|
+
del crit["letter_text"]
|
|
2887
|
+
del crit["letter_item_ref"]
|
|
2888
|
+
code, out, err = run_checker(tmp_path, s, capsys)
|
|
2889
|
+
assert code == crs.EXIT_PASS, out
|
|
2890
|
+
assert "no Required Item Details blocks parsed" in err
|
|
2891
|
+
|
|
2892
|
+
|
|
2893
|
+
def test_superseded_attempt_must_be_failed(tmp_path, capsys):
|
|
2894
|
+
# codex round-2 #1: supersession is defined for FAILED attempts only (§6)
|
|
2895
|
+
s = scenario_g2d_retry()
|
|
2896
|
+
rap1 = s["traceability"]["reapplications"][0]
|
|
2897
|
+
rap1["reapplied_verdict"] = "FULLY_ADDRESSED"
|
|
2898
|
+
rap1.pop("cannot_verify_reason")
|
|
2899
|
+
rap1["evidence_anchor"] = [_anchor('text: §6 "procedure"')]
|
|
2900
|
+
assert_mismatch(tmp_path, s, capsys, "only FAILED (CANNOT_VERIFY) attempts are superseded")
|
|
2901
|
+
|
|
2902
|
+
|
|
2903
|
+
def test_superseded_pre_value_binds_to_direct_retry(tmp_path, capsys):
|
|
2904
|
+
# codex round-2 #1: a failed attempt appends nothing, so its dispatch
|
|
2905
|
+
# tail equals its direct retry's recorded pre-value
|
|
2906
|
+
s = scenario_g2d_retry()
|
|
2907
|
+
s["traceability"]["reapplications"][0]["pre_reapplication_verdict"] = "NOT_ADDRESSED"
|
|
2908
|
+
assert_mismatch(tmp_path, s, capsys, "dispatch tail equals its direct retry's")
|
|
2909
|
+
|
|
2910
|
+
|
|
2911
|
+
def test_aborted_emission_cannot_fake_manifest_root_cause(tmp_path, capsys):
|
|
2912
|
+
# codex round-2 #2: the checker reached recomputation, so the §11
|
|
2913
|
+
# manifest layer validated — a claimed G0/checker abort is a false root cause
|
|
2914
|
+
s = scenario_accept()
|
|
2915
|
+
s["traceability"]["decision_state"] = "aborted"
|
|
2916
|
+
s["traceability"]["abort_reason"] = "manifest_incomplete"
|
|
2917
|
+
s["traceability"]["decision_inputs"].pop("reject_recommended")
|
|
2918
|
+
assert_mismatch(tmp_path, s, capsys, "cannot be the true root cause")
|
|
2919
|
+
|
|
2920
|
+
|
|
2921
|
+
def test_reapplication_letter_tag_requires_booked_rebuttal_in_chain(tmp_path, capsys):
|
|
2922
|
+
# general round-2 P2-1: a NOT_ADDRESSED -> FULLY upgrade cannot ride
|
|
2923
|
+
# letter-side anchors when the re-examined chain never booked a
|
|
2924
|
+
# valid_rebuttal record (§5.3 letter-tag condition, §3.4 closing rule)
|
|
2925
|
+
s = scenario_complex()
|
|
2926
|
+
letter_anchors = [_anchor('text: letter §3 "assertion"', "letter")]
|
|
2927
|
+
s["traceability"]["reapplications"][0]["evidence_anchor"] = copy.deepcopy(letter_anchors)
|
|
2928
|
+
s["traceability"]["adjustments"][1]["evidence_anchor"] = copy.deepcopy(letter_anchors)
|
|
2929
|
+
assert_mismatch(tmp_path, s, capsys, "letter-tagged anchors are valid exactly when")
|
|
2930
|
+
|
|
2931
|
+
|
|
2932
|
+
def test_superseded_record_with_derived_adjustment_still_checked(tmp_path, capsys):
|
|
2933
|
+
# codex round-3: a verdict-changing successful reapplication carrying its
|
|
2934
|
+
# derived adjustment must NOT bypass the failed-only supersession guard
|
|
2935
|
+
s = scenario_g2d_retry()
|
|
2936
|
+
t = s["traceability"]
|
|
2937
|
+
rap1 = t["reapplications"][0]
|
|
2938
|
+
rap1["reapplied_verdict"] = "NOT_ADDRESSED"
|
|
2939
|
+
rap1.pop("cannot_verify_reason")
|
|
2940
|
+
rap1_anchors = [_anchor('text: §6 "procedure"')]
|
|
2941
|
+
rap1["evidence_anchor"] = copy.deepcopy(rap1_anchors)
|
|
2942
|
+
adj0 = {
|
|
2943
|
+
"adjustment_id": "ADJ-2",
|
|
2944
|
+
"item_id": "REV-001",
|
|
2945
|
+
"from_verdict": "FULLY_ADDRESSED",
|
|
2946
|
+
"to_verdict": "NOT_ADDRESSED",
|
|
2947
|
+
"basis": "cross_model_adjudication",
|
|
2948
|
+
"evidence_anchor": copy.deepcopy(rap1_anchors),
|
|
2949
|
+
"rationale": rap1["rationale"],
|
|
2950
|
+
"source_ref": "reapplication:RAP-1",
|
|
2951
|
+
}
|
|
2952
|
+
adj1 = t["adjustments"][0]
|
|
2953
|
+
adj1["from_verdict"] = "NOT_ADDRESSED"
|
|
2954
|
+
adj1["supersedes_adjustment_id"] = "ADJ-2"
|
|
2955
|
+
t["adjustments"].insert(0, adj0)
|
|
2956
|
+
t["reapplications"][1]["pre_reapplication_verdict"] = "NOT_ADDRESSED"
|
|
2957
|
+
assert_mismatch(tmp_path, s, capsys, "only FAILED (CANNOT_VERIFY) attempts are superseded")
|
|
2958
|
+
|
|
2959
|
+
|
|
2960
|
+
def test_p2_dissent_must_take_the_user_path(tmp_path, capsys):
|
|
2961
|
+
# general round-3 P2: §7 — the judge's adjudication scope equals the §9
|
|
2962
|
+
# pass's P1 coverage; a judge-cleared P2 dissent cannot clear G2(a)
|
|
2963
|
+
s = scenario_complex()
|
|
2964
|
+
pre_rev002 = next(rec for rec in s["precommitment"]["items"] if rec["item_id"] == "REV-002")
|
|
2965
|
+
s["verdict_record"]["dissents"].append({
|
|
2966
|
+
"dissent_id": "DIS-2",
|
|
2967
|
+
"item_id": "REV-002",
|
|
2968
|
+
"criterion_hash": "0" * 64, # recomputed at emit
|
|
2969
|
+
"reason_code": "criterion_ambiguous",
|
|
2970
|
+
"original_operationalization": pre_rev002["operationalization"]["fully_addressed"],
|
|
2971
|
+
"replacement_operationalization": "Results carries a robustness table with at least one alternative specification.",
|
|
2972
|
+
"evidence": 'text: §4 "specifications"',
|
|
2973
|
+
"decision_impact_note": "Narrows the robustness surface.",
|
|
2974
|
+
})
|
|
2975
|
+
rev002 = next(rec for rec in s["verdict_record"]["items"] if rec["item_id"] == "REV-002")
|
|
2976
|
+
rev002["applied_criterion"] = "dissented:DIS-2"
|
|
2977
|
+
s["traceability"]["dissent_adjudications"] = [
|
|
2978
|
+
{"dissent_id": "DIS-1", "adjudicator": "cross_model", "outcome": "replacement_approved",
|
|
2979
|
+
"rationale": "Replacement approved by the judge."},
|
|
2980
|
+
{"dissent_id": "DIS-2", "adjudicator": "cross_model", "outcome": "replacement_approved",
|
|
2981
|
+
"rationale": "Replacement approved by the judge."},
|
|
2982
|
+
]
|
|
2983
|
+
assert_mismatch(tmp_path, s, capsys, "always take the G2(a) user path")
|
|
2984
|
+
|
|
2985
|
+
|
|
2986
|
+
def test_user_adjudication_on_active_p1_dissent_stays_legal(tmp_path, capsys):
|
|
2987
|
+
# adjudicated AGAINST the one-way §7 reading (codex round-4 #1a): the §6
|
|
2988
|
+
# deferral loop records a user-adjudicated DissentAdjudication DIRECTLY,
|
|
2989
|
+
# and the §9 pass can be per-row unavailable — the user path must remain
|
|
2990
|
+
# a legal fallback on an active-setup P1 dissent
|
|
2991
|
+
s = scenario_g2d(accepted=False)
|
|
2992
|
+
s["traceability"]["dissent_adjudications"][0]["adjudicator"] = "user"
|
|
2993
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
2994
|
+
assert code == crs.EXIT_PASS, out
|
|
2995
|
+
|
|
2996
|
+
|
|
2997
|
+
def test_system_intent_requires_evaluated_p1_row(tmp_path, capsys):
|
|
2998
|
+
# general round-4 P1: a system intent forged onto a not_configured row
|
|
2999
|
+
s = scenario_accept()
|
|
3000
|
+
s["traceability"]["resolution_intents"].append(
|
|
3001
|
+
{"intent_id": "INT-1", "item_id": "REV-001", "answered_by": "system"}
|
|
3002
|
+
)
|
|
3003
|
+
assert_mismatch(tmp_path, s, capsys, "system intents exist only for evaluated must_fix diverges rows")
|
|
3004
|
+
|
|
3005
|
+
|
|
3006
|
+
def test_divergence_reapplication_requires_evaluated_p1_row(tmp_path, capsys):
|
|
3007
|
+
# general round-4 P1: the intent→reapplication→resolution chain cannot
|
|
3008
|
+
# exist for a row the judge never evaluated (not_configured / P2)
|
|
3009
|
+
s = scenario_accept()
|
|
3010
|
+
s["traceability"]["resolution_intents"].append(
|
|
3011
|
+
{"intent_id": "INT-1", "item_id": "REV-001", "answered_by": "user", "guidance_note": "forged"}
|
|
3012
|
+
)
|
|
3013
|
+
s["traceability"]["reapplications"].append({
|
|
3014
|
+
"reapplication_id": "RAP-1",
|
|
3015
|
+
"item_id": "REV-001",
|
|
3016
|
+
"answer_refs": ["intent:INT-1"],
|
|
3017
|
+
"pre_reapplication_verdict": "FULLY_ADDRESSED",
|
|
3018
|
+
"reapplied_verdict": "FULLY_ADDRESSED",
|
|
3019
|
+
"evidence_anchor": [_anchor()],
|
|
3020
|
+
"rationale": "forged divergence re-verification",
|
|
3021
|
+
"criterion_ref": "phase1:REV-001",
|
|
3022
|
+
})
|
|
3023
|
+
s["traceability"]["cross_model_resolutions"].append({
|
|
3024
|
+
"resolution_id": "RES-1",
|
|
3025
|
+
"item_id": "REV-001",
|
|
3026
|
+
"intent_id": "INT-1",
|
|
3027
|
+
"reapplication_id": "RAP-1",
|
|
3028
|
+
"state": "primary_upheld",
|
|
3029
|
+
"resolved_by": "system",
|
|
3030
|
+
"rationale": "forged resolution",
|
|
3031
|
+
})
|
|
3032
|
+
assert_mismatch(tmp_path, s, capsys, "divergence-only re-application exists only for an evaluated must_fix row")
|
|
3033
|
+
|
|
3034
|
+
|
|
3035
|
+
def test_forged_divergence_on_agree_row_rejected(tmp_path, capsys):
|
|
3036
|
+
# codex round-5 P1: an originally-agree row (judge verdict == committed
|
|
3037
|
+
# verdict) cannot manufacture its own divergence by forging a
|
|
3038
|
+
# downgrading intent→reapplication→adjustment→resolution chain — §6
|
|
3039
|
+
# identifies diverges BEFORE the system intent is emitted
|
|
3040
|
+
s = scenario_complex()
|
|
3041
|
+
vrec = next(rec for rec in s["verdict_record"]["items"] if rec["item_id"] == "REV-002")
|
|
3042
|
+
vrec["verdict"] = "FULLY_ADDRESSED"
|
|
3043
|
+
vrec["evidence_anchor"] = ['table: Table 4 [robustness]']
|
|
3044
|
+
row = s["traceability"]["rows"][1]
|
|
3045
|
+
row["phase2a_verdict"] = "FULLY_ADDRESSED"
|
|
3046
|
+
row["final_verdict"] = "NOT_ADDRESSED"
|
|
3047
|
+
row["status"] = "NOT_ADDRESSED"
|
|
3048
|
+
row["verified"] = "NO"
|
|
3049
|
+
row["cross_model_status"] = "diverges" # manufactured after the change
|
|
3050
|
+
adj2 = s["traceability"]["adjustments"][1]
|
|
3051
|
+
adj2["from_verdict"] = "FULLY_ADDRESSED"
|
|
3052
|
+
adj2["to_verdict"] = "NOT_ADDRESSED"
|
|
3053
|
+
rap = s["traceability"]["reapplications"][0]
|
|
3054
|
+
rap["pre_reapplication_verdict"] = "FULLY_ADDRESSED"
|
|
3055
|
+
rap["reapplied_verdict"] = "NOT_ADDRESSED"
|
|
3056
|
+
di = s["traceability"]["decision_inputs"]
|
|
3057
|
+
di["per_item"][1]["final_verdict"] = "NOT_ADDRESSED"
|
|
3058
|
+
di["verdict_counts"]["must_fix"] = {v: 0 for v in crs.VERDICTS}
|
|
3059
|
+
di["verdict_counts"]["must_fix"].update({"FULLY_ADDRESSED": 1, "NOT_ADDRESSED": 1})
|
|
3060
|
+
di["reject_recommended"] = True
|
|
3061
|
+
s["traceability"]["decision_state"] = "Major Revision"
|
|
3062
|
+
assert_mismatch(tmp_path, s, capsys, "no divergence existed at dispatch")
|
|
3063
|
+
|
|
3064
|
+
|
|
3065
|
+
def test_divergence_chain_carries_original_system_intent(tmp_path, capsys):
|
|
3066
|
+
# codex round-5 P1 (companion rule): a user-only intent chain on an
|
|
3067
|
+
# evaluated diverging row lacks the §6 mandating system intent
|
|
3068
|
+
s = scenario_complex()
|
|
3069
|
+
s["traceability"]["resolution_intents"] = [
|
|
3070
|
+
{"intent_id": "INT-2", "item_id": "REV-002", "answered_by": "user", "guidance_note": "recheck"}
|
|
3071
|
+
]
|
|
3072
|
+
s["traceability"]["reapplications"][0]["answer_refs"] = ["intent:INT-2"]
|
|
3073
|
+
s["traceability"]["cross_model_resolutions"][0]["intent_id"] = "INT-2"
|
|
3074
|
+
assert_mismatch(tmp_path, s, capsys, "ORIGINAL mandating system intent")
|
|
3075
|
+
|
|
3076
|
+
|
|
3077
|
+
def test_ghost_dissent_must_be_applied(tmp_path, capsys):
|
|
3078
|
+
# general round-5 P1: a DissentRecord never referenced by its item's
|
|
3079
|
+
# verdict record (applied_criterion stays precommitted) is a ghost — it
|
|
3080
|
+
# cannot trip the §7 bound or authorize a re-application second chance
|
|
3081
|
+
s = scenario_accept()
|
|
3082
|
+
s["verdict_record"]["dissents"].append({
|
|
3083
|
+
"dissent_id": "DIS-1",
|
|
3084
|
+
"item_id": "REV-001",
|
|
3085
|
+
"criterion_hash": "0" * 64, # recomputed at emit
|
|
3086
|
+
"reason_code": "criterion_ambiguous",
|
|
3087
|
+
"original_operationalization": "Methods carries a power analysis naming effect size, alpha, and power.",
|
|
3088
|
+
"replacement_operationalization": "Methods carries a power analysis naming effect size.",
|
|
3089
|
+
"evidence": 'text: §3.2 "power analysis"',
|
|
3090
|
+
"decision_impact_note": "Narrows the committed pattern.",
|
|
3091
|
+
})
|
|
3092
|
+
s["traceability"]["dissent_adjudications"] = [
|
|
3093
|
+
{"dissent_id": "DIS-1", "adjudicator": "user", "outcome": "replacement_approved",
|
|
3094
|
+
"rationale": "Approved at the checkpoint."}
|
|
3095
|
+
]
|
|
3096
|
+
assert_mismatch(tmp_path, s, capsys, "is a ghost")
|
|
3097
|
+
|
|
3098
|
+
|
|
3099
|
+
def test_below_bound_adjudication_rejected(tmp_path, capsys):
|
|
3100
|
+
# codex round-4 #1: dissents below the §7 bound stand unadjudicated by design
|
|
3101
|
+
s = scenario_complex()
|
|
3102
|
+
s["traceability"]["dissent_adjudications"] = [
|
|
3103
|
+
{"dissent_id": "DIS-1", "adjudicator": "user", "outcome": "replacement_approved",
|
|
3104
|
+
"rationale": "Adjudicated despite no bound tripping."}
|
|
3105
|
+
]
|
|
3106
|
+
assert_mismatch(tmp_path, s, capsys, "unadjudicated by design")
|
|
3107
|
+
|
|
3108
|
+
|
|
3109
|
+
def test_acceptance_must_reference_current_reapplication(tmp_path, capsys):
|
|
3110
|
+
# codex round-4 #2: a stale acceptance of a superseded attempt cannot
|
|
3111
|
+
# override the successful current retry
|
|
3112
|
+
s = scenario_g2d_retry()
|
|
3113
|
+
s["traceability"]["g2d_acceptances"] = [
|
|
3114
|
+
{"acceptance_id": "ACC-1", "item_id": "REV-001", "reapplication_id": "RAP-1", "accepted_by": "user"}
|
|
3115
|
+
]
|
|
3116
|
+
assert_mismatch(tmp_path, s, capsys, "SUPERSEDED reapplication")
|
|
3117
|
+
|
|
3118
|
+
|
|
3119
|
+
# --- §6 derivation unit table --------------------------------------------------
|
|
3120
|
+
|
|
3121
|
+
|
|
3122
|
+
@pytest.mark.parametrize(
|
|
3123
|
+
"p1_items, p2_partials, p2_made_worse, rate, regressions, expected",
|
|
3124
|
+
(
|
|
3125
|
+
([("MADE_WORSE", "critical", None)], [], False, (1, 1), set(), ("Major Revision", True, "B1")),
|
|
3126
|
+
([("FULLY_ADDRESSED", "major", None)], [], False, (1, 1), {"critical"}, ("Major Revision", True, "B1")),
|
|
3127
|
+
([("NOT_ADDRESSED", "major", None), ("FULLY_ADDRESSED", "major", None)], [], False, (1, 1), set(),
|
|
3128
|
+
("Major Revision", True, "B2")),
|
|
3129
|
+
([("MADE_WORSE", "major", None)], [], False, (1, 1), set(), ("Major Revision", True, "B2")),
|
|
3130
|
+
([("CANNOT_VERIFY", "major", None), ("FULLY_ADDRESSED", "major", None), ("FULLY_ADDRESSED", None, None)],
|
|
3131
|
+
[], False, (1, 1), set(), ("Major Revision", False, "B3")),
|
|
3132
|
+
([("FULLY_ADDRESSED", "major", None)], [], False, (1, 1), {"major"}, ("Major Revision", False, "B3")),
|
|
3133
|
+
([("PARTIALLY_ADDRESSED", "major", "must_fix")], [], False, (1, 1), set(), ("Major Revision", False, "B4")),
|
|
3134
|
+
([("FULLY_ADDRESSED", "major", None)], ["must_fix"], False, (1, 2), set(), ("Major Revision", False, "B4")),
|
|
3135
|
+
([("PARTIALLY_ADDRESSED", "major", "should_fix")], [], False, (1, 1), set(), ("Minor Revision", False, "B5")),
|
|
3136
|
+
([("FULLY_ADDRESSED", "major", None)], [], False, (3, 4), set(), ("Minor Revision", False, "B5")),
|
|
3137
|
+
([("FULLY_ADDRESSED", "major", None)], [], True, (1, 1), set(), ("Minor Revision", False, "B5")),
|
|
3138
|
+
([("FULLY_ADDRESSED", "major", None)], [], False, (1, 1), {"minor"}, ("Minor Revision", False, "B5")),
|
|
3139
|
+
([("FULLY_ADDRESSED", "major", None)], [], False, (4, 5), set(), ("Accept", False, "B6")),
|
|
3140
|
+
([], [], False, (0, 0), set(), ("Accept", False, "B6")),
|
|
3141
|
+
),
|
|
3142
|
+
)
|
|
3143
|
+
def test_derive_decision_base_table(p1_items, p2_partials, p2_made_worse, rate, regressions, expected):
|
|
3144
|
+
assert crs.derive_decision(p1_items, p2_partials, p2_made_worse, rate[0], rate[1], regressions, []) == expected
|
|
3145
|
+
|
|
3146
|
+
|
|
3147
|
+
def test_derive_decision_floor_and_reject_semantics():
|
|
3148
|
+
approved_major = {"effective_approval_state": "approved", "escalation_class": "ethics",
|
|
3149
|
+
"mechanical_decision_impact": "Major Revision"}
|
|
3150
|
+
pending_major = dict(approved_major, effective_approval_state="pending")
|
|
3151
|
+
rejected_major = dict(approved_major, effective_approval_state="rejected")
|
|
3152
|
+
integrity_minor = {"effective_approval_state": "approved", "escalation_class": "research_integrity",
|
|
3153
|
+
"mechanical_decision_impact": "Minor Revision"}
|
|
3154
|
+
base_accept = ([("FULLY_ADDRESSED", "major", None)], [], False, 1, 1, set())
|
|
3155
|
+
assert crs.derive_decision(*base_accept, [approved_major]) == ("Major Revision", False, "B6")
|
|
3156
|
+
assert crs.derive_decision(*base_accept, [pending_major]) == ("Accept", False, "B6")
|
|
3157
|
+
assert crs.derive_decision(*base_accept, [rejected_major]) == ("Accept", False, "B6")
|
|
3158
|
+
assert crs.derive_decision(*base_accept, [integrity_minor]) == ("Minor Revision", True, "B6")
|
|
3159
|
+
|
|
3160
|
+
|
|
3161
|
+
def test_zero_p1_run_reaches_non_p1_disjuncts():
|
|
3162
|
+
# zero P1 items: B2 vacuously unsatisfied, but B1's critical-regression
|
|
3163
|
+
# disjunct still fires (§6 B2 note)
|
|
3164
|
+
assert crs.derive_decision([], [], False, 1, 1, {"critical"}, []) == ("Major Revision", True, "B1")
|
|
3165
|
+
|
|
3166
|
+
|
|
3167
|
+
# --- §11 witness unit tests ----------------------------------------------------
|
|
3168
|
+
|
|
3169
|
+
|
|
3170
|
+
def _witness_manifest(original_present=True):
|
|
3171
|
+
return {
|
|
3172
|
+
"artifacts": {
|
|
3173
|
+
"original_manuscript": _entry("path:m1", ORIGINAL_SHA) if original_present else {"present": False},
|
|
3174
|
+
"revised_manuscript": _entry("path:m2", REVISED_SHA),
|
|
3175
|
+
"revision_patches": {"present": True, "items": [
|
|
3176
|
+
{"path_or_passport_ref": "path:p", "sha256": PATCH_SHA, "version_label": None, "origin_date": None}
|
|
3177
|
+
]},
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
|
|
3182
|
+
def _authorization_witness():
|
|
3183
|
+
return {
|
|
3184
|
+
"status": "pass",
|
|
3185
|
+
"unregistered_claim_drift_review_required": True,
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
|
|
3189
|
+
def test_witness_pass_and_prefix_format():
|
|
3190
|
+
report = {"report_format_version": "1.3", "base_draft_hash": ORIGINAL_SHA[:12],
|
|
3191
|
+
"output_draft_hash": REVISED_SHA[:12], "patch_digest": PATCH_SHA,
|
|
3192
|
+
"authorization_witness": _authorization_witness()}
|
|
3193
|
+
witness, notes = crs.compute_apply_chain_witness(_witness_manifest(), [report])
|
|
3194
|
+
assert witness == "pass" and notes == []
|
|
3195
|
+
|
|
3196
|
+
|
|
3197
|
+
def test_witness_rejects_absent_original_in_current_contract():
|
|
3198
|
+
report = {"report_format_version": "1.3", "base_draft_hash": "aaaaaaaaaaaa",
|
|
3199
|
+
"output_draft_hash": REVISED_SHA[:12], "patch_digest": PATCH_SHA,
|
|
3200
|
+
"authorization_witness": _authorization_witness()}
|
|
3201
|
+
with pytest.raises(crs.ManifestError, match="hard-requires original_manuscript"):
|
|
3202
|
+
crs.compute_apply_chain_witness(
|
|
3203
|
+
_witness_manifest(original_present=False), [report]
|
|
3204
|
+
)
|
|
3205
|
+
|
|
3206
|
+
|
|
3207
|
+
def test_witness_no_reports():
|
|
3208
|
+
witness, notes = crs.compute_apply_chain_witness(_witness_manifest(), [])
|
|
3209
|
+
assert witness == "not_run_no_reports" and notes == []
|
|
3210
|
+
|
|
3211
|
+
|
|
3212
|
+
# --- canonical hash vector -----------------------------------------------------
|
|
3213
|
+
|
|
3214
|
+
|
|
3215
|
+
def test_canonical_hash_is_jcs_sha256():
|
|
3216
|
+
expected = hashlib.sha256('{"a":"中","b":1}'.encode("utf-8")).hexdigest()
|
|
3217
|
+
assert crs.canonical_hash({"b": 1, "a": "中"}) == expected
|
|
3218
|
+
|
|
3219
|
+
|
|
3220
|
+
# --- §10 card-normalization fixtures -------------------------------------------
|
|
3221
|
+
|
|
3222
|
+
HEI_PINNED = (
|
|
3223
|
+
("EIC", ["EIC"]),
|
|
3224
|
+
("Peer Reviewer 1 (Methodology)", ["R1"]),
|
|
3225
|
+
("Peer Reviewer 2 (Domain)", ["R2"]),
|
|
3226
|
+
("Peer Reviewer 3 (Cross-disciplinary/Practical)", ["R3"]),
|
|
3227
|
+
)
|
|
3228
|
+
INTERDISCIPLINARY_PINNED = (
|
|
3229
|
+
("EIC", ["EIC"]),
|
|
3230
|
+
("Peer Reviewer 1 (Methodology — ML Technical Expert)", ["R1"]),
|
|
3231
|
+
("Peer Reviewer 2 (Domain — Institutional Research Expert)", ["R2"]),
|
|
3232
|
+
("Peer Reviewer 3 (Cross-disciplinary — Public Policy / AI Ethics)", ["R3"]),
|
|
3233
|
+
)
|
|
3234
|
+
|
|
3235
|
+
|
|
3236
|
+
def _role_lines(path: Path):
|
|
3237
|
+
lines = []
|
|
3238
|
+
for line in path.read_text(encoding="utf-8").splitlines():
|
|
3239
|
+
if line.startswith("**Role**: "):
|
|
3240
|
+
lines.append(line[len("**Role**: "):])
|
|
3241
|
+
return lines
|
|
3242
|
+
|
|
3243
|
+
|
|
3244
|
+
@pytest.mark.parametrize(
|
|
3245
|
+
"filename, pinned",
|
|
3246
|
+
(
|
|
3247
|
+
("hei_paper_review_example.md", HEI_PINNED),
|
|
3248
|
+
("interdisciplinary_review_example.md", INTERDISCIPLINARY_PINNED),
|
|
3249
|
+
),
|
|
3250
|
+
)
|
|
3251
|
+
def test_example_card_role_lines_pinned_verbatim(filename, pinned):
|
|
3252
|
+
lines = _role_lines(EXAMPLES / filename)
|
|
3253
|
+
assert lines == [raw for raw, _expected in pinned], "example file Role lines changed — re-pin §10 fixtures"
|
|
3254
|
+
for raw, expected in pinned:
|
|
3255
|
+
labels, parse_failure = crs.normalize_reviewer_labels(raw)
|
|
3256
|
+
assert labels == expected, (raw, labels)
|
|
3257
|
+
assert parse_failure is False
|
|
3258
|
+
|
|
3259
|
+
|
|
3260
|
+
@pytest.mark.parametrize(
|
|
3261
|
+
"raw, expected",
|
|
3262
|
+
(
|
|
3263
|
+
("DA", ["DA"]),
|
|
3264
|
+
("Devil's Advocate", ["DA"]),
|
|
3265
|
+
("Devil’s Advocate", ["DA"]),
|
|
3266
|
+
("DA (Adversarial)", ["DA"]),
|
|
3267
|
+
),
|
|
3268
|
+
)
|
|
3269
|
+
def test_synthetic_da_fixtures(raw, expected):
|
|
3270
|
+
labels, parse_failure = crs.normalize_reviewer_labels(raw)
|
|
3271
|
+
assert labels == expected and parse_failure is False
|
|
3272
|
+
|
|
3273
|
+
|
|
3274
|
+
@pytest.mark.parametrize(
|
|
3275
|
+
"raw, expected, parse_failure",
|
|
3276
|
+
(
|
|
3277
|
+
("R1, R3", ["R1", "R3"], False),
|
|
3278
|
+
("R1 and R2", ["R1", "R2"], False),
|
|
3279
|
+
("R1/R2;R3", ["R1", "R2", "R3"], False),
|
|
3280
|
+
("Editor-in-Chief", ["EIC"], False),
|
|
3281
|
+
("Reviewer 2", ["R2"], False),
|
|
3282
|
+
("EIC & R1", ["EIC", "R1"], False),
|
|
3283
|
+
("R1, R1", ["R1"], False),
|
|
3284
|
+
("Editorial Office", [], True),
|
|
3285
|
+
("", [], False),
|
|
3286
|
+
("Methodology Board (R1)", [], True), # parenthetical stripped FIRST — no substring extraction
|
|
3287
|
+
),
|
|
3288
|
+
)
|
|
3289
|
+
def test_normalization_grammar_edges(raw, expected, parse_failure):
|
|
3290
|
+
labels, failure = crs.normalize_reviewer_labels(raw)
|
|
3291
|
+
assert labels == expected
|
|
3292
|
+
assert failure is parse_failure
|
|
3293
|
+
|
|
3294
|
+
|
|
3295
|
+
def test_paren_strip_precedes_dash_strip():
|
|
3296
|
+
# an em-dash inside a parenthetical is removed by step 1a; running 1b
|
|
3297
|
+
# first would leave an unbalanced fragment (§10 order is normative)
|
|
3298
|
+
labels, failure = crs.normalize_reviewer_labels("Peer Reviewer 1 (Methodology — ML) — lead")
|
|
3299
|
+
assert labels == ["R1"] and failure is False
|
|
3300
|
+
|
|
3301
|
+
|
|
3302
|
+
# --- #576 §8 routing fixtures (PR-B2): frozen records reach AND are ingested at 4.5
|
|
3303
|
+
|
|
3304
|
+
|
|
3305
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
3306
|
+
_INTEGRITY_AGENT = REPO_ROOT / "academic-pipeline/agents/integrity_verification_agent.md"
|
|
3307
|
+
_ORCHESTRATOR = REPO_ROOT / "academic-pipeline/agents/pipeline_orchestrator_agent.md"
|
|
3308
|
+
|
|
3309
|
+
|
|
3310
|
+
def _add_frozen_non_regression_records(scenario):
|
|
3311
|
+
"""Insert an identical previously_missed + indeterminate pair into BOTH the
|
|
3312
|
+
2A set and the 2B frozen copy (whole-record byte equality), and mirror the
|
|
3313
|
+
ids into decision_inputs — the §8 goalpost guard keeps them decision-inert."""
|
|
3314
|
+
records = [
|
|
3315
|
+
_new_issue(8, "previously_missed", "major"),
|
|
3316
|
+
_new_issue(9, "indeterminate", "minor"),
|
|
3317
|
+
]
|
|
3318
|
+
scenario["verdict_record"]["new_issues"].extend(json.loads(json.dumps(records)))
|
|
3319
|
+
scenario["traceability"]["new_issues"].extend(json.loads(json.dumps(records)))
|
|
3320
|
+
di = scenario["traceability"]["decision_inputs"]
|
|
3321
|
+
di["non_regression_new_issue_ids"] = sorted(
|
|
3322
|
+
set(di["non_regression_new_issue_ids"]) | {"NEW-8", "NEW-9"}
|
|
3323
|
+
)
|
|
3324
|
+
return scenario
|
|
3325
|
+
|
|
3326
|
+
|
|
3327
|
+
def _assert_4_5_ingestion_pinned(path_marker: str):
|
|
3328
|
+
"""The INGESTION half of the §8 fixture: the Stage 4.5 gate's input list
|
|
3329
|
+
consumes BOTH attributions (not just arrival), and the orchestrator's
|
|
3330
|
+
handoff row for this path carries the sidecar's frozen records."""
|
|
3331
|
+
integrity = _INTEGRITY_AGENT.read_text(encoding="utf-8")
|
|
3332
|
+
assert (
|
|
3333
|
+
"the Stage 3' traceability sidecar's frozen `previously_missed` AND "
|
|
3334
|
+
"`indeterminate` new-issue records" in integrity
|
|
3335
|
+
), "integrity_verification_agent must consume BOTH attributions (#576 §8)"
|
|
3336
|
+
assert "Current #576 1.1 hard-requires the original manuscript" in integrity
|
|
3337
|
+
assert "Consume both attributions as integrity-check input" in integrity
|
|
3338
|
+
assert "disposition appears in the report" in integrity, (
|
|
3339
|
+
"ingestion means per-record assessment, not just arrival"
|
|
3340
|
+
)
|
|
3341
|
+
orch = _ORCHESTRATOR.read_text(encoding="utf-8")
|
|
3342
|
+
assert path_marker in orch, f"orchestrator handoff row missing: {path_marker!r}"
|
|
3343
|
+
|
|
3344
|
+
|
|
3345
|
+
def test_routing_fixture_accept_direct_records_reach_and_are_ingested(tmp_path, capsys):
|
|
3346
|
+
# Accept-direct path (Stage 3' -> 4.5, no Stage 4' between): the frozen
|
|
3347
|
+
# records survive a checker-validated emission at decision Accept...
|
|
3348
|
+
s = _add_frozen_non_regression_records(scenario_accept())
|
|
3349
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
3350
|
+
assert code == crs.EXIT_PASS, out
|
|
3351
|
+
assert "'Accept'" in out
|
|
3352
|
+
# ...and the 4.5 gate INGESTS them on this path (prose contract pinned).
|
|
3353
|
+
_assert_4_5_ingestion_pinned(
|
|
3354
|
+
"| Stage 3' -> 4.5 | (Accept/Minor direct path — no Stage 4' between)"
|
|
3355
|
+
)
|
|
3356
|
+
orch = _ORCHESTRATOR.read_text(encoding="utf-8")
|
|
3357
|
+
assert "the frozen records are gate INPUT, not just cargo" in orch
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
def test_routing_fixture_major_via_4prime_records_reach_and_are_ingested(tmp_path, capsys):
|
|
3361
|
+
# Major-via-4' path: same frozen records on a Major Revision emission...
|
|
3362
|
+
s = _add_frozen_non_regression_records(scenario_g2d(accepted=True))
|
|
3363
|
+
code, out, _err = run_checker(tmp_path, s, capsys)
|
|
3364
|
+
assert code == crs.EXIT_PASS, out
|
|
3365
|
+
assert "'Major Revision'" in out
|
|
3366
|
+
# ...ride through 4' with the roadmap on the extended Stage 4/4' -> 4.5 row.
|
|
3367
|
+
_assert_4_5_ingestion_pinned(
|
|
3368
|
+
"(Major-via-4' path) the Stage 3' traceability sidecar with its frozen "
|
|
3369
|
+
"`previously_missed`/`indeterminate` new-issue records"
|
|
3370
|
+
)
|
|
3371
|
+
|
|
3372
|
+
|
|
3373
|
+
_RE_REVIEW_PROTOCOL = REPO_ROOT / "academic-paper-reviewer/references/re_review_mode_protocol.md"
|
|
3374
|
+
|
|
3375
|
+
|
|
3376
|
+
def test_rev_pm_closed_mapping_all_fields_pinned():
|
|
3377
|
+
"""#576 §8: the previously_missed forward-seed mapping is CLOSED — every
|
|
3378
|
+
required RoadmapItem field plus all four transported optional fields must
|
|
3379
|
+
have a stated derivation on the operative protocol surface, so a Stage 3'
|
|
3380
|
+
synthesizer can build a legal REV-PM item from the living docs alone."""
|
|
3381
|
+
text = _RE_REVIEW_PROTOCOL.read_text(encoding="utf-8")
|
|
3382
|
+
start = text.index("- `previously_missed`")
|
|
3383
|
+
block = text[start:text.index("- `indeterminate`", start)]
|
|
3384
|
+
for needle in (
|
|
3385
|
+
"`id = REV-PM-<n>`",
|
|
3386
|
+
"`source_refs = [{seat: found_by, channel: finding, ordinal: <n>, subclaim_ordinal: 0}]`",
|
|
3387
|
+
"`obligation_class = consider`",
|
|
3388
|
+
"`cost_scope = {kind: section, locator: <location_anchor section>}`",
|
|
3389
|
+
"`consequence_if_unaddressed = {code: reader_traceability_reduced",
|
|
3390
|
+
"target section derives from `location_anchor`",
|
|
3391
|
+
'suggested action is "assess; address or record as a limitation"',
|
|
3392
|
+
"consensus is `SINGLE-VERIFIER`",
|
|
3393
|
+
"verification criteria require resolution or explicit limitation",
|
|
3394
|
+
"severity/evidence/confidence/competence copy the frozen record",
|
|
3395
|
+
"`proposed_targets` is the unique exact current block",
|
|
3396
|
+
"Legacy `type` is not emitted",
|
|
3397
|
+
):
|
|
3398
|
+
assert needle in block, f"REV-PM closed mapping missing field derivation: {needle!r}"
|